diff options
| author | Michael Biebl <biebl@debian.org> | 2015-01-22 00:29:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2015-01-22 00:29:39 +0100 |
| commit | 2c032d8f1c6292c1338a615e6ec40252889ba85c (patch) | |
| tree | 1f77182220b2b0264288ba4a476ab47e5bc48716 /clients | |
| parent | 33491bc4279481db8ae47213e34a6d695a0e8830 (diff) | |
Imported Upstream version 1.0.0 upstream/1.0.0
Diffstat (limited to 'clients')
154 files changed, 54257 insertions, 0 deletions
diff --git a/clients/Makefile.am b/clients/Makefile.am new file mode 100644 index 00000000..dea8f9e3 --- /dev/null +++ b/clients/Makefile.am @@ -0,0 +1,21 @@ +SUBDIRS = cli tui + +AM_CPPFLAGS = \ + -I${top_srcdir}/libnm-core \ + -I${top_builddir}/libnm-core \ + -I${top_srcdir}/libnm \ + -I${top_builddir}/libnm \ + $(GLIB_CFLAGS) \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ + -DNMLOCALEDIR=\"$(datadir)/locale\" + +bin_PROGRAMS = nm-online + +nm_online_SOURCES = nm-online.c +nm_online_CPPFLAGS = \ + -DG_LOG_DOMAIN=\""nm-online"\" \ + $(AM_CPPFLAGS) + +nm_online_LDADD = \ + $(top_builddir)/libnm/libnm.la \ + $(GLIB_LIBS) diff --git a/clients/Makefile.in b/clients/Makefile.in new file mode 100644 index 00000000..7abc2123 --- /dev/null +++ b/clients/Makefile.in @@ -0,0 +1,917 @@ +# Makefile.in generated by automake 1.13.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = nm-online$(EXEEXT) +subdir = clients +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/build-aux/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gnome-code-coverage.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am_nm_online_OBJECTS = nm_online-nm-online.$(OBJEXT) +nm_online_OBJECTS = $(am_nm_online_OBJECTS) +am__DEPENDENCIES_1 = +nm_online_DEPENDENCIES = $(top_builddir)/libnm/libnm.la \ + $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(nm_online_SOURCES) +DIST_SOURCES = $(nm_online_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@ +BLUEZ5_LIBS = @BLUEZ5_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CKDB_PATH = @CKDB_PATH@ +CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ +CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ +CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ +DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ +DLLTOOL = @DLLTOOL@ +DNSMASQ_PATH = @DNSMASQ_PATH@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MAKEFILE = @GLIB_MAKEFILE@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@ +INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@ +INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@ +INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@ +INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ +INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@ +INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@ +INTROSPECTION_LIBS = @INTROSPECTION_LIBS@ +INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ +INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ +INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ +IPTABLES_PATH = @IPTABLES_PATH@ +IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@ +IWMX_SDK_LIBS = @IWMX_SDK_LIBS@ +KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBNDP_CFLAGS = @LIBNDP_CFLAGS@ +LIBNDP_LIBS = @LIBNDP_LIBS@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ +LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ +LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@ +MM_GLIB_LIBS = @MM_GLIB_LIBS@ +MOC = @MOC@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NEWT_CFLAGS = @NEWT_CFLAGS@ +NEWT_LIBS = @NEWT_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@ +NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ +NM_MICRO_VERSION = @NM_MICRO_VERSION@ +NM_MINOR_VERSION = @NM_MINOR_VERSION@ +NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@ +NM_VERSION = @NM_VERSION@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PPPD_PATH = @PPPD_PATH@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +PPPOE_PATH = @PPPOE_PATH@ +QT_CFLAGS = @QT_CFLAGS@ +QT_LIBS = @QT_LIBS@ +RANLIB = @RANLIB@ +READLINE_LIBS = @READLINE_LIBS@ +SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@ +SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@ +SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@ +SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@ +SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@ +SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_DIR = @UDEV_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VALGRIND_RULES = @VALGRIND_RULES@ +VAPIGEN = @VAPIGEN@ +VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@ +VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +intltool__v_merge_options_ = @intltool__v_merge_options_@ +intltool__v_merge_options_0 = @intltool__v_merge_options_0@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +nmbinary = @nmbinary@ +nmconfdir = @nmconfdir@ +nmdatadir = @nmdatadir@ +nmrundir = @nmrundir@ +nmstatedir = @nmstatedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_dhclient = @with_dhclient@ +with_dhcpcd = @with_dhcpcd@ +with_netconfig = @with_netconfig@ +with_resolvconf = @with_resolvconf@ +with_valgrind = @with_valgrind@ +SUBDIRS = cli tui +AM_CPPFLAGS = \ + -I${top_srcdir}/libnm-core \ + -I${top_builddir}/libnm-core \ + -I${top_srcdir}/libnm \ + -I${top_builddir}/libnm \ + $(GLIB_CFLAGS) \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ + -DNMLOCALEDIR=\"$(datadir)/locale\" + +nm_online_SOURCES = nm-online.c +nm_online_CPPFLAGS = \ + -DG_LOG_DOMAIN=\""nm-online"\" \ + $(AM_CPPFLAGS) + +nm_online_LDADD = \ + $(top_builddir)/libnm/libnm.la \ + $(GLIB_LIBS) + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu clients/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu clients/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +nm-online$(EXEEXT): $(nm_online_OBJECTS) $(nm_online_DEPENDENCIES) $(EXTRA_nm_online_DEPENDENCIES) + @rm -f nm-online$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(nm_online_OBJECTS) $(nm_online_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm_online-nm-online.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +nm_online-nm-online.o: nm-online.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_online_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm_online-nm-online.o -MD -MP -MF $(DEPDIR)/nm_online-nm-online.Tpo -c -o nm_online-nm-online.o `test -f 'nm-online.c' || echo '$(srcdir)/'`nm-online.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm_online-nm-online.Tpo $(DEPDIR)/nm_online-nm-online.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nm-online.c' object='nm_online-nm-online.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_online_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm_online-nm-online.o `test -f 'nm-online.c' || echo '$(srcdir)/'`nm-online.c + +nm_online-nm-online.obj: nm-online.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_online_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm_online-nm-online.obj -MD -MP -MF $(DEPDIR)/nm_online-nm-online.Tpo -c -o nm_online-nm-online.obj `if test -f 'nm-online.c'; then $(CYGPATH_W) 'nm-online.c'; else $(CYGPATH_W) '$(srcdir)/nm-online.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm_online-nm-online.Tpo $(DEPDIR)/nm_online-nm-online.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nm-online.c' object='nm_online-nm-online.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_online_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm_online-nm-online.obj `if test -f 'nm-online.c'; then $(CYGPATH_W) 'nm-online.c'; else $(CYGPATH_W) '$(srcdir)/nm-online.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-binPROGRAMS clean-generic clean-libtool \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-binPROGRAMS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/clients/cli/Makefile.am b/clients/cli/Makefile.am new file mode 100644 index 00000000..75c00a99 --- /dev/null +++ b/clients/cli/Makefile.am @@ -0,0 +1,72 @@ +bin_PROGRAMS = \ + nmcli + +AM_CPPFLAGS = \ + -I${top_srcdir} \ + -I${top_builddir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-core \ + -I${top_builddir}/libnm-core \ + -I${top_srcdir}/libnm \ + -I${top_builddir}/libnm \ + -I${top_srcdir}/clients/common \ + $(GLIB_CFLAGS) \ + -DG_LOG_DOMAIN=\""nmcli"\" \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ + -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" + +nmcli_SOURCES = \ + agent.c \ + agent.h \ + common.c \ + common.h \ + connections.c \ + connections.h \ + devices.c \ + devices.h \ + general.c \ + general.h \ + settings.c \ + settings.h \ + nmcli.c \ + nmcli.h \ + utils.c \ + utils.h \ + polkit-agent.c \ + polkit-agent.h \ + \ + $(srcdir)/../common/nm-secret-agent-simple.c \ + $(srcdir)/../common/nm-secret-agent-simple.h \ + $(NULL) + +nmcli_LDADD = \ + $(GLIB_LIBS) \ + $(READLINE_LIBS) \ + $(top_builddir)/libnm/libnm.la + +if WITH_POLKIT_AGENT +AM_CPPFLAGS += $(POLKIT_CFLAGS) +nmcli_SOURCES += $(srcdir)/../common/nm-polkit-listener.c $(srcdir)/../common/nm-polkit-listener.h +nmcli_LDADD += $(POLKIT_LIBS) +endif + +if BUILD_SETTING_DOCS +settings-docs.c: settings-docs.xsl $(top_builddir)/libnm/nm-property-docs.xml + $(AM_V_GEN) xsltproc --output $@ $^ + +BUILT_SOURCES = settings-docs.c +CLEANFILES = settings-docs.c +endif + +EXTRA_DIST = settings-docs.c settings-docs.xsl + +completiondir = $(datadir)/bash-completion/completions + +install-data-hook: + $(mkinstalldirs) $(DESTDIR)$(completiondir) + $(INSTALL_DATA) $(srcdir)/nmcli-completion $(DESTDIR)$(completiondir)/nmcli + +uninstall-hook: + rm -f $(DESTDIR)$(completiondir)/nmcli + +EXTRA_DIST += nmcli-completion diff --git a/clients/cli/Makefile.in b/clients/cli/Makefile.in new file mode 100644 index 00000000..871b9525 --- /dev/null +++ b/clients/cli/Makefile.in @@ -0,0 +1,862 @@ +# Makefile.in generated by automake 1.13.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = nmcli$(EXEEXT) +@WITH_POLKIT_AGENT_TRUE@am__append_1 = $(POLKIT_CFLAGS) +@WITH_POLKIT_AGENT_TRUE@am__append_2 = $(srcdir)/../common/nm-polkit-listener.c $(srcdir)/../common/nm-polkit-listener.h +@WITH_POLKIT_AGENT_TRUE@am__append_3 = $(POLKIT_LIBS) +subdir = clients/cli +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/build-aux/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gnome-code-coverage.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am__nmcli_SOURCES_DIST = agent.c agent.h common.c common.h \ + connections.c connections.h devices.c devices.h general.c \ + general.h settings.c settings.h nmcli.c nmcli.h utils.c \ + utils.h polkit-agent.c polkit-agent.h \ + $(srcdir)/../common/nm-secret-agent-simple.c \ + $(srcdir)/../common/nm-secret-agent-simple.h \ + $(srcdir)/../common/nm-polkit-listener.c \ + $(srcdir)/../common/nm-polkit-listener.h +@WITH_POLKIT_AGENT_TRUE@am__objects_1 = nm-polkit-listener.$(OBJEXT) +am_nmcli_OBJECTS = agent.$(OBJEXT) common.$(OBJEXT) \ + connections.$(OBJEXT) devices.$(OBJEXT) general.$(OBJEXT) \ + settings.$(OBJEXT) nmcli.$(OBJEXT) utils.$(OBJEXT) \ + polkit-agent.$(OBJEXT) nm-secret-agent-simple.$(OBJEXT) \ + $(am__objects_1) +nmcli_OBJECTS = $(am_nmcli_OBJECTS) +am__DEPENDENCIES_1 = +@WITH_POLKIT_AGENT_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +nmcli_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(top_builddir)/libnm/libnm.la $(am__DEPENDENCIES_2) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(nmcli_SOURCES) +DIST_SOURCES = $(am__nmcli_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@ +BLUEZ5_LIBS = @BLUEZ5_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CKDB_PATH = @CKDB_PATH@ +CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ +CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ +CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ +DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ +DLLTOOL = @DLLTOOL@ +DNSMASQ_PATH = @DNSMASQ_PATH@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MAKEFILE = @GLIB_MAKEFILE@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@ +INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@ +INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@ +INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@ +INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ +INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@ +INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@ +INTROSPECTION_LIBS = @INTROSPECTION_LIBS@ +INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ +INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ +INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ +IPTABLES_PATH = @IPTABLES_PATH@ +IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@ +IWMX_SDK_LIBS = @IWMX_SDK_LIBS@ +KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBNDP_CFLAGS = @LIBNDP_CFLAGS@ +LIBNDP_LIBS = @LIBNDP_LIBS@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ +LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ +LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@ +MM_GLIB_LIBS = @MM_GLIB_LIBS@ +MOC = @MOC@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NEWT_CFLAGS = @NEWT_CFLAGS@ +NEWT_LIBS = @NEWT_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@ +NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ +NM_MICRO_VERSION = @NM_MICRO_VERSION@ +NM_MINOR_VERSION = @NM_MINOR_VERSION@ +NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@ +NM_VERSION = @NM_VERSION@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PPPD_PATH = @PPPD_PATH@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +PPPOE_PATH = @PPPOE_PATH@ +QT_CFLAGS = @QT_CFLAGS@ +QT_LIBS = @QT_LIBS@ +RANLIB = @RANLIB@ +READLINE_LIBS = @READLINE_LIBS@ +SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@ +SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@ +SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@ +SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@ +SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@ +SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_DIR = @UDEV_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VALGRIND_RULES = @VALGRIND_RULES@ +VAPIGEN = @VAPIGEN@ +VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@ +VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +intltool__v_merge_options_ = @intltool__v_merge_options_@ +intltool__v_merge_options_0 = @intltool__v_merge_options_0@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +nmbinary = @nmbinary@ +nmconfdir = @nmconfdir@ +nmdatadir = @nmdatadir@ +nmrundir = @nmrundir@ +nmstatedir = @nmstatedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_dhclient = @with_dhclient@ +with_dhcpcd = @with_dhcpcd@ +with_netconfig = @with_netconfig@ +with_resolvconf = @with_resolvconf@ +with_valgrind = @with_valgrind@ +AM_CPPFLAGS = -I${top_srcdir} -I${top_builddir} \ + -I${top_srcdir}/include -I${top_srcdir}/libnm-core \ + -I${top_builddir}/libnm-core -I${top_srcdir}/libnm \ + -I${top_builddir}/libnm -I${top_srcdir}/clients/common \ + $(GLIB_CFLAGS) -DG_LOG_DOMAIN=\""nmcli"\" \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ + -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" $(am__append_1) +nmcli_SOURCES = agent.c agent.h common.c common.h connections.c \ + connections.h devices.c devices.h general.c general.h \ + settings.c settings.h nmcli.c nmcli.h utils.c utils.h \ + polkit-agent.c polkit-agent.h \ + $(srcdir)/../common/nm-secret-agent-simple.c \ + $(srcdir)/../common/nm-secret-agent-simple.h $(NULL) \ + $(am__append_2) +nmcli_LDADD = $(GLIB_LIBS) $(READLINE_LIBS) \ + $(top_builddir)/libnm/libnm.la $(am__append_3) +@BUILD_SETTING_DOCS_TRUE@BUILT_SOURCES = settings-docs.c +@BUILD_SETTING_DOCS_TRUE@CLEANFILES = settings-docs.c +EXTRA_DIST = settings-docs.c settings-docs.xsl nmcli-completion +completiondir = $(datadir)/bash-completion/completions +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu clients/cli/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu clients/cli/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +nmcli$(EXEEXT): $(nmcli_OBJECTS) $(nmcli_DEPENDENCIES) $(EXTRA_nmcli_DEPENDENCIES) + @rm -f nmcli$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(nmcli_OBJECTS) $(nmcli_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/agent.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connections.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/devices.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/general.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-polkit-listener.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-secret-agent-simple.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmcli.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polkit-agent.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +nm-secret-agent-simple.o: $(srcdir)/../common/nm-secret-agent-simple.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-secret-agent-simple.o -MD -MP -MF $(DEPDIR)/nm-secret-agent-simple.Tpo -c -o nm-secret-agent-simple.o `test -f '$(srcdir)/../common/nm-secret-agent-simple.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-secret-agent-simple.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-secret-agent-simple.Tpo $(DEPDIR)/nm-secret-agent-simple.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(srcdir)/../common/nm-secret-agent-simple.c' object='nm-secret-agent-simple.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-secret-agent-simple.o `test -f '$(srcdir)/../common/nm-secret-agent-simple.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-secret-agent-simple.c + +nm-secret-agent-simple.obj: $(srcdir)/../common/nm-secret-agent-simple.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-secret-agent-simple.obj -MD -MP -MF $(DEPDIR)/nm-secret-agent-simple.Tpo -c -o nm-secret-agent-simple.obj `if test -f '$(srcdir)/../common/nm-secret-agent-simple.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-secret-agent-simple.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-secret-agent-simple.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-secret-agent-simple.Tpo $(DEPDIR)/nm-secret-agent-simple.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(srcdir)/../common/nm-secret-agent-simple.c' object='nm-secret-agent-simple.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-secret-agent-simple.obj `if test -f '$(srcdir)/../common/nm-secret-agent-simple.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-secret-agent-simple.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-secret-agent-simple.c'; fi` + +nm-polkit-listener.o: $(srcdir)/../common/nm-polkit-listener.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-polkit-listener.o -MD -MP -MF $(DEPDIR)/nm-polkit-listener.Tpo -c -o nm-polkit-listener.o `test -f '$(srcdir)/../common/nm-polkit-listener.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-polkit-listener.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-polkit-listener.Tpo $(DEPDIR)/nm-polkit-listener.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(srcdir)/../common/nm-polkit-listener.c' object='nm-polkit-listener.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-polkit-listener.o `test -f '$(srcdir)/../common/nm-polkit-listener.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-polkit-listener.c + +nm-polkit-listener.obj: $(srcdir)/../common/nm-polkit-listener.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-polkit-listener.obj -MD -MP -MF $(DEPDIR)/nm-polkit-listener.Tpo -c -o nm-polkit-listener.obj `if test -f '$(srcdir)/../common/nm-polkit-listener.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-polkit-listener.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-polkit-listener.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-polkit-listener.Tpo $(DEPDIR)/nm-polkit-listener.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(srcdir)/../common/nm-polkit-listener.c' object='nm-polkit-listener.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-polkit-listener.obj `if test -f '$(srcdir)/../common/nm-polkit-listener.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-polkit-listener.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-polkit-listener.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-hook +.MAKE: all check install install-am install-data-am install-strip \ + uninstall-am + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-data-hook install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-hook + + +@BUILD_SETTING_DOCS_TRUE@settings-docs.c: settings-docs.xsl $(top_builddir)/libnm/nm-property-docs.xml +@BUILD_SETTING_DOCS_TRUE@ $(AM_V_GEN) xsltproc --output $@ $^ + +install-data-hook: + $(mkinstalldirs) $(DESTDIR)$(completiondir) + $(INSTALL_DATA) $(srcdir)/nmcli-completion $(DESTDIR)$(completiondir)/nmcli + +uninstall-hook: + rm -f $(DESTDIR)$(completiondir)/nmcli + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/clients/cli/agent.c b/clients/cli/agent.c new file mode 100644 index 00000000..1695ed59 --- /dev/null +++ b/clients/cli/agent.c @@ -0,0 +1,252 @@ +/* + * nmcli - command-line tool for controlling NetworkManager + * Functions for running NM secret agent. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2014 Red Hat, Inc. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <readline/readline.h> +#include <readline/history.h> + +#include "common.h" +#include "utils.h" +#include "nm-secret-agent-simple.h" +#include "polkit-agent.h" +#include "agent.h" + +static void +usage (void) +{ + g_printerr (_("Usage: nmcli agent { COMMAND | help }\n\n" + "COMMAND := { secret | polkit | all }\n\n" + )); +} + +static void +usage_agent_secret (void) +{ + g_printerr (_("Usage: nmcli agent secret { help }\n" + "\n" + "Runs nmcli as NetworkManager secret agent. When NetworkManager requires\n" + "a password it asks registered agents for it. This command keeps nmcli running\n" + "and if a password is required asks the user for it.\n\n")); +} + +static void +usage_agent_polkit (void) +{ + g_printerr (_("Usage: nmcli agent polkit { help }\n" + "\n" + "Registers nmcli as a polkit action for the user session.\n" + "When a polkit daemon requires an authorization, nmcli asks the user and gives\n" + "the response back to polkit.\n\n")); +} + +static void +usage_agent_all (void) +{ + g_printerr (_("Usage: nmcli agent all { help }\n" + "\n" + "Runs nmcli as both NetworkManager secret and a polkit agent.\n\n")); +} + +/* for pre-filling a string to readline prompt */ +static char *pre_input_deftext; +static int +set_deftext (void) +{ + if (pre_input_deftext && rl_startup_hook) { + rl_insert_text (pre_input_deftext); + g_free (pre_input_deftext); + pre_input_deftext = NULL; + rl_startup_hook = NULL; + } + return 0; +} + +static gboolean +get_secrets_from_user (const char *request_id, + const char *title, + const char *msg, + GPtrArray *secrets) +{ + int i; + + for (i = 0; i < secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; + char *pwd = NULL; + + /* Ask user for the password */ + g_print ("%s\n", msg); + if (secret->value) { + /* Prefill the password if we have it. */ + rl_startup_hook = set_deftext; + pre_input_deftext = g_strdup (secret->value); + } + pwd = nmc_readline ("%s (%s): ", secret->name, secret->prop_name); + + /* No password provided, cancel the secrets. */ + if (!pwd) + return FALSE; + g_free (secret->value); + secret->value = pwd; + } + return TRUE; +} + +static void +secrets_requested (NMSecretAgentSimple *agent, + const char *request_id, + const char *title, + const char *msg, + GPtrArray *secrets, + gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + gboolean success = FALSE; + + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + + success = get_secrets_from_user (request_id, title, msg, secrets); + if (success) + nm_secret_agent_simple_response (agent, request_id, secrets); + else + nm_secret_agent_simple_response (agent, request_id, NULL); +} + + +static NMCResultCode +do_agent_secret (NmCli *nmc, int argc, char **argv) +{ + /* Create secret agent */ + nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-agent"); + if (nmc->secret_agent) { + /* We keep running */ + nmc->should_wait = TRUE; + + nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent), NULL); + g_signal_connect (nmc->secret_agent, "request-secrets", G_CALLBACK (secrets_requested), nmc); + g_print (_("nmcli successfully registered as a NetworkManager's secret agent.\n")); + } else { + g_string_printf (nmc->return_text, _("Error: secret agent initialization failed")); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + } + + return nmc->return_value; +} + +static NMCResultCode +do_agent_polkit (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + + /* Initialize polkit agent */ + if (!nmc_polkit_agent_init (nmc, TRUE, &error)) { + g_dbus_error_strip_remote_error (error); + g_string_printf (nmc->return_text, _("Error: polkit agent initialization failed: %s"), + error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_error_free (error); + } else { + /* We keep running */ + nmc->should_wait = TRUE; + + g_print (_("nmcli successfully registered as a polkit agent.\n")); + } + + return nmc->return_value; +} + +static NMCResultCode +do_agent_all (NmCli *nmc, int argc, char **argv) +{ + NMCResultCode secret_res; + + /* Run both secret and polkit agent */ + secret_res = do_agent_secret (nmc, argc, argv); + if (secret_res != NMC_RESULT_SUCCESS) + g_printerr ("%s\n", nmc->return_text->str); + + nmc->return_value = do_agent_polkit (nmc, argc, argv); + + if (nmc->return_value == NMC_RESULT_SUCCESS && secret_res != NMC_RESULT_SUCCESS) + nmc->return_value = secret_res; + + return nmc->return_value; +} + +NMCResultCode +do_agent (NmCli *nmc, int argc, char **argv) +{ + /* Get NMClient object */ + nmc->get_client (nmc); + + /* Check whether NetworkManager is running */ + if (!nm_client_get_nm_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return nmc->return_value; + } + /* Compare NM and nmcli versions */ + if (!nmc_versions_match (nmc)) + return nmc->return_value; + + if (argc == 0) { + nmc->return_value = do_agent_all (nmc, 0, NULL); + } + + if (argc > 0) { + if (nmc_arg_is_help (*argv)) { + usage (); + goto usage_exit; + } else if (matches (*argv, "secret") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_agent_secret (); + goto usage_exit; + } + nmc->return_value = do_agent_secret (nmc, argc-1, argv+1); + } else if (matches (*argv, "polkit") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_agent_polkit (); + goto usage_exit; + } + nmc->return_value = do_agent_polkit (nmc, argc-1, argv+1); + } else if (matches (*argv, "all") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_agent_all (); + goto usage_exit; + } + nmc->return_value = do_agent_all (nmc, argc-1, argv+1); + } else { + usage (); + g_string_printf (nmc->return_text, _("Error: 'agent' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + +usage_exit: + return nmc->return_value; +} diff --git a/clients/cli/agent.h b/clients/cli/agent.h new file mode 100644 index 00000000..70ea0d9b --- /dev/null +++ b/clients/cli/agent.h @@ -0,0 +1,29 @@ +/* + * nmcli - command-line tool for controlling NetworkManager + * Functions for running NM secret agent. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2014 Red Hat, Inc. + */ + +#ifndef __NMC_AGENT_H__ +#define __NMC_AGENT_H__ + +#include "nmcli.h" + +NMCResultCode do_agent (NmCli *nmc, int argc, char **argv); + +#endif /* __NMC_AGENT_H__ */ diff --git a/clients/cli/common.c b/clients/cli/common.c new file mode 100644 index 00000000..6b13f7f8 --- /dev/null +++ b/clients/cli/common.c @@ -0,0 +1,1146 @@ +/* + * nmcli - command-line tool for controlling NetworkManager + * Common functions and data shared between files. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2012 - 2014 Red Hat, Inc. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <readline/readline.h> +#include <readline/history.h> + +#include "nm-glib-compat.h" + +#include "common.h" +#include "utils.h" + +/* Available fields for IPv4 group */ +NmcOutputField nmc_fields_ip4_config[] = { + {"GROUP", N_("GROUP"), 15}, /* 0 */ + {"ADDRESS", N_("ADDRESS"), 68}, /* 1 */ + {"GATEWAY", N_("GATEWAY"), 0}, /* 2 */ + {"ROUTE", N_("ROUTE"), 68}, /* 3 */ + {"DNS", N_("DNS"), 35}, /* 4 */ + {"DOMAIN", N_("DOMAIN"), 35}, /* 5 */ + {"WINS", N_("WINS"), 20}, /* 6 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_IP4_CONFIG_ALL "GROUP,ADDRESS,GATEWAY,ROUTE,DNS,DOMAIN,WINS" + +/* Available fields for DHCPv4 group */ +NmcOutputField nmc_fields_dhcp4_config[] = { + {"GROUP", N_("GROUP"), 15}, /* 0 */ + {"OPTION", N_("OPTION"), 80}, /* 1 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DHCP4_CONFIG_ALL "GROUP,OPTION" + +/* Available fields for IPv6 group */ +NmcOutputField nmc_fields_ip6_config[] = { + {"GROUP", N_("GROUP"), 15}, /* 0 */ + {"ADDRESS", N_("ADDRESS"), 95}, /* 1 */ + {"GATEWAY", N_("GATEWAY"), 0}, /* 2 */ + {"ROUTE", N_("ROUTE"), 95}, /* 3 */ + {"DNS", N_("DNS"), 60}, /* 4 */ + {"DOMAIN", N_("DOMAIN"), 35}, /* 5 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_IP6_CONFIG_ALL "GROUP,ADDRESS,GATEWAY,ROUTE,DNS,DOMAIN" + +/* Available fields for DHCPv6 group */ +NmcOutputField nmc_fields_dhcp6_config[] = { + {"GROUP", N_("GROUP"), 15}, /* 0 */ + {"OPTION", N_("OPTION"), 80}, /* 1 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DHCP6_CONFIG_ALL "GROUP,OPTION" + + +gboolean +print_ip4_config (NMIPConfig *cfg4, + NmCli *nmc, + const char *group_prefix, + const char *one_field) +{ + GPtrArray *ptr_array; + char **addr_arr = NULL; + char **route_arr = NULL; + char **dns_arr = NULL; + char **domain_arr = NULL; + char **wins_arr = NULL; + int i = 0; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + if (cfg4 == NULL) + return FALSE; + + tmpl = nmc_fields_ip4_config; + tmpl_len = sizeof (nmc_fields_ip4_config); + nmc->print_fields.indices = parse_output_fields (one_field ? one_field : NMC_FIELDS_IP4_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + /* addresses */ + ptr_array = nm_ip_config_get_addresses (cfg4); + if (ptr_array) { + addr_arr = g_new (char *, ptr_array->len + 1); + for (i = 0; i < ptr_array->len; i++) { + NMIPAddress *addr = (NMIPAddress *) g_ptr_array_index (ptr_array, i); + + addr_arr[i] = g_strdup_printf ("%s/%u", + nm_ip_address_get_address (addr), + nm_ip_address_get_prefix (addr)); + } + addr_arr[i] = NULL; + } + + /* routes */ + ptr_array = nm_ip_config_get_routes (cfg4); + if (ptr_array) { + route_arr = g_new (char *, ptr_array->len + 1); + for (i = 0; i < ptr_array->len; i++) { + NMIPRoute *route = (NMIPRoute *) g_ptr_array_index (ptr_array, i); + const char *next_hop; + + next_hop = nm_ip_route_get_next_hop (route); + if (!next_hop) + next_hop = "0.0.0.0"; + + route_arr[i] = g_strdup_printf ("dst = %s/%u, nh = %s%c mt = %u", + nm_ip_route_get_dest (route), + nm_ip_route_get_prefix (route), + next_hop, + nm_ip_route_get_metric (route) == -1 ? '\0' : ',', + (guint32) nm_ip_route_get_metric (route)); + } + route_arr[i] = NULL; + } + + /* DNS */ + dns_arr = g_strdupv ((char **) nm_ip_config_get_nameservers (cfg4)); + + /* domains */ + domain_arr = g_strdupv ((char **) nm_ip_config_get_domains (cfg4)); + + /* WINS */ + wins_arr = g_strdupv ((char **) nm_ip_config_get_wins_servers (cfg4)); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, group_prefix); + set_val_arr (arr, 1, addr_arr); + set_val_strc (arr, 2, nm_ip_config_get_gateway (cfg4)); + set_val_arr (arr, 3, route_arr); + set_val_arr (arr, 4, dns_arr); + set_val_arr (arr, 5, domain_arr); + set_val_arr (arr, 6, wins_arr); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + return TRUE; +} + +gboolean +print_ip6_config (NMIPConfig *cfg6, + NmCli *nmc, + const char *group_prefix, + const char *one_field) +{ + GPtrArray *ptr_array; + char **addr_arr = NULL; + char **route_arr = NULL; + char **dns_arr = NULL; + char **domain_arr = NULL; + int i = 0; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + if (cfg6 == NULL) + return FALSE; + + tmpl = nmc_fields_ip6_config; + tmpl_len = sizeof (nmc_fields_ip6_config); + nmc->print_fields.indices = parse_output_fields (one_field ? one_field : NMC_FIELDS_IP6_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + /* addresses */ + ptr_array = nm_ip_config_get_addresses (cfg6); + if (ptr_array) { + addr_arr = g_new (char *, ptr_array->len + 1); + for (i = 0; i < ptr_array->len; i++) { + NMIPAddress *addr = (NMIPAddress *) g_ptr_array_index (ptr_array, i); + + addr_arr[i] = g_strdup_printf ("%s/%u", + nm_ip_address_get_address (addr), + nm_ip_address_get_prefix (addr)); + } + addr_arr[i] = NULL; + } + + /* routes */ + ptr_array = nm_ip_config_get_routes (cfg6); + if (ptr_array) { + route_arr = g_new (char *, ptr_array->len + 1); + for (i = 0; i < ptr_array->len; i++) { + NMIPRoute *route = (NMIPRoute *) g_ptr_array_index (ptr_array, i); + const char *next_hop; + + next_hop = nm_ip_route_get_next_hop (route); + if (!next_hop) + next_hop = "::"; + + route_arr[i] = g_strdup_printf ("dst = %s/%u, nh = %s%c mt = %u", + nm_ip_route_get_dest (route), + nm_ip_route_get_prefix (route), + next_hop, + nm_ip_route_get_metric (route) == -1 ? '\0' : ',', + (guint32) nm_ip_route_get_metric (route)); + } + route_arr[i] = NULL; + } + + /* DNS */ + dns_arr = g_strdupv ((char **) nm_ip_config_get_nameservers (cfg6)); + + /* domains */ + domain_arr = g_strdupv ((char **) nm_ip_config_get_domains (cfg6)); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, group_prefix); + set_val_arr (arr, 1, addr_arr); + set_val_strc (arr, 2, nm_ip_config_get_gateway (cfg6)); + set_val_arr (arr, 3, route_arr); + set_val_arr (arr, 4, dns_arr); + set_val_arr (arr, 5, domain_arr); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + return TRUE; +} + +gboolean +print_dhcp4_config (NMDhcpConfig *dhcp4, + NmCli *nmc, + const char *group_prefix, + const char *one_field) +{ + GHashTable *table; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + if (dhcp4 == NULL) + return FALSE; + + table = nm_dhcp_config_get_options (dhcp4); + if (table) { + GHashTableIter table_iter; + gpointer key, value; + char **options_arr = NULL; + int i = 0; + + tmpl = nmc_fields_dhcp4_config; + tmpl_len = sizeof (nmc_fields_dhcp4_config); + nmc->print_fields.indices = parse_output_fields (one_field ? one_field : NMC_FIELDS_DHCP4_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + options_arr = g_new (char *, g_hash_table_size (table) + 1); + g_hash_table_iter_init (&table_iter, table); + while (g_hash_table_iter_next (&table_iter, &key, &value)) + options_arr[i++] = g_strdup_printf ("%s = %s", (char *) key, (char *) value); + options_arr[i] = NULL; + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, group_prefix); + set_val_arr (arr, 1, options_arr); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + return TRUE; + } + return FALSE; +} + +gboolean +print_dhcp6_config (NMDhcpConfig *dhcp6, + NmCli *nmc, + const char *group_prefix, + const char *one_field) +{ + GHashTable *table; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + if (dhcp6 == NULL) + return FALSE; + + table = nm_dhcp_config_get_options (dhcp6); + if (table) { + GHashTableIter table_iter; + gpointer key, value; + char **options_arr = NULL; + int i = 0; + + tmpl = nmc_fields_dhcp6_config; + tmpl_len = sizeof (nmc_fields_dhcp6_config); + nmc->print_fields.indices = parse_output_fields (one_field ? one_field : NMC_FIELDS_DHCP6_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + options_arr = g_new (char *, g_hash_table_size (table) + 1); + g_hash_table_iter_init (&table_iter, table); + while (g_hash_table_iter_next (&table_iter, &key, &value)) + options_arr[i++] = g_strdup_printf ("%s = %s", (char *) key, (char *) value); + options_arr[i] = NULL; + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, group_prefix); + set_val_arr (arr, 1, options_arr); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + return TRUE; + } + return FALSE; +} + +/* + * Parse IP address from string to NMIPAddress stucture. + * ip_str is the IP address in the form address/prefix + */ +NMIPAddress * +nmc_parse_and_build_address (int family, const char *ip_str, GError **error) +{ + int max_prefix = (family == AF_INET) ? 32 : 128; + NMIPAddress *addr = NULL; + const char *ip; + char *tmp; + char *plen; + long int prefix; + GError *local = NULL; + + g_return_val_if_fail (ip_str != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + tmp = g_strdup (ip_str); + plen = strchr (tmp, '/'); /* prefix delimiter */ + if (plen) + *plen++ = '\0'; + + ip = tmp; + + prefix = max_prefix; + if (plen) { + if (!nmc_string_to_int (plen, TRUE, 1, max_prefix, &prefix)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid prefix '%s'; <1-%d> allowed"), plen, max_prefix); + goto finish; + } + } + + addr = nm_ip_address_new (family, ip, (guint32) prefix, &local); + if (!addr) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid IP address: %s"), local->message); + g_clear_error (&local); + } + +finish: + g_free (tmp); + return addr; +} + +/* + * nmc_parse_and_build_route: + * @family: AF_INET or AF_INET6 + * @first: the route destination in the form of "address/prefix" + (/prefix is optional) + * @second: (allow-none): next hop address, if third is not NULL. Otherwise it could be + either next hop address or metric. (It can be NULL when @third is NULL). + * @third: (allow-none): route metric + * @error: location to store GError + * + * Parse route from strings and return an #NMIPRoute + * + * Returns: %TRUE on success, %FALSE on failure + */ +NMIPRoute * +nmc_parse_and_build_route (int family, + const char *first, + const char *second, + const char *third, + GError **error) +{ + int max_prefix = (family == AF_INET) ? 32 : 128; + char *dest = NULL, *plen = NULL; + const char *next_hop = NULL; + const char *canon_dest; + long int prefix = max_prefix, metric = -1; + NMIPRoute *route = NULL; + gboolean success = FALSE; + GError *local = NULL; + + g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); + g_return_val_if_fail (first != NULL, FALSE); + g_return_val_if_fail (second || !third, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + dest = g_strdup (first); + plen = strchr (dest, '/'); /* prefix delimiter */ + if (plen) + *plen++ = '\0'; + + if (plen) { + if (!nmc_string_to_int (plen, TRUE, 1, max_prefix, &prefix)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid prefix '%s'; <1-%d> allowed"), + plen, max_prefix); + goto finish; + } + } + + if (second) { + if (third || nm_utils_ipaddr_valid (family, second)) + next_hop = second; + else { + /* 'second' can be a metric */ + if (!nmc_string_to_int (second, TRUE, 0, G_MAXUINT32, &metric)) { + g_set_error (error, 1, 0, _("the second component of route ('%s') is neither " + "a next hop address nor a metric"), second); + goto finish; + } + } + } + + if (third) { + if (!nmc_string_to_int (third, TRUE, 0, G_MAXUINT32, &metric)) { + g_set_error (error, 1, 0, _("invalid metric '%s'"), third); + goto finish; + } + } + + route = nm_ip_route_new (family, dest, prefix, next_hop, metric, &local); + if (!route) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid route: %s"), local->message); + g_clear_error (&local); + goto finish; + } + + /* We don't accept default routes as NetworkManager handles it + * itself. But we have to check this after @route has normalized the + * dest string. + */ + canon_dest = nm_ip_route_get_dest (route); + if (!strcmp (canon_dest, "0.0.0.0") || !strcmp (canon_dest, "::")) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("default route cannot be added (NetworkManager handles it by itself)")); + g_clear_pointer (&route, nm_ip_route_unref); + goto finish; + } + + success = TRUE; + +finish: + g_free (dest); + return route; +} + +const char * +nmc_device_state_to_string (NMDeviceState state) +{ + switch (state) { + case NM_DEVICE_STATE_UNMANAGED: + return _("unmanaged"); + case NM_DEVICE_STATE_UNAVAILABLE: + return _("unavailable"); + case NM_DEVICE_STATE_DISCONNECTED: + return _("disconnected"); + case NM_DEVICE_STATE_PREPARE: + return _("connecting (prepare)"); + case NM_DEVICE_STATE_CONFIG: + return _("connecting (configuring)"); + case NM_DEVICE_STATE_NEED_AUTH: + return _("connecting (need authentication)"); + case NM_DEVICE_STATE_IP_CONFIG: + return _("connecting (getting IP configuration)"); + case NM_DEVICE_STATE_IP_CHECK: + return _("connecting (checking IP connectivity)"); + case NM_DEVICE_STATE_SECONDARIES: + return _("connecting (starting secondary connections)"); + case NM_DEVICE_STATE_ACTIVATED: + return _("connected"); + case NM_DEVICE_STATE_DEACTIVATING: + return _("deactivating"); + case NM_DEVICE_STATE_FAILED: + return _("connection failed"); + default: + return _("unknown"); + } +} + +const char * +nmc_device_reason_to_string (NMDeviceStateReason reason) +{ + switch (reason) { + case NM_DEVICE_STATE_REASON_NONE: + return _("No reason given"); + + case NM_DEVICE_STATE_REASON_UNKNOWN: + return _("Unknown error"); + + case NM_DEVICE_STATE_REASON_NOW_MANAGED: + return _("Device is now managed"); + + case NM_DEVICE_STATE_REASON_NOW_UNMANAGED: + return _("Device is now unmanaged"); + + case NM_DEVICE_STATE_REASON_CONFIG_FAILED: + return _("The device could not be readied for configuration"); + + case NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE: + return _("IP configuration could not be reserved (no available address, timeout, etc.)"); + + case NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED: + return _("The IP configuration is no longer valid"); + + case NM_DEVICE_STATE_REASON_NO_SECRETS: + return _("Secrets were required, but not provided"); + + case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT: + return _("802.1X supplicant disconnected"); + + case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED: + return _("802.1X supplicant configuration failed"); + + case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED: + return _("802.1X supplicant failed"); + + case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT: + return _("802.1X supplicant took too long to authenticate"); + + case NM_DEVICE_STATE_REASON_PPP_START_FAILED: + return _("PPP service failed to start"); + + case NM_DEVICE_STATE_REASON_PPP_DISCONNECT: + return _("PPP service disconnected"); + + case NM_DEVICE_STATE_REASON_PPP_FAILED: + return _("PPP failed"); + + case NM_DEVICE_STATE_REASON_DHCP_START_FAILED: + return _("DHCP client failed to start"); + + case NM_DEVICE_STATE_REASON_DHCP_ERROR: + return _("DHCP client error"); + + case NM_DEVICE_STATE_REASON_DHCP_FAILED: + return _("DHCP client failed"); + + case NM_DEVICE_STATE_REASON_SHARED_START_FAILED: + return _("Shared connection service failed to start"); + + case NM_DEVICE_STATE_REASON_SHARED_FAILED: + return _("Shared connection service failed"); + + case NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED: + return _("AutoIP service failed to start"); + + case NM_DEVICE_STATE_REASON_AUTOIP_ERROR: + return _("AutoIP service error"); + + case NM_DEVICE_STATE_REASON_AUTOIP_FAILED: + return _("AutoIP service failed"); + + case NM_DEVICE_STATE_REASON_MODEM_BUSY: + return _("The line is busy"); + + case NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE: + return _("No dial tone"); + + case NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER: + return _("No carrier could be established"); + + case NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT: + return _("The dialing request timed out"); + + case NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED: + return _("The dialing attempt failed"); + + case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: + return _("Modem initialization failed"); + + case NM_DEVICE_STATE_REASON_GSM_APN_FAILED: + return _("Failed to select the specified APN"); + + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: + return _("Not searching for networks"); + + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: + return _("Network registration denied"); + + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT: + return _("Network registration timed out"); + + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED: + return _("Failed to register with the requested network"); + + case NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED: + return _("PIN check failed"); + + case NM_DEVICE_STATE_REASON_FIRMWARE_MISSING: + return _("Necessary firmware for the device may be missing"); + + case NM_DEVICE_STATE_REASON_REMOVED: + return _("The device was removed"); + + case NM_DEVICE_STATE_REASON_SLEEPING: + return _("NetworkManager went to sleep"); + + case NM_DEVICE_STATE_REASON_CONNECTION_REMOVED: + return _("The device's active connection disappeared"); + + case NM_DEVICE_STATE_REASON_USER_REQUESTED: + return _("Device disconnected by user or client"); + + case NM_DEVICE_STATE_REASON_CARRIER: + return _("Carrier/link changed"); + + case NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED: + return _("The device's existing connection was assumed"); + + case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE: + return _("The supplicant is now available"); + + case NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND: + return _("The modem could not be found"); + + case NM_DEVICE_STATE_REASON_BT_FAILED: + return _("The Bluetooth connection failed or timed out"); + + case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: + return _("GSM Modem's SIM card not inserted"); + + case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: + return _("GSM Modem's SIM PIN required"); + + case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: + return _("GSM Modem's SIM PUK required"); + + case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: + return _("GSM Modem's SIM wrong"); + + case NM_DEVICE_STATE_REASON_INFINIBAND_MODE: + return _("InfiniBand device does not support connected mode"); + + case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED: + return _("A dependency of the connection failed"); + + case NM_DEVICE_STATE_REASON_BR2684_FAILED: + return _("A problem with the RFC 2684 Ethernet over ADSL bridge"); + + case NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE: + return _("ModemManager is unavailable"); + + case NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: + return _("The Wi-Fi network could not be found"); + + case NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED: + return _("A secondary connection of the base connection failed"); + + case NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED: + return _("DCB or FCoE setup failed"); + + case NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED: + return _("teamd control failed"); + + case NM_DEVICE_STATE_REASON_MODEM_FAILED: + return _("Modem failed or no longer available"); + + case NM_DEVICE_STATE_REASON_MODEM_AVAILABLE: + return _("Modem now ready and available"); + + case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: + return _("SIM PIN was incorrect"); + + default: + /* TRANSLATORS: Unknown reason for a device state change (NMDeviceStateReason) */ + return _("Unknown"); + } +} + + +/* Max priority values from libnm-core/nm-setting-vlan.c */ +#define MAX_SKB_PRIO G_MAXUINT32 +#define MAX_8021P_PRIO 7 /* Max 802.1p priority */ + +/* + * Parse VLAN priority mappings from the following format: 2:1,3:4,7:3 + * and verify if the priority numbers are valid + * + * Return: string array with split maps, or NULL on error + * Caller is responsible for freeing the array. + */ +char ** +nmc_vlan_parse_priority_maps (const char *priority_map, + NMVlanPriorityMap map_type, + GError **error) +{ + char **mapping = NULL, **iter; + unsigned long from, to, from_max, to_max; + + g_return_val_if_fail (priority_map != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (map_type == NM_VLAN_INGRESS_MAP) { + from_max = MAX_8021P_PRIO; + to_max = MAX_SKB_PRIO; + } else { + from_max = MAX_SKB_PRIO; + to_max = MAX_8021P_PRIO; + } + + mapping = g_strsplit (priority_map, ",", 0); + for (iter = mapping; iter && *iter; iter++) { + char *left, *right; + + left = g_strstrip (*iter); + right = strchr (left, ':'); + if (!right) { + g_set_error (error, 1, 0, _("invalid priority map '%s'"), *iter); + g_strfreev (mapping); + return NULL; + } + *right++ = '\0'; + + if (!nmc_string_to_uint (left, TRUE, 0, from_max, &from)) { + g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"), + left, from_max); + g_strfreev (mapping); + return NULL; + } + if (!nmc_string_to_uint (right, TRUE, 0, to_max, &to)) { + g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"), + right, to_max); + g_strfreev (mapping); + return NULL; + } + *(right-1) = ':'; /* Put back ':' */ + } + return mapping; +} + +const char * +nmc_bond_validate_mode (const char *mode, GError **error) +{ + unsigned long mode_int; + static const char *valid_modes[] = { "balance-rr", + "active-backup", + "balance-xor", + "broadcast", + "802.3ad", + "balance-tlb", + "balance-alb", + NULL }; + if (nmc_string_to_uint (mode, TRUE, 0, 6, &mode_int)) { + /* Translate bonding mode numbers to mode names: + * https://www.kernel.org/doc/Documentation/networking/bonding.txt + */ + return valid_modes[mode_int]; + } else + return nmc_string_is_valid (mode, valid_modes, error); +} + +/* + * nmc_team_check_config: + * @config: file name with team config, or raw team JSON config data + * @out_config: raw team JSON config data (with removed new-line characters) + * @error: location to store error, or %NUL + * + * Check team config from @config parameter and return the checked/sanitized + * config in @out_config. + * + * Returns: %TRUE if the config is valid, %FALSE if it is invalid + */ +gboolean +nmc_team_check_config (const char *config, char **out_config, GError **error) +{ + char *contents = NULL; + size_t c_len = 0; + + *out_config = NULL; + + if (!config || strlen (config) == strspn (config, " \t")) + return TRUE; + + /* 'config' can be either a file name or raw JSON config data */ + if (g_file_test (config, G_FILE_TEST_EXISTS)) + (void) g_file_get_contents (config, &contents, NULL, NULL); + else + contents = g_strdup (config); + + if (contents) { + g_strstrip (contents); + c_len = strlen (contents); + } + + /* Do a simple validity check */ + if (!contents || !contents[0] || c_len > 100000 || contents[0] != '{' || contents[c_len-1] != '}') { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("'%s' is not a valid team configuration or file name."), config); + g_free (contents); + return FALSE; + } + *out_config = g_strdelimit (contents, "\r\n", ' '); + return TRUE; +} + +/* + * nmc_find_connection: + * @connections: array of NMConnections to search in + * @filter_type: "id", "uuid", "path" or %NULL + * @filter_val: connection to find (connection name, UUID or path) + * @start: where to start in @list. The location is updated so that the function + * can be called multiple times (for connections with the same name). + * + * Find a connection in @list according to @filter_val. @filter_type determines + * what property is used for comparison. When @filter_type is NULL, compare + * @filter_val against all types. Otherwise, only compare against the specified + * type. If 'path' filter type is specified, comparison against numeric index + * (in addition to the whole path) is allowed. + * + * Returns: found connection, or %NULL + */ +NMConnection * +nmc_find_connection (const GPtrArray *connections, + const char *filter_type, + const char *filter_val, + int *start) +{ + NMConnection *connection; + NMConnection *found = NULL; + int i; + const char *id; + const char *uuid; + const char *path, *path_num; + + for (i = start ? *start : 0; i < connections->len; i++) { + connection = NM_CONNECTION (connections->pdata[i]); + + id = nm_connection_get_id (connection); + uuid = nm_connection_get_uuid (connection); + path = nm_connection_get_path (connection); + path_num = path ? strrchr (path, '/') + 1 : NULL; + + /* When filter_type is NULL, compare connection ID (filter_val) + * against all types. Otherwise, only compare against the specific + * type. If 'path' filter type is specified, comparison against + * numeric index (in addition to the whole path) is allowed. + */ + if ( ( (!filter_type || strcmp (filter_type, "id") == 0) + && strcmp (filter_val, id) == 0) + || ( (!filter_type || strcmp (filter_type, "uuid") == 0) + && strcmp (filter_val, uuid) == 0) + || ( (!filter_type || strcmp (filter_type, "path") == 0) + && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)))) { + if (!start) + return connection; + if (found) { + *start = i; + return found; + } + found = connection; + } + } + + if (start) + *start = 0; + return found; +} + +static gboolean +get_secrets_from_user (const char *request_id, + const char *title, + const char *msg, + gboolean ask, + GHashTable *pwds_hash, + GPtrArray *secrets) +{ + int i; + + for (i = 0; i < secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; + char *pwd = NULL; + + /* First try to find the password in provided passwords file, + * then ask user. */ + if (pwds_hash && (pwd = g_hash_table_lookup (pwds_hash, secret->prop_name))) { + pwd = g_strdup (pwd); + } else { + g_print ("%s\n", msg); + if (ask) { + if (secret->value) { + /* Prefill the password if we have it. */ + rl_startup_hook = nmc_rl_set_deftext; + nmc_rl_pre_input_deftext = g_strdup (secret->value); + } + pwd = nmc_readline ("%s (%s): ", secret->name, secret->prop_name); + if (!pwd) + pwd = g_strdup (""); + } else { + g_printerr (_("Warning: password for '%s' not given in 'passwd-file' " + "and nmcli cannot ask without '--ask' option.\n"), + secret->prop_name); + } + } + /* No password provided, cancel the secrets. */ + if (!pwd) + return FALSE; + g_free (secret->value); + secret->value = pwd; + } + return TRUE; +} + +/** + * nmc_secrets_requested: + * @agent: the #NMSecretAgentSimple + * @request_id: request ID, to eventually pass to + * nm_secret_agent_simple_response() + * @title: a title for the password request + * @msg: a prompt message for the password request + * @secrets: (element-type #NMSecretAgentSimpleSecret): array of secrets + * being requested. + * @user_data: user data passed to the function + * + * This function is used as a callback for "request-secrets" signal of + * NMSecretAgentSimpleSecret. +*/ +void +nmc_secrets_requested (NMSecretAgentSimple *agent, + const char *request_id, + const char *title, + const char *msg, + GPtrArray *secrets, + gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + gboolean success = FALSE; + + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + + success = get_secrets_from_user (request_id, title, msg, nmc->in_editor || nmc->ask, + nmc->pwds_hash, secrets); + if (success) + nm_secret_agent_simple_response (agent, request_id, secrets); + else { + /* Unregister our secret agent on failure, so that another agent + * may be tried */ + if (nmc->secret_agent) { + nm_secret_agent_old_unregister (nmc->secret_agent, NULL, NULL); + g_clear_object (&nmc->secret_agent); + } + } +} + + +/** + * nmc_cleanup_readline: + * + * Cleanup readline when nmcli is terminated with a signal. + * It makes sure the terminal is not garbled. + */ +void +nmc_cleanup_readline (void) +{ + rl_free_line_state (); + rl_cleanup_after_signal (); +} + + +static gboolean nmcli_in_readline = FALSE; +static pthread_mutex_t readline_mutex = PTHREAD_MUTEX_INITIALIZER; + +gboolean +nmc_get_in_readline (void) +{ + gboolean in_readline; + + pthread_mutex_lock (&readline_mutex); + in_readline = nmcli_in_readline; + pthread_mutex_unlock (&readline_mutex); + return in_readline; +} + +void +nmc_set_in_readline (gboolean in_readline) +{ + pthread_mutex_lock (&readline_mutex); + nmcli_in_readline = in_readline; + pthread_mutex_unlock (&readline_mutex); +} + +/* Global variable defined in nmcli.c */ +extern NmCli nm_cli; + +/** + * nmc_readline: + * @prompt_fmt: prompt to print (telling user what to enter). It is standard + * printf() format string + * @...: a list of arguments according to the @prompt_fmt format string + * + * Wrapper around libreadline's readline() function. + * If user pressed Ctrl-C, readline() is called again (if not in editor and + * line is empty, nmcli will quit). + * If user pressed Ctrl-D on empty line, nmcli will quit. + * + * Returns: the user provided string. In case the user entered empty string, + * this function returns NULL. + */ +char * +nmc_readline (const char *prompt_fmt, ...) +{ + va_list args; + char *prompt, *str; + + va_start (args, prompt_fmt); + prompt = g_strdup_vprintf (prompt_fmt, args); + va_end (args); + +readline_mark: + /* We are in readline -> Ctrl-C should not quit nmcli */ + nmc_set_in_readline (TRUE); + str = readline (prompt); + /* We are outside readline -> Ctrl-C should quit nmcli */ + nmc_set_in_readline (FALSE); + + /* Add string to the history */ + if (str && *str) + add_history (str); + + /*-- React on Ctrl-C and Ctrl-D --*/ + /* We quit on Ctrl-D when line is empty */ + if (str == NULL) { + /* Send SIGQUIT to itself */ + nmc_set_sigquit_internal (); + kill (getpid (), SIGQUIT); + /* Sleep in this thread so that we don't do anything else until exit */ + for (;;) + sleep (3); + } + /* Ctrl-C */ + if (nmc_seen_sigint ()) { + nmc_clear_sigint (); + if (nm_cli.in_editor || *str) { + /* In editor, or the line is not empty */ + /* Call readline again to get new prompt (repeat) */ + g_free (str); + goto readline_mark; + } else { + /* Not in editor and line is empty */ + /* Send SIGQUIT to itself */ + nmc_set_sigquit_internal (); + kill (getpid (), SIGQUIT); + /* Sleep in this thread so that we don't do anything else until exit */ + for (;;) + sleep (3); + } + } + g_free (prompt); + + /* Return NULL, not empty string */ + if (str && *str == '\0') { + g_free (str); + str = NULL; + } + return str; +} + +/** + * nmc_rl_gen_func_basic: + * @text: text to complete + * @state: readline state; says whether start from scratch (state == 0) + * @words: strings for completion + * + * Basic function generating list of completion strings for readline. + * See e.g. http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC49 + */ +char * +nmc_rl_gen_func_basic (const char *text, int state, const char **words) +{ + static int list_idx, len; + const char *name; + + if (!state) { + list_idx = 0; + len = strlen (text); + } + + /* Return the next name which partially matches one from the 'words' list. */ + while ((name = words[list_idx])) { + list_idx++; + + if (strncmp (name, text, len) == 0) + return g_strdup (name); + } + return NULL; +} + +/* for pre-filling a string to readline prompt */ +char *nmc_rl_pre_input_deftext; + +int +nmc_rl_set_deftext (void) +{ + if (nmc_rl_pre_input_deftext && rl_startup_hook) { + rl_insert_text (nmc_rl_pre_input_deftext); + g_free (nmc_rl_pre_input_deftext); + nmc_rl_pre_input_deftext = NULL; + rl_startup_hook = NULL; + } + return 0; +} + diff --git a/clients/cli/common.h b/clients/cli/common.h new file mode 100644 index 00000000..8bb043f4 --- /dev/null +++ b/clients/cli/common.h @@ -0,0 +1,69 @@ +/* + * nmcli - command-line tool for controlling NetworkManager + * Common functions and data shared between files. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2012 - 2014 Red Hat, Inc. + */ + +#ifndef NMC_COMMON_H +#define NMC_COMMON_H + +#include "nmcli.h" +#include "nm-secret-agent-simple.h" + +gboolean print_ip4_config (NMIPConfig *cfg4, NmCli *nmc, const char *group_prefix, const char *one_field); +gboolean print_ip6_config (NMIPConfig *cfg6, NmCli *nmc, const char *group_prefix, const char *one_field); +gboolean print_dhcp4_config (NMDhcpConfig *dhcp4, NmCli *nmc, const char *group_prefix, const char *one_field); +gboolean print_dhcp6_config (NMDhcpConfig *dhcp6, NmCli *nmc, const char *group_prefix, const char *one_field); + +NMIPAddress *nmc_parse_and_build_address (int family, const char *ip_str, GError **error); +NMIPRoute *nmc_parse_and_build_route (int family, const char *first, const char *second, const char *third, GError **error); + +const char * nmc_device_state_to_string (NMDeviceState state); +const char * nmc_device_reason_to_string (NMDeviceStateReason reason); + +char ** +nmc_vlan_parse_priority_maps (const char *priority_map, + NMVlanPriorityMap map_type, + GError **error); + +const char *nmc_bond_validate_mode (const char *mode, GError **error); +gboolean nmc_team_check_config (const char *config, char **out_config, GError **error); + +NMConnection *nmc_find_connection (const GPtrArray *connections, + const char *filter_type, + const char *filter_val, + int *start); + +void nmc_secrets_requested (NMSecretAgentSimple *agent, + const char *request_id, + const char *title, + const char *msg, + GPtrArray *secrets, + gpointer user_data); + +void nmc_cleanup_readline (void); +char *nmc_readline (const char *prompt_fmt, ...) G_GNUC_PRINTF (1, 2); +char *nmc_rl_gen_func_basic (const char *text, int state, const char **words); +gboolean nmc_get_in_readline (void); +void nmc_set_in_readline (gboolean in_readline); + +/* for pre-filling a string to readline prompt */ +extern char *nmc_rl_pre_input_deftext; +int nmc_rl_set_deftext (void); + +#endif /* NMC_COMMON_H */ diff --git a/clients/cli/connections.c b/clients/cli/connections.c new file mode 100644 index 00000000..5de9cc9d --- /dev/null +++ b/clients/cli/connections.c @@ -0,0 +1,9020 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <netinet/ether.h> +#include <readline/readline.h> +#include <readline/history.h> + +#include "utils.h" +#include "common.h" +#include "settings.h" +#include "connections.h" +#include "nm-secret-agent-simple.h" +#include "polkit-agent.h" + +/* define some prompts for connection editor */ +#define EDITOR_PROMPT_SETTING _("Setting name? ") +#define EDITOR_PROMPT_PROPERTY _("Property name? ") +#define EDITOR_PROMPT_CON_TYPE _("Enter connection type: ") + +/* define some other prompts */ +#define PROMPT_CON_TYPE _("Connection type: ") +#define PROMPT_VPN_TYPE _("VPN type: ") +#define PROMPT_BOND_MASTER _("Bond master: ") +#define PROMPT_TEAM_MASTER _("Team master: ") +#define PROMPT_BRIDGE_MASTER _("Bridge master: ") +#define PROMPT_CONNECTION _("Connection (name, UUID, or path): ") + +static const char *nmc_known_vpns[] = + { "openvpn", "vpnc", "pptp", "openconnect", "openswan", "libreswan", + "ssh", "l2tp", "iodine", NULL }; + +/* Available fields for 'connection show' */ +static NmcOutputField nmc_fields_con_show[] = { + {"NAME", N_("NAME"), 25}, /* 0 */ + {"UUID", N_("UUID"), 38}, /* 1 */ + {"TYPE", N_("TYPE"), 17}, /* 2 */ + {"TIMESTAMP", N_("TIMESTAMP"), 12}, /* 3 */ + {"TIMESTAMP-REAL", N_("TIMESTAMP-REAL"), 34}, /* 4 */ + {"AUTOCONNECT", N_("AUTOCONNECT"), 13}, /* 5 */ + {"AUTOCONNECT-PRIORITY", N_("AUTOCONNECT-PRIORITY"), 10}, /* 6 */ + {"READONLY", N_("READONLY"), 10}, /* 7 */ + {"DBUS-PATH", N_("DBUS-PATH"), 42}, /* 8 */ + {"ACTIVE", N_("ACTIVE"), 10}, /* 9 */ + {"DEVICE", N_("DEVICE"), 10}, /* 10 */ + {"STATE", N_("STATE"), 12}, /* 11 */ + {"ACTIVE-PATH", N_("ACTIVE-PATH"), 51}, /* 12 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_CON_SHOW_ALL "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,AUTOCONNECT-PRIORITY,READONLY,DBUS-PATH,"\ + "ACTIVE,DEVICE,STATE,ACTIVE-PATH" +#define NMC_FIELDS_CON_SHOW_COMMON "NAME,UUID,TYPE,DEVICE" + +/* Helper macro to define fields */ +#define SETTING_FIELD(setting, props) { setting, N_(setting), 0, props, NULL, FALSE, FALSE, 0 } + +/* defined in settings.c */ +extern NmcOutputField nmc_fields_setting_connection[]; +extern NmcOutputField nmc_fields_setting_wired[]; +extern NmcOutputField nmc_fields_setting_8021X[]; +extern NmcOutputField nmc_fields_setting_wireless[]; +extern NmcOutputField nmc_fields_setting_wireless_security[]; +extern NmcOutputField nmc_fields_setting_ip4_config[]; +extern NmcOutputField nmc_fields_setting_ip6_config[]; +extern NmcOutputField nmc_fields_setting_serial[]; +extern NmcOutputField nmc_fields_setting_ppp[]; +extern NmcOutputField nmc_fields_setting_pppoe[]; +extern NmcOutputField nmc_fields_setting_adsl[]; +extern NmcOutputField nmc_fields_setting_gsm[]; +extern NmcOutputField nmc_fields_setting_cdma[]; +extern NmcOutputField nmc_fields_setting_bluetooth[]; +extern NmcOutputField nmc_fields_setting_olpc_mesh[]; +extern NmcOutputField nmc_fields_setting_vpn[]; +extern NmcOutputField nmc_fields_setting_wimax[]; +extern NmcOutputField nmc_fields_setting_infiniband[]; +extern NmcOutputField nmc_fields_setting_bond[]; +extern NmcOutputField nmc_fields_setting_vlan[]; +extern NmcOutputField nmc_fields_setting_bridge[]; +extern NmcOutputField nmc_fields_setting_bridge_port[]; +extern NmcOutputField nmc_fields_setting_team[]; +extern NmcOutputField nmc_fields_setting_team_port[]; +extern NmcOutputField nmc_fields_setting_dcb[]; + +/* Available settings for 'connection show <con>' - profile part */ +static NmcOutputField nmc_fields_settings_names[] = { + SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME, nmc_fields_setting_connection + 1), /* 0 */ + SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME, nmc_fields_setting_wired + 1), /* 1 */ + SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME, nmc_fields_setting_8021X + 1), /* 2 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME, nmc_fields_setting_wireless + 1), /* 3 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, nmc_fields_setting_wireless_security + 1), /* 4 */ + SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME, nmc_fields_setting_ip4_config + 1), /* 5 */ + SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME, nmc_fields_setting_ip6_config + 1), /* 6 */ + SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME, nmc_fields_setting_serial + 1), /* 7 */ + SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME, nmc_fields_setting_ppp + 1), /* 8 */ + SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME, nmc_fields_setting_pppoe + 1), /* 9 */ + SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME, nmc_fields_setting_gsm + 1), /* 10 */ + SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME, nmc_fields_setting_cdma + 1), /* 11 */ + SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME, nmc_fields_setting_bluetooth + 1), /* 12 */ + SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME, nmc_fields_setting_olpc_mesh + 1), /* 13 */ + SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME, nmc_fields_setting_vpn + 1), /* 14 */ + SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME, nmc_fields_setting_wimax + 1), /* 15 */ + SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME, nmc_fields_setting_infiniband + 1), /* 16 */ + SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME, nmc_fields_setting_bond + 1), /* 17 */ + SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME, nmc_fields_setting_vlan + 1), /* 18 */ + SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME, nmc_fields_setting_adsl + 1), /* 19 */ + SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME, nmc_fields_setting_bridge + 1), /* 20 */ + SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME, nmc_fields_setting_bridge_port + 1), /* 21 */ + SETTING_FIELD (NM_SETTING_TEAM_SETTING_NAME, nmc_fields_setting_team + 1), /* 22 */ + SETTING_FIELD (NM_SETTING_TEAM_PORT_SETTING_NAME, nmc_fields_setting_team_port + 1), /* 23 */ + SETTING_FIELD (NM_SETTING_DCB_SETTING_NAME, nmc_fields_setting_dcb + 1), /* 24 */ + {NULL, NULL, 0, NULL, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTINGS_NAMES_ALL_X NM_SETTING_CONNECTION_SETTING_NAME","\ + NM_SETTING_WIRED_SETTING_NAME","\ + NM_SETTING_802_1X_SETTING_NAME","\ + NM_SETTING_WIRELESS_SETTING_NAME","\ + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME","\ + NM_SETTING_IP4_CONFIG_SETTING_NAME","\ + NM_SETTING_IP6_CONFIG_SETTING_NAME","\ + NM_SETTING_SERIAL_SETTING_NAME","\ + NM_SETTING_PPP_SETTING_NAME","\ + NM_SETTING_PPPOE_SETTING_NAME","\ + NM_SETTING_ADSL_SETTING_NAME","\ + NM_SETTING_GSM_SETTING_NAME","\ + NM_SETTING_CDMA_SETTING_NAME","\ + NM_SETTING_BLUETOOTH_SETTING_NAME","\ + NM_SETTING_OLPC_MESH_SETTING_NAME","\ + NM_SETTING_VPN_SETTING_NAME","\ + NM_SETTING_INFINIBAND_SETTING_NAME","\ + NM_SETTING_BOND_SETTING_NAME","\ + NM_SETTING_VLAN_SETTING_NAME","\ + NM_SETTING_BRIDGE_SETTING_NAME","\ + NM_SETTING_BRIDGE_PORT_SETTING_NAME","\ + NM_SETTING_TEAM_SETTING_NAME","\ + NM_SETTING_TEAM_PORT_SETTING_NAME"," \ + NM_SETTING_DCB_SETTING_NAME +#if WITH_WIMAX +#define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X","\ + NM_SETTING_WIMAX_SETTING_NAME +#else +#define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X +#endif + +/* Active connection data */ +/* Available fields for GENERAL group */ +static NmcOutputField nmc_fields_con_active_details_general[] = { + {"GROUP", N_("GROUP"), 9}, /* 0 */ + {"NAME", N_("NAME"), 25}, /* 1 */ + {"UUID", N_("UUID"), 38}, /* 2 */ + {"DEVICES", N_("DEVICES"), 10}, /* 3 */ + {"STATE", N_("STATE"), 12}, /* 4 */ + {"DEFAULT", N_("DEFAULT"), 8}, /* 5 */ + {"DEFAULT6", N_("DEFAULT6"), 9}, /* 6 */ + {"SPEC-OBJECT", N_("SPEC-OBJECT"), 10}, /* 7 */ + {"VPN", N_("VPN"), 5}, /* 8 */ + {"DBUS-PATH", N_("DBUS-PATH"), 51}, /* 9 */ + {"CON-PATH", N_("CON-PATH"), 44}, /* 10 */ + {"ZONE", N_("ZONE"), 15}, /* 11 */ + {"MASTER-PATH", N_("MASTER-PATH"), 44}, /* 12 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL "GROUP,NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,"\ + "VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH" + +/* IP group is handled by common.c */ + +/* Available fields for VPN group */ +static NmcOutputField nmc_fields_con_active_details_vpn[] = { + {"GROUP", N_("GROUP"), 9}, /* 0 */ + {"TYPE", N_("TYPE"), 15}, /* 1 */ + {"USERNAME", N_("USERNAME"), 15}, /* 2 */ + {"GATEWAY", N_("GATEWAY"), 25}, /* 3 */ + {"BANNER", N_("BANNER"), 120}, /* 4 */ + {"VPN-STATE", N_("VPN-STATE"), 40}, /* 5 */ + {"CFG", N_("CFG"), 120}, /* 6 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG" + +/* defined in common.c */ +extern NmcOutputField nmc_fields_ip4_config[]; +extern NmcOutputField nmc_fields_ip6_config[]; +extern NmcOutputField nmc_fields_dhcp4_config[]; +extern NmcOutputField nmc_fields_dhcp6_config[]; + +/* Available fields for 'connection show <con>' - active part */ +static NmcOutputField nmc_fields_con_active_details_groups[] = { + {"GENERAL", N_("GENERAL"), 0, nmc_fields_con_active_details_general + 1}, /* 0 */ + {"IP4", N_("IP4"), 0, nmc_fields_ip4_config + 1 }, /* 1 */ + {"DHCP4", N_("DHCP4"), 0, nmc_fields_dhcp4_config + 1 }, /* 2 */ + {"IP6", N_("IP6"), 0, nmc_fields_ip6_config + 1 }, /* 3 */ + {"DHCP6", N_("DHCP6"), 0, nmc_fields_dhcp6_config + 1 }, /* 4 */ + {"VPN", N_("VPN"), 0, nmc_fields_con_active_details_vpn + 1 }, /* 5 */ + {NULL, NULL, 0, NULL} +}; +#define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL "GENERAL,IP4,DHCP4,IP6,DHCP6,VPN" + +/* Pseudo group names for 'connection show <con>' */ +/* e.g.: nmcli -f profile con show my-eth0 */ +/* e.g.: nmcli -f active con show my-eth0 */ +#define CON_SHOW_DETAIL_GROUP_PROFILE "profile" +#define CON_SHOW_DETAIL_GROUP_ACTIVE "active" + +/* glib main loop variable - defined in nmcli.c */ +extern GMainLoop *loop; + +static guint progress_id = 0; /* ID of event source for displaying progress */ + +/* for readline TAB completion in editor */ +typedef struct { + NmCli *nmc; + char *con_type; + NMConnection *connection; + NMSetting *setting; +} TabCompletionInfo; +static TabCompletionInfo nmc_tab_completion = {NULL, NULL, NULL, NULL}; + +/* Global variable defined in nmcli.c - used for TAB completion */ +extern NmCli nm_cli; + +static char *gen_connection_types (const char *text, int state); + +static void +usage (void) +{ + g_printerr (_("Usage: nmcli connection { COMMAND | help }\n\n" + "COMMAND := { show | up | down | add | modify | edit | delete | reload | load }\n\n" + " show [--active] [[--show-secrets] [id | uuid | path | apath] <ID>] ...\n\n" +#if WITH_WIMAX + " up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n\n" +#else + " up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [passwd-file <file with passwords>]\n\n" +#endif + " down [id | uuid | path | apath] <ID> ...\n\n" + " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n" + " modify [--temporary] [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n\n" + " edit [id | uuid | path] <ID>\n" + " edit [type <new_con_type>] [con-name <new_con_name>]\n\n" + " delete [id | uuid | path] <ID>\n\n" + " reload\n\n" + " load <filename> [ <filename>... ]\n\n")); +} + +static void +usage_connection_show (void) +{ + g_printerr (_("Usage: nmcli connection show { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [--active]\n" + "\n" + "List in-memory and on-disk connection profiles, some of which may also be\n" + "active if a device is using that connection profile. Without a parameter, all\n" + "profiles are listed. When --active option is specified, only the active\n" + "profiles are shown.\n" + "\n" + "ARGUMENTS := [--active] [--show-secrets] [id | uuid | path | apath] <ID> ...\n" + "\n" + "Show details for specified connections. By default, both static configuration\n" + "and active connection data are displayed. It is possible to filter the output\n" + "using global '--fields' option. Refer to the manual page for more information.\n" + "When --active option is specified, only the active profiles are taken into\n" + "account. --show-secrets option will reveal associated secrets as well.\n")); +} + +static void +usage_connection_up (void) +{ + g_printerr (_("Usage: nmcli connection up { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [id | uuid | path] <ID> [ifname <ifname>] [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n" + "\n" + "Activate a connection on a device. The profile to activate is identified by its\n" + "name, UUID or D-Bus path.\n" + "\n" + "ARGUMENTS := ifname <ifname> [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n" + "\n" + "Activate a device with a connection. The connection profile is selected\n" + "automatically by NetworkManager.\n" + "\n" + "ifname - specifies the device to active the connection on\n" + "ap - specifies AP to connect to (only valid for Wi-Fi)\n" + "nsp - specifies NSP to connect to (only valid for WiMAX)\n" + "passwd-file - file with password(s) required to activate the connection\n\n")); +} + +static void +usage_connection_down (void) +{ + g_printerr (_("Usage: nmcli connection down { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [id | uuid | path | apath] <ID> ...\n" + "\n" + "Deactivate a connection from a device (without preventing the device from\n" + "further auto-activation). The profile to deactivate is identified by its name,\n" + "UUID or D-Bus path.\n\n")); +} + +static void +usage_connection_add (void) +{ + g_printerr (_("Usage: nmcli connection add { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n" + " COMMON_OPTIONS:\n" + " type <type>\n" + " ifname <interface name> | \"*\"\n" + " [con-name <connection name>]\n" + " [autoconnect yes|no]\n\n" + " [save yes|no]\n\n" + " TYPE_SPECIFIC_OPTIONS:\n" + " ethernet: [mac <MAC address>]\n" + " [cloned-mac <cloned MAC address>]\n" + " [mtu <MTU>]\n\n" + " wifi: ssid <SSID>\n" + " [mac <MAC address>]\n" + " [cloned-mac <cloned MAC address>]\n" + " [mtu <MTU>]\n" + " [mode infrastructure|ap|adhoc]\n\n" + " wimax: [mac <MAC address>]\n" + " [nsp <NSP>]\n\n" + " pppoe: username <PPPoE username>\n" + " [password <PPPoE password>]\n" + " [service <PPPoE service name>]\n" + " [mtu <MTU>]\n" + " [mac <MAC address>]\n\n" + " gsm: apn <APN>\n" + " [user <username>]\n" + " [password <password>]\n\n" + " cdma: [user <username>]\n" + " [password <password>]\n\n" + " infiniband: [mac <MAC address>]\n" + " [mtu <MTU>]\n" + " [transport-mode datagram | connected]\n" + " [parent <ifname>]\n" + " [p-key <IPoIB P_Key>]\n\n" + " bluetooth: [addr <bluetooth address>]\n" + " [bt-type panu|dun-gsm|dun-cdma]\n\n" + " vlan: dev <parent device (connection UUID, ifname, or MAC)>\n" + " id <VLAN ID>\n" + " [flags <VLAN flags>]\n" + " [ingress <ingress priority mapping>]\n" + " [egress <egress priority mapping>]\n" + " [mtu <MTU>]\n\n" + " bond: [mode balance-rr (0) | active-backup (1) | balance-xor (2) | broadcast (3) |\n" + " 802.3ad (4) | balance-tlb (5) | balance-alb (6)]\n" + " [primary <ifname>]\n" + " [miimon <num>]\n" + " [downdelay <num>]\n" + " [updelay <num>]\n" + " [arp-interval <num>]\n" + " [arp-ip-target <num>]\n" + " [lacp-rate slow (0) | fast (1)]\n\n" + " bond-slave: master <master (ifname, or connection UUID or name)>\n\n" + " team: [config <file>|<raw JSON data>]\n\n" + " team-slave: master <master (ifname, or connection UUID or name)>\n" + " [config <file>|<raw JSON data>]\n\n" + " bridge: [stp yes|no]\n" + " [priority <num>]\n" + " [forward-delay <2-30>]\n" + " [hello-time <1-10>]\n" + " [max-age <6-40>]\n" + " [ageing-time <0-1000000>]\n" + " [mac <MAC address>]\n\n" + " bridge-slave: master <master (ifname, or connection UUID or name)>\n" + " [priority <0-63>]\n" + " [path-cost <1-65535>]\n" + " [hairpin yes|no]\n\n" + " vpn: vpn-type vpnc|openvpn|pptp|openconnect|openswan|libreswan|ssh|l2tp|iodine|...\n" + " [user <username>]\n\n" + " olpc-mesh: ssid <SSID>\n" + " [channel <1-13>]\n" + " [dhcp-anycast <MAC address>]\n\n" + " IP_OPTIONS:\n" + " [ip4 <IPv4 address>] [gw4 <IPv4 gateway>]\n" + " [ip6 <IPv6 address>] [gw6 <IPv6 gateway>]\n\n")); +} + +static void +usage_connection_modify (void) +{ + g_printerr (_("Usage: nmcli connection modify { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n" + "\n" + "Modify one or more properties of the connection profile.\n" + "The profile is identified by its name, UUID or D-Bus path. For multi-valued\n" + "properties you can use optional '+' or '-' prefix to the property name.\n" + "The '+' sign allows appending items instead of overwriting the whole value.\n" + "The '-' sign allows removing selected items instead of the whole value.\n" + "\n" + "Examples:\n" + "nmcli con mod home-wifi wifi.ssid rakosnicek\n" + "nmcli con mod em1-1 ipv4.method manual ipv4.addr \"192.168.1.2/24, 10.10.1.5/8\"\n" + "nmcli con mod em1-1 +ipv4.dns 8.8.4.4\n" + "nmcli con mod em1-1 -ipv4.dns 1\n" + "nmcli con mod em1-1 -ipv6.addr \"abbe::cafe/56\"\n" + "nmcli con mod bond0 +bond.options mii=500\n" + "nmcli con mod bond0 -bond.options downdelay\n\n")); +} + +static void +usage_connection_edit (void) +{ + g_printerr (_("Usage: nmcli connection edit { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [id | uuid | path] <ID>\n" + "\n" + "Edit an existing connection profile in an interactive editor.\n" + "The profile is identified by its name, UUID or D-Bus path\n" + "\n" + "ARGUMENTS := [type <new connection type>] [con-name <new connection name>]\n" + "\n" + "Add a new connection profile in an interactive editor.\n\n")); +} + +static void +usage_connection_delete (void) +{ + g_printerr (_("Usage: nmcli connection delete { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [id | uuid | path] <ID>\n" + "\n" + "Delete a connection profile.\n" + "The profile is identified by its name, UUID or D-Bus path.\n\n")); +} + +static void +usage_connection_reload (void) +{ + g_printerr (_("Usage: nmcli connection reload { help }\n" + "\n" + "Reload all connection files from disk.\n\n")); +} + +static void +usage_connection_load (void) +{ + g_printerr (_("Usage: nmcli connection load { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := <filename> [<filename>...]\n" + "\n" + "Load/reload one or more connection files from disk. Use this after manually\n" + "editing a connection file to ensure that NetworkManager is aware of its latest\n" + "state.\n\n")); +} + +static gboolean +usage_connection_second_level (const char *cmd) +{ + gboolean ret = TRUE; + + if (matches (cmd, "show") == 0) + usage_connection_show (); + else if (matches (cmd, "up") == 0) + usage_connection_up (); + else if (matches (cmd, "down") == 0) + usage_connection_down (); + else if (matches (cmd, "add") == 0) + usage_connection_add (); + else if (matches (cmd, "modify") == 0) + usage_connection_modify (); + else if (matches (cmd, "edit") == 0) + usage_connection_edit (); + else if (matches (cmd, "delete") == 0) + usage_connection_delete (); + else if (matches (cmd, "reload") == 0) + usage_connection_reload (); + else if (matches (cmd, "load") == 0) + usage_connection_load (); + else + ret = FALSE; + return ret; +} + +/* quit main loop */ +static void +quit (void) +{ + if (progress_id) { + g_source_remove (progress_id); + progress_id = 0; + nmc_terminal_erase_line (); + } + + g_main_loop_quit (loop); /* quit main loop */ +} + +static const char * +construct_header_name (const char *base, const char *spec) +{ + static char header_name[128]; + + if (spec == NULL) + return base; + + g_strlcpy (header_name, base, sizeof (header_name)); + g_strlcat (header_name, " (", sizeof (header_name)); + g_strlcat (header_name, spec, sizeof (header_name)); + g_strlcat (header_name, ")", sizeof (header_name)); + + return header_name; +} + +static const char * +active_connection_state_to_string (NMActiveConnectionState state) +{ + switch (state) { + case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: + return _("activating"); + case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: + return _("activated"); + case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: + return _("deactivating"); + case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED: + return _("deactivated"); + case NM_ACTIVE_CONNECTION_STATE_UNKNOWN: + default: + return _("unknown"); + } +} + +static const char * +vpn_connection_state_to_string (NMVpnConnectionState state) +{ + switch (state) { + case NM_VPN_CONNECTION_STATE_PREPARE: + return _("VPN connecting (prepare)"); + case NM_VPN_CONNECTION_STATE_NEED_AUTH: + return _("VPN connecting (need authentication)"); + case NM_VPN_CONNECTION_STATE_CONNECT: + return _("VPN connecting"); + case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: + return _("VPN connecting (getting IP configuration)"); + case NM_VPN_CONNECTION_STATE_ACTIVATED: + return _("VPN connected"); + case NM_VPN_CONNECTION_STATE_FAILED: + return _("VPN connection failed"); + case NM_VPN_CONNECTION_STATE_DISCONNECTED: + return _("VPN disconnected"); + default: + return _("unknown"); + } +} + +/* Caller has to free the returned string */ +static char * +get_ac_device_string (NMActiveConnection *active) +{ + GString *dev_str; + const GPtrArray *devices; + int i; + + if (!active) + return NULL; + + /* Get devices of the active connection */ + dev_str = g_string_new (NULL); + devices = nm_active_connection_get_devices (active); + for (i = 0; i < devices->len; i++) { + NMDevice *device = g_ptr_array_index (devices, i); + const char *dev_iface = nm_device_get_iface (device); + + if (dev_iface) { + g_string_append (dev_str, dev_iface); + g_string_append_c (dev_str, ','); + } + } + if (dev_str->len > 0) + g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */ + + return g_string_free (dev_str, FALSE); +} + +static NMActiveConnection * +get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection) +{ + const char *con_path, *ac_con_path; + int i; + NMActiveConnection *ac = NULL; + + /* Is the connection active? */ + con_path = nm_connection_get_path (connection); + for (i = 0; i < active_cons->len; i++) { + NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); + NMRemoteConnection *con; + + con = nm_active_connection_get_connection (candidate); + ac_con_path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL; + if (!g_strcmp0 (ac_con_path, con_path)) { + ac = candidate; + break; + } + } + return ac; +} + +/* Put secrets into local connection. */ +static void +update_secrets_in_connection (NMRemoteConnection *remote, NMConnection *local) +{ + GVariant *secrets; + int i; + GError *error = NULL; + + for (i = 0; nmc_fields_settings_names[i].name; i++) { + secrets = nm_remote_connection_get_secrets (remote, nmc_fields_settings_names[i].name, NULL, NULL); + if (secrets) { + if (!nm_connection_update_secrets (local, NULL, secrets, &error) && error) { + g_printerr (_("Error updating secrets for %s: %s\n"), + nmc_fields_settings_names[i].name, + error->message); + g_clear_error (&error); + } + g_variant_unref (secrets); + } + } +} + +static gboolean +nmc_connection_profile_details (NMConnection *connection, NmCli *nmc, gboolean secrets) +{ + GError *error = NULL; + GArray *print_settings_array; + GPtrArray *prop_array = NULL; + int i; + char *fields_str; + char *fields_all = NMC_FIELDS_SETTINGS_NAMES_ALL; + char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL; + const char *base_hdr = _("Connection profile details"); + gboolean was_output = FALSE; + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, TRUE, &prop_array, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + g_assert (print_settings_array); + + /* Main header */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_connection_get_id (connection)); + nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL, + nmc_fields_settings_names, FALSE, NULL, NULL); + + nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY; + print_required_fields (nmc, nmc_fields_settings_names); + + /* Loop through the required settings and print them. */ + for (i = 0; i < print_settings_array->len; i++) { + NMSetting *setting; + int section_idx = g_array_index (print_settings_array, int, i); + const char *prop_name = (const char *) g_ptr_array_index (prop_array, i); + + if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) + g_print ("\n"); /* Empty line */ + + was_output = FALSE; + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name); + if (setting) { + setting_details (setting, nmc, prop_name, secrets); + was_output = TRUE; + continue; + } + } + + g_array_free (print_settings_array, TRUE); + if (prop_array) + g_ptr_array_free (prop_array, TRUE); + + return TRUE; +} + +static NMActiveConnection * +find_active_connection (const GPtrArray *active_cons, + const GPtrArray *cons, + const char *filter_type, + const char *filter_val, + int *idx) +{ + int i; + int start = (idx && *idx > 0) ? *idx : 0; + const char *path, *a_path, *path_num, *a_path_num; + const char *id; + const char *uuid; + NMRemoteConnection *con; + NMActiveConnection *found = NULL; + + for (i = start; i < active_cons->len; i++) { + NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); + + con = nm_active_connection_get_connection (candidate); + + id = nm_active_connection_get_id (candidate); + uuid = nm_active_connection_get_uuid (candidate); + path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL; + path_num = path ? strrchr (path, '/') + 1 : NULL; + a_path = nm_object_get_path (NM_OBJECT (candidate)); + a_path_num = a_path ? strrchr (a_path, '/') + 1 : NULL; + + /* When filter_type is NULL, compare connection ID (filter_val) + * against all types. Otherwise, only compare against the specific + * type. If 'path' or 'apath' filter types are specified, comparison + * against numeric index (in addition to the whole path) is allowed. + */ + if ( ( (!filter_type || strcmp (filter_type, "id") == 0) + && strcmp (filter_val, id) == 0) + || ( (!filter_type || strcmp (filter_type, "uuid") == 0) + && strcmp (filter_val, uuid) == 0) + || ( (!filter_type || strcmp (filter_type, "path") == 0) + && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0))) + || ( (!filter_type || strcmp (filter_type, "apath") == 0) + && (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0)))) { + if (!idx) + return candidate; + if (found) { + *idx = i; + return found; + } + found = candidate; + } + } + + if (idx) + *idx = 0; + return found; +} + +static void +fill_output_connection (gpointer data, gpointer user_data, gboolean active_only) +{ + NMConnection *connection = (NMConnection *) data; + NmCli *nmc = (NmCli *) user_data; + NMSettingConnection *s_con; + guint64 timestamp; + time_t timestamp_real; + char *timestamp_str; + char *timestamp_real_str = ""; + char *prio_str; + NmcOutputField *arr; + NMActiveConnection *ac = NULL; + const char *ac_path = NULL; + const char *ac_state = NULL; + char *ac_dev = NULL; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ac = get_ac_for_connection (nm_client_get_active_connections (nmc->client), connection); + if (active_only && !ac) + return; + + if (ac) { + ac_path = nm_object_get_path (NM_OBJECT (ac)); + ac_state = active_connection_state_to_string (nm_active_connection_get_state (ac)); + ac_dev = get_ac_device_string (ac); + } + + /* Obtain field values */ + timestamp = nm_setting_connection_get_timestamp (s_con); + timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp); + if (timestamp) { + timestamp_real = timestamp; + timestamp_real_str = g_malloc0 (64); + strftime (timestamp_real_str, 64, "%c", localtime (×tamp_real)); + } + prio_str = g_strdup_printf ("%u", nm_setting_connection_get_autoconnect_priority (s_con)); + + arr = nmc_dup_fields_array (nmc_fields_con_show, + sizeof (nmc_fields_con_show), + 0); + set_val_strc (arr, 0, nm_setting_connection_get_id (s_con)); + set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con)); + set_val_strc (arr, 2, nm_setting_connection_get_connection_type (s_con)); + set_val_str (arr, 3, timestamp_str); + set_val_str (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never"))); + set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no")); + set_val_str (arr, 6, prio_str); + set_val_strc (arr, 7, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no")); + set_val_strc (arr, 8, nm_connection_get_path (connection)); + set_val_strc (arr, 9, ac ? _("yes") : _("no")); + set_val_str (arr, 10, ac_dev); + set_val_strc (arr, 11, ac_state); + set_val_strc (arr, 12, ac_path); + + g_ptr_array_add (nmc->output_data, arr); +} + +static void +fill_output_connection_for_invisible (NMActiveConnection *ac, NmCli *nmc) +{ + NmcOutputField *arr; + const char *ac_path = NULL; + const char *ac_state = NULL; + char *ac_dev = NULL; + + ac_path = nm_object_get_path (NM_OBJECT (ac)); + ac_state = active_connection_state_to_string (nm_active_connection_get_state (ac)); + ac_dev = get_ac_device_string (ac); + + arr = nmc_dup_fields_array (nmc_fields_con_show, + sizeof (nmc_fields_con_show), + 0); + set_val_strc (arr, 0, nm_active_connection_get_id (ac)); + set_val_strc (arr, 1, nm_active_connection_get_uuid (ac)); + set_val_strc (arr, 2, nm_active_connection_get_connection_type (ac)); + set_val_strc (arr, 3, NULL); + set_val_strc (arr, 4, NULL); + set_val_strc (arr, 5, NULL); + set_val_strc (arr, 6, NULL); + set_val_strc (arr, 7, NULL); + set_val_strc (arr, 8, NULL); + set_val_strc (arr, 9, _("yes")); + set_val_str (arr, 10, ac_dev); + set_val_strc (arr, 11, ac_state); + set_val_strc (arr, 12, ac_path); + + g_ptr_array_add (nmc->output_data, arr); +} + +static void +fill_output_active_connection (NMActiveConnection *active, + NmCli *nmc, + gboolean with_group, + guint32 o_flags) +{ + NMRemoteConnection *con; + NMSettingConnection *s_con; + const GPtrArray *devices; + GString *dev_str; + NMActiveConnectionState state; + NMDevice *master; + const char *con_path = NULL, *con_zone = NULL; + int i; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + int idx_start = with_group ? 0 : 1; + + con = nm_active_connection_get_connection (active); + if (con) { + con_path = nm_connection_get_path (NM_CONNECTION (con)); + s_con = nm_connection_get_setting_connection (NM_CONNECTION (con)); + g_assert (s_con != NULL); + con_zone = nm_setting_connection_get_zone (s_con); + } + + state = nm_active_connection_get_state (active); + master = nm_active_connection_get_master (active); + + /* Get devices of the active connection */ + dev_str = g_string_new (NULL); + devices = nm_active_connection_get_devices (active); + for (i = 0; i < devices->len; i++) { + NMDevice *device = g_ptr_array_index (devices, i); + const char *dev_iface = nm_device_get_iface (device); + + if (dev_iface) { + g_string_append (dev_str, dev_iface); + g_string_append_c (dev_str, ','); + } + } + if (dev_str->len > 0) + g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */ + + tmpl = nmc_fields_con_active_details_general; + tmpl_len = sizeof (nmc_fields_con_active_details_general); + if (!with_group) { + tmpl++; + tmpl_len -= sizeof (NmcOutputField); + } + + /* Fill field values */ + arr = nmc_dup_fields_array (tmpl, tmpl_len, o_flags); + if (with_group) + set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0].name); + set_val_strc (arr, 1-idx_start, nm_active_connection_get_id (active)); + set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active)); + set_val_str (arr, 3-idx_start, dev_str->str); + set_val_strc (arr, 4-idx_start, active_connection_state_to_string (state)); + set_val_strc (arr, 5-idx_start, nm_active_connection_get_default (active) ? _("yes") : _("no")); + set_val_strc (arr, 6-idx_start, nm_active_connection_get_default6 (active) ? _("yes") : _("no")); + set_val_strc (arr, 7-idx_start, nm_active_connection_get_specific_object_path (active)); + set_val_strc (arr, 8-idx_start, NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no")); + set_val_strc (arr, 9-idx_start, nm_object_get_path (NM_OBJECT (active))); + set_val_strc (arr, 10-idx_start, con_path); + set_val_strc (arr, 11-idx_start, con_zone); + set_val_strc (arr, 12-idx_start, master ? nm_object_get_path (NM_OBJECT (master)) : NULL); + + g_ptr_array_add (nmc->output_data, arr); + + g_string_free (dev_str, FALSE); +} + +static void +fill_output_for_all_invisible (NmCli *nmc) +{ + const GPtrArray *acons; + int a, c; + + g_return_if_fail (nmc != NULL); + + acons = nm_client_get_active_connections (nmc->client); + for (a = 0; a < acons->len; a++) { + gboolean found = FALSE; + NMActiveConnection *acon = g_ptr_array_index (acons, a); + const char *a_uuid = nm_active_connection_get_uuid (acon); + + for (c = 0; c < nmc->connections->len; c++) { + NMConnection *con = g_ptr_array_index (nmc->connections, c); + const char *c_uuid = nm_connection_get_uuid (con); + + if (strcmp (a_uuid, c_uuid) == 0) { + found = TRUE; + break; + } + } + + /* Active connection is not in connections list */ + if (!found) + fill_output_connection_for_invisible (acon, nmc); + } +} + +typedef struct { + char **array; + guint32 idx; +} FillVPNDataInfo; + +static void +fill_vpn_data_item (const char *key, const char *value, gpointer user_data) +{ + FillVPNDataInfo *info = (FillVPNDataInfo *) user_data; + + info->array[info->idx++] = g_strdup_printf ("%s = %s", key, value); +} + +// FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c), +// and in gnome-control-center as well. It could probably be shared somehow. +static char * +get_vpn_connection_type (NMConnection *connection) +{ + const char *type, *p; + + /* The service type is in form of "org.freedesktop.NetworkManager.vpnc". + * Extract end part after last dot, e.g. "vpnc" + */ + type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection)); + p = strrchr (type, '.'); + return g_strdup (p ? p + 1 : type); +} + +/* VPN parameters can be found at: + * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h + * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h + * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h + * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h + * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h + * See also 'properties' directory in these plugins. + */ +static const gchar * +find_vpn_gateway_key (const char *vpn_type) +{ + if (g_strcmp0 (vpn_type, "openvpn") == 0) return "remote"; + if (g_strcmp0 (vpn_type, "vpnc") == 0) return "IPSec gateway"; + if (g_strcmp0 (vpn_type, "pptp") == 0) return "gateway"; + if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway"; + if (g_strcmp0 (vpn_type, "openswan") == 0) return "right"; + if (g_strcmp0 (vpn_type, "libreswan") == 0) return "right"; + if (g_strcmp0 (vpn_type, "ssh") == 0) return "remote"; + if (g_strcmp0 (vpn_type, "l2tp") == 0) return "gateway"; + return ""; +} + +static const gchar * +find_vpn_username_key (const char *vpn_type) +{ + if (g_strcmp0 (vpn_type, "openvpn") == 0) return "username"; + if (g_strcmp0 (vpn_type, "vpnc") == 0) return "Xauth username"; + if (g_strcmp0 (vpn_type, "pptp") == 0) return "user"; + if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username"; + if (g_strcmp0 (vpn_type, "openswan") == 0) return "leftxauthusername"; + if (g_strcmp0 (vpn_type, "libreswan") == 0) return "leftxauthusername"; + if (g_strcmp0 (vpn_type, "l2tp") == 0) return "user"; + return ""; +} + +enum VpnDataItem { + VPN_DATA_ITEM_GATEWAY, + VPN_DATA_ITEM_USERNAME +}; + +static const gchar * +get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item) +{ + const char *key; + char *type = get_vpn_connection_type (connection); + + switch (vpn_data_item) { + case VPN_DATA_ITEM_GATEWAY: + key = find_vpn_gateway_key (type); + break; + case VPN_DATA_ITEM_USERNAME: + key = find_vpn_username_key (type); + break; + default: + key = ""; + break; + } + g_free (type); + + return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key); +} +/* FIXME end */ + +static gboolean +nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc) +{ + GError *error = NULL; + GArray *print_groups; + GPtrArray *group_fields = NULL; + int i; + char *fields_str; + char *fields_all = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL; + char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + const char *base_hdr = _("Activate connection details"); + gboolean was_output = FALSE; + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, TRUE, &group_fields, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + g_assert (print_groups); + + /* Main header */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_active_connection_get_uuid (acon)); + nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL, + nmc_fields_con_active_details_groups, FALSE, NULL, NULL); + + nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY; + print_required_fields (nmc, nmc_fields_con_active_details_groups); + + /* Loop through the groups and print them. */ + for (i = 0; i < print_groups->len; i++) { + int group_idx = g_array_index (print_groups, int, i); + char *group_fld = (char *) g_ptr_array_index (group_fields, i); + + if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) + g_print ("\n"); /* Empty line */ + + was_output = FALSE; + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + /* GENERAL */ + if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) { + /* Add field names */ + tmpl = nmc_fields_con_active_details_general; + tmpl_len = sizeof (nmc_fields_con_active_details_general); + nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + /* Fill in values */ + fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX); + + print_data (nmc); /* Print all data */ + + was_output = TRUE; + } + + /* IP4 */ + if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[1].name) == 0) { + gboolean b1 = FALSE; + NMIPConfig *cfg4 = nm_active_connection_get_ip4_config (acon); + + b1 = print_ip4_config (cfg4, nmc, "IP4", group_fld); + was_output = was_output || b1; + } + + /* DHCP4 */ + if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[2].name) == 0) { + gboolean b1 = FALSE; + NMDhcpConfig *dhcp4 = nm_active_connection_get_dhcp4_config (acon); + + b1 = print_dhcp4_config (dhcp4, nmc, "DHCP4", group_fld); + was_output = was_output || b1; + } + + /* IP6 */ + if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[3].name) == 0) { + gboolean b1 = FALSE; + NMIPConfig *cfg6 = nm_active_connection_get_ip6_config (acon); + + b1 = print_ip6_config (cfg6, nmc, "IP6", group_fld); + was_output = was_output || b1; + } + + /* DHCP6 */ + if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[4].name) == 0) { + gboolean b1 = FALSE; + NMDhcpConfig *dhcp6 = nm_active_connection_get_dhcp6_config (acon); + + b1 = print_dhcp6_config (dhcp6, nmc, "DHCP6", group_fld); + was_output = was_output || b1; + } + + /* VPN */ + if (NM_IS_VPN_CONNECTION (acon) && + strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[5].name) == 0) { + NMConnection *con; + NMSettingConnection *s_con; + NMSettingVpn *s_vpn; + NMVpnConnectionState vpn_state; + char *type_str, *banner_str = NULL, *vpn_state_str; + const char *banner; + const char *username = NULL; + char **vpn_data_array = NULL; + guint32 items_num; + + con = NM_CONNECTION (nm_active_connection_get_connection (acon)); + + s_con = nm_connection_get_setting_connection (con); + g_assert (s_con != NULL); + + tmpl = nmc_fields_con_active_details_vpn; + tmpl_len = sizeof (nmc_fields_con_active_details_vpn); + nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + s_vpn = nm_connection_get_setting_vpn (con); + if (s_vpn) { + items_num = nm_setting_vpn_get_num_data_items (s_vpn); + if (items_num > 0) { + FillVPNDataInfo info; + + vpn_data_array = g_new (char *, items_num + 1); + info.array = vpn_data_array; + info.idx = 0; + nm_setting_vpn_foreach_data_item (s_vpn, &fill_vpn_data_item, &info); + vpn_data_array[items_num] = NULL; + } + username = nm_setting_vpn_get_user_name (s_vpn); + } + + type_str = get_vpn_connection_type (con); + banner = nm_vpn_connection_get_banner (NM_VPN_CONNECTION (acon)); + if (banner) + banner_str = g_strescape (banner, ""); + vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon)); + vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state)); + + /* Add values */ + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_con_active_details_groups[5].name); + set_val_str (arr, 1, type_str); + set_val_strc (arr, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME)); + set_val_strc (arr, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY)); + set_val_str (arr, 4, banner_str); + set_val_str (arr, 5, vpn_state_str); + set_val_arr (arr, 6, vpn_data_array); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + was_output = TRUE; + } + } + + g_array_free (print_groups, TRUE); + if (group_fields) + g_ptr_array_free (group_fields, TRUE); + + return TRUE; +} + +static gboolean +split_required_fields_for_con_show (const char *input, + char **profile_flds, + char **active_flds, + GError **error) +{ + char **fields, **iter; + char *dot; + GString *str1, *str2; + gboolean found; + gboolean group_profile = FALSE; + gboolean group_active = FALSE; + gboolean success = TRUE; + gboolean is_all, is_common; + int i; + + if (!input) { + *profile_flds = NULL; + *active_flds = NULL; + return TRUE; + } + + str1 = g_string_new (NULL); + str2 = g_string_new (NULL); + + /* Split supplied fields string */ + fields = g_strsplit_set (input, ",", -1); + for (iter = fields; iter && *iter; iter++) { + g_strstrip (*iter); + dot = strchr (*iter, '.'); + if (dot) + *dot = '\0'; + + is_all = !dot && strcasecmp (*iter, "all") == 0; + is_common = !dot && strcasecmp (*iter, "common") == 0; + + found = FALSE; + + for (i = 0; nmc_fields_settings_names[i].name; i++) { + if ( is_all || is_common + || !strcasecmp (*iter, nmc_fields_settings_names[i].name)) { + if (dot) + *dot = '.'; + g_string_append (str1, *iter); + g_string_append_c (str1, ','); + found = TRUE; + break; + } + } + if (found) + continue; + for (i = 0; nmc_fields_con_active_details_groups[i].name; i++) { + if ( is_all || is_common + || !strcasecmp (*iter, nmc_fields_con_active_details_groups[i].name)) { + if (dot) + *dot = '.'; + g_string_append (str2, *iter); + g_string_append_c (str2, ','); + found = TRUE; + break; + } + } + if (!found) { + if (dot) + *dot = '.'; + if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_PROFILE)) + group_profile = TRUE; + else if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_ACTIVE)) + group_active = TRUE; + else { + char *allowed1 = nmc_get_allowed_fields (nmc_fields_settings_names, -1); + char *allowed2 = nmc_get_allowed_fields (nmc_fields_con_active_details_groups, -1); + g_set_error (error, NMCLI_ERROR, 0, _("invalid field '%s'; allowed fields: %s and %s, or %s,%s"), + *iter, allowed1, allowed2, CON_SHOW_DETAIL_GROUP_PROFILE, CON_SHOW_DETAIL_GROUP_ACTIVE); + g_free (allowed1); + g_free (allowed2); + success = FALSE; + break; + } + } + } + if (fields) + g_strfreev (fields); + + /* Handle pseudo groups: profile, active */ + if (success && group_profile) { + if (str1->len > 0) { + g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"), + CON_SHOW_DETAIL_GROUP_PROFILE); + success = FALSE; + } else + g_string_assign (str1, "all,"); + } + if (success && group_active) { + if (str2->len > 0) { + g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"), + CON_SHOW_DETAIL_GROUP_ACTIVE); + success = FALSE; + } else + g_string_assign (str2, "all,"); + } + + if (success) { + if (str1->len > 0) + g_string_truncate (str1, str1->len - 1); + if (str2->len > 0) + g_string_truncate (str2, str2->len - 1); + *profile_flds = g_string_free (str1, str1->len == 0); + *active_flds = g_string_free (str2, str2->len == 0); + } else { + g_string_free (str1, TRUE); + g_string_free (str2, TRUE); + } + return success; +} + +static NMCResultCode +do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, + int argc, char **argv) +{ + GError *err = NULL; + char *profile_flds = NULL, *active_flds = NULL; + + nmc->should_wait = FALSE; + + if (argc == 0) { + char *fields_str; + char *fields_all = NMC_FIELDS_CON_SHOW_ALL; + char *fields_common = NMC_FIELDS_CON_SHOW_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + int i; + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + tmpl = nmc_fields_con_show; + tmpl_len = sizeof (nmc_fields_con_show); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err); + if (err) { + goto finish; + } + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &err)) + goto finish; + + /* Add headers */ + nmc->print_fields.header_name = active_only ? _("NetworkManager active profiles") : + _("NetworkManager connection profiles"); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + /* Add values */ + for (i = 0; i < nmc->connections->len; i++) { + NMConnection *con = NM_CONNECTION (nmc->connections->pdata[i]); + fill_output_connection (con, nmc, active_only); + } + /* Some active connections may not be in connection list, show them here. */ + fill_output_for_all_invisible (nmc); + print_data (nmc); /* Print all data */ + } else { + gboolean new_line = FALSE; + gboolean without_fields = (nmc->required_fields == NULL); + const GPtrArray *active_cons = nm_client_get_active_connections (nmc->client); + int pos = 0; + + /* multiline mode is default for 'connection show <ID>' */ + if (!nmc->mode_specified) + nmc->multiline_output = TRUE; + + /* Split required fields into the settings and active ones. */ + if (!split_required_fields_for_con_show (nmc->required_fields, &profile_flds, &active_flds, &err)) + goto finish; + g_free (nmc->required_fields); + nmc->required_fields = NULL; + + while (argc > 0) { + gboolean res; + NMConnection *con; + NMActiveConnection *acon = NULL; + const char *selector = NULL; + + if ( strcmp (*argv, "id") == 0 + || strcmp (*argv, "uuid") == 0 + || strcmp (*argv, "path") == 0 + || strcmp (*argv, "apath") == 0) { + selector = *argv; + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + } + + /* Find connection by id, uuid, path or apath */ + con = nmc_find_connection (nmc->connections, selector, *argv, &pos); + if (!con) { + acon = find_active_connection (active_cons, nmc->connections, selector, *argv, NULL); + if (acon) + con = NM_CONNECTION (nm_active_connection_get_connection (acon)); + } + + if (!con && !acon) { + g_string_printf (nmc->return_text, _("Error: %s - no such connection profile."), *argv); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto finish; + } + + /* Print connection details: + * Usually we have both static and active connection. + * But when a connection is private to a user, another user + * may see only the active connection. + */ + + /* Filter only active connections */ + if (!acon) + acon = get_ac_for_connection (active_cons, con); + if (active_only && !acon) { + next_arg (&argc, &argv); + continue; + } + + /* Show an empty line between connections */ + if (new_line) + g_print ("\n"); + + /* Show profile configuration */ + if (without_fields || profile_flds) { + if (con) { + nmc->required_fields = profile_flds; + if (show_secrets) + update_secrets_in_connection (NM_REMOTE_CONNECTION (con), con); + res = nmc_connection_profile_details (con, nmc, show_secrets); + nmc->required_fields = NULL; + if (!res) + goto finish; + } + } + + /* If the profile is active, print also active details */ + if (without_fields || active_flds) { + if (acon) { + nmc->required_fields = active_flds; + res = nmc_active_connection_details (acon, nmc); + nmc->required_fields = NULL; + if (!res) + goto finish; + } + } + new_line = TRUE; + + /* Take next argument. + * But for pos != NULL we have more connections of the same name, + * so process the same argument again. + */ + if (!pos) + next_arg (&argc, &argv); + } + } + +finish: + if (err) { + g_string_printf (nmc->return_text, _("Error: %s."), err->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_error_free (err); + } + g_free (profile_flds); + g_free (active_flds); + return nmc->return_value; +} + +static NMActiveConnection * +get_default_active_connection (NmCli *nmc, NMDevice **device) +{ + NMActiveConnection *default_ac = NULL; + NMDevice *non_default_device = NULL; + NMActiveConnection *non_default_ac = NULL; + const GPtrArray *connections; + int i; + + g_return_val_if_fail (nmc != NULL, NULL); + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (*device == NULL, NULL); + + connections = nm_client_get_active_connections (nmc->client); + for (i = 0; i < connections->len; i++) { + NMActiveConnection *candidate = g_ptr_array_index (connections, i); + const GPtrArray *devices; + + devices = nm_active_connection_get_devices (candidate); + if (!devices->len) + continue; + + if (nm_active_connection_get_default (candidate)) { + if (!default_ac) { + *device = g_ptr_array_index (devices, 0); + default_ac = candidate; + } + } else { + if (!non_default_ac) { + non_default_device = g_ptr_array_index (devices, 0); + non_default_ac = candidate; + } + } + } + + /* Prefer the default connection if one exists, otherwise return the first + * non-default connection. + */ + if (!default_ac && non_default_ac) { + default_ac = non_default_ac; + *device = non_default_device; + } + return default_ac; +} + +/* Find a device to activate the connection on. + * IN: connection: connection to activate + * iface: device interface name to use (optional) + * ap: access point to use (optional; valid just for 802-11-wireless) + * nsp: Network Service Provider to use (option; valid only for wimax) + * OUT: device: found device + * spec_object: specific_object path of NMAccessPoint + * RETURNS: TRUE when a device is found, FALSE otherwise. + */ +static gboolean +find_device_for_connection (NmCli *nmc, + NMConnection *connection, + const char *iface, + const char *ap, + const char *nsp, + NMDevice **device, + const char **spec_object, + GError **error) +{ + NMSettingConnection *s_con; + const char *con_type; + int i, j; + + g_return_val_if_fail (nmc != NULL, FALSE); + g_return_val_if_fail (device != NULL && *device == NULL, FALSE); + g_return_val_if_fail (spec_object != NULL && *spec_object == NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + + if (strcmp (con_type, NM_SETTING_VPN_SETTING_NAME) == 0) { + /* VPN connections */ + NMActiveConnection *active = NULL; + if (iface) { + *device = nm_client_get_device_by_iface (nmc->client, iface); + if (*device) + active = nm_device_get_active_connection (*device); + + if (!active) { + g_set_error (error, NMCLI_ERROR, 0, _("no active connection on device '%s'"), iface); + return FALSE; + } + *spec_object = nm_object_get_path (NM_OBJECT (active)); + return TRUE; + } else { + active = get_default_active_connection (nmc, device); + if (!active) { + g_set_error_literal (error, NMCLI_ERROR, 0, _("no active connection or device")); + return FALSE; + } + *spec_object = nm_object_get_path (NM_OBJECT (active)); + return TRUE; + } + } else { + /* Other connections */ + NMDevice *found_device = NULL; + const GPtrArray *devices = nm_client_get_devices (nmc->client); + + for (i = 0; i < devices->len && !found_device; i++) { + NMDevice *dev = g_ptr_array_index (devices, i); + + if (iface) { + const char *dev_iface = nm_device_get_iface (dev); + if ( !g_strcmp0 (dev_iface, iface) + && nm_device_connection_compatible (dev, connection, NULL)) { + found_device = dev; + } + } else { + if (nm_device_connection_compatible (dev, connection, NULL)) { + found_device = dev; + } + } + + if (found_device && ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) { + char *bssid_up = g_ascii_strup (ap, -1); + const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev)); + found_device = NULL; /* Mark as not found; set to the device again later, only if AP matches */ + + for (j = 0; j < aps->len; j++) { + NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j); + const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); + + if (!strcmp (bssid_up, candidate_bssid)) { + found_device = dev; + *spec_object = nm_object_get_path (NM_OBJECT (candidate_ap)); + break; + } + } + g_free (bssid_up); + } + +#if WITH_WIMAX + if ( found_device + && nsp + && !strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME) + && NM_IS_DEVICE_WIMAX (dev)) { + const GPtrArray *nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (dev)); + found_device = NULL; /* Mark as not found; set to the device again later, only if NSP matches */ + + for (j = 0; j < nsps->len; j++) { + NMWimaxNsp *candidate_nsp = g_ptr_array_index (nsps, j); + const char *candidate_name = nm_wimax_nsp_get_name (candidate_nsp); + + if (!strcmp (nsp, candidate_name)) { + found_device = dev; + *spec_object = nm_object_get_path (NM_OBJECT (candidate_nsp)); + break; + } + } + } +#endif + } + + if (found_device) { + *device = found_device; + return TRUE; + } else { + if (iface) + g_set_error (error, NMCLI_ERROR, 0, _("device '%s' not compatible with connection '%s'"), + iface, nm_setting_connection_get_id (s_con)); + else + g_set_error (error, NMCLI_ERROR, 0, _("no device found for connection '%s'"), + nm_setting_connection_get_id (s_con)); + return FALSE; + } + } +} + +static const char * +vpn_connection_state_reason_to_string (NMVpnConnectionStateReason reason) +{ + switch (reason) { + case NM_VPN_CONNECTION_STATE_REASON_UNKNOWN: + return _("unknown reason"); + case NM_VPN_CONNECTION_STATE_REASON_NONE: + return _("none"); + case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED: + return _("the user was disconnected"); + case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED: + return _("the base network connection was interrupted"); + case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED: + return _("the VPN service stopped unexpectedly"); + case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID: + return _("the VPN service returned invalid configuration"); + case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT: + return _("the connection attempt timed out"); + case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT: + return _("the VPN service did not start in time"); + case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED: + return _("the VPN service failed to start"); + case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS: + return _("no valid VPN secrets"); + case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED: + return _("invalid VPN secrets"); + case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED: + return _("the connection was removed"); + default: + return _("unknown"); + } +} + +static void +device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + NMActiveConnection *active; + NMDeviceState state; + NMActiveConnectionState ac_state; + + active = nm_device_get_active_connection (device); + state = nm_device_get_state (device); + + ac_state = active ? nm_active_connection_get_state (active) : NM_ACTIVE_CONNECTION_STATE_UNKNOWN; + + if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("Connection successfully activated (D-Bus active path: %s)\n"), + nm_object_get_path (NM_OBJECT (active))); + quit (); + } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING + && state == NM_DEVICE_STATE_IP_CONFIG) { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("Connection successfully activated (master waiting for slaves) (D-Bus active path: %s)\n"), + nm_object_get_path (NM_OBJECT (active))); + quit (); + } else if (active && ac_state != NM_ACTIVE_CONNECTION_STATE_ACTIVATING) { + g_string_printf (nmc->return_text, _("Error: Connection activation failed.")); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + quit (); + } +} + +static void +active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + NMActiveConnectionState state; + + state = nm_active_connection_get_state (active); + + if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("Connection successfully activated (D-Bus active path: %s)\n"), + nm_object_get_path (NM_OBJECT (active))); + g_object_unref (active); + quit (); + } else if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) { + g_string_printf (nmc->return_text, _("Error: Connection activation failed.")); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + g_object_unref (active); + quit (); + } else if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) { + /* activating master connection does not automatically activate any slaves, so their + * active connection state will not progress beyond ACTIVATING state. + * Monitor the device instead. */ + const GPtrArray *devices; + NMDevice *device; + + if (nmc->secret_agent) { + NMRemoteConnection *connection = nm_active_connection_get_connection (active); + + nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent), + nm_connection_get_path (NM_CONNECTION (connection))); + } + + devices = nm_active_connection_get_devices (active); + device = devices->len ? g_ptr_array_index (devices, 0) : NULL; + if ( device + && ( NM_IS_DEVICE_BOND (device) + || NM_IS_DEVICE_TEAM (device) + || NM_IS_DEVICE_BRIDGE (device))) { + g_signal_handlers_disconnect_by_func (active, G_CALLBACK (active_connection_state_cb), nmc); + g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state_cb), nmc); + + device_state_cb (device, NULL, nmc); + } + } +} + +static void +vpn_connection_state_cb (NMVpnConnection *vpn, + NMVpnConnectionState state, + NMVpnConnectionStateReason reason, + gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + + switch (state) { + case NM_VPN_CONNECTION_STATE_PREPARE: + case NM_VPN_CONNECTION_STATE_NEED_AUTH: + case NM_VPN_CONNECTION_STATE_CONNECT: + case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: + /* no operation */ + break; + + case NM_VPN_CONNECTION_STATE_ACTIVATED: + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("VPN connection successfully activated (D-Bus active path: %s)\n"), + nm_object_get_path (NM_OBJECT (vpn))); + g_object_unref (vpn); + quit (); + break; + + case NM_VPN_CONNECTION_STATE_FAILED: + case NM_VPN_CONNECTION_STATE_DISCONNECTED: + g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."), + vpn_connection_state_reason_to_string (reason)); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + g_object_unref (vpn); + quit (); + break; + + default: + break; + } +} + +static gboolean +timeout_cb (gpointer user_data) +{ + /* Time expired -> exit nmcli */ + + NmCli *nmc = (NmCli *) user_data; + + g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout); + nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED; + quit (); + return FALSE; +} + +static gboolean +progress_cb (gpointer user_data) +{ + const char *str = (const char *) user_data; + + nmc_terminal_show_progress (str); + + return TRUE; +} + +static gboolean +progress_device_cb (gpointer user_data) +{ + NMDevice *device = (NMDevice *) user_data; + + nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : ""); + + return TRUE; +} + +static gboolean +progress_vpn_cb (gpointer user_data) +{ + NMVpnConnection *vpn = (NMVpnConnection *) user_data; + const char *str; + + str = NM_IS_VPN_CONNECTION (vpn) ? + vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) : + ""; + + nmc_terminal_show_progress (str); + + return TRUE; +} + +typedef struct { + NmCli *nmc; + NMDevice *device; +} ActivateConnectionInfo; + +static void +activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_data) +{ + ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data; + NmCli *nmc = info->nmc; + NMDevice *device = info->device; + NMActiveConnection *active; + NMActiveConnectionState state; + const GPtrArray *ac_devs; + GError *error = NULL; + + active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error); + + if (error) { + g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"), + error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + quit (); + } else { + state = nm_active_connection_get_state (active); + if (!device) { + /* device could be NULL for virtual devices. Fill it here. */ + ac_devs = nm_active_connection_get_devices (active); + info->device = device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL; + } + + if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + /* User doesn't want to wait or already activated */ + if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("Connection successfully activated (D-Bus active path: %s)\n"), + nm_object_get_path (NM_OBJECT (active))); + } + g_object_unref (active); + quit (); + } else { + if (NM_IS_VPN_CONNECTION (active)) { + /* Monitor VPN state */ + g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc); + + /* Start progress indication showing VPN states */ + if (nmc->print_output == NMC_PRINT_PRETTY) { + if (progress_id) + g_source_remove (progress_id); + progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active)); + } + } else { + g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc); + active_connection_state_cb (active, NULL, nmc); + + /* Start progress indication showing device states */ + if (nmc->print_output == NMC_PRINT_PRETTY) { + if (progress_id) + g_source_remove (progress_id); + progress_id = g_timeout_add (120, progress_device_cb, device); + } + } + + /* Start timer not to loop forever when signals are not emitted */ + g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); + } + } + g_free (info); +} + +/** + * parse_passwords: + * @passwd_file: file with passwords to parse + * @error: location to store error, or %NULL + * + * Parse passwords given in @passwd_file and insert them into a hash table. + * Example of @passwd_file contents: + * wifi.psk:tajne heslo + * 802-1x.password:krakonos + * 802-11-wireless-security:leap-password:my leap password + * + * Returns: hash table with parsed passwords, or %NULL on an error + */ +static GHashTable * +parse_passwords (const char *passwd_file, GError **error) +{ + GHashTable *pwds_hash; + char *contents = NULL; + gsize len = 0; + GError *local_err = NULL; + char **lines, **iter; + char *pwd_spec, *pwd, *prop; + const char *setting; + + pwds_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + if (!passwd_file) + return pwds_hash; + + /* Read the passwords file */ + if (!g_file_get_contents (passwd_file, &contents, &len, &local_err)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("failed to read passwd-file '%s': %s"), + passwd_file, local_err->message); + g_error_free (local_err); + g_hash_table_destroy (pwds_hash); + return NULL; + } + + lines = nmc_strsplit_set (contents, "\r\n", -1); + for (iter = lines; *iter; iter++) { + pwd = strchr (*iter, ':'); + if (!pwd) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("missing colon in 'password' entry '%s'"), *iter); + goto failure; + } + *(pwd++) = '\0'; + + prop = strchr (*iter, '.'); + if (!prop) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("missing dot in 'password' entry '%s'"), *iter); + goto failure; + } + *(prop++) = '\0'; + + setting = *iter; + while (g_ascii_isspace (*setting)) + setting++; + /* Accept wifi-sec or wifi instead of cumbersome '802-11-wireless-security' */ + if (!strcmp (setting, "wifi-sec") || !strcmp (setting, "wifi")) + setting = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME; + if (nm_setting_lookup_type (setting) == G_TYPE_INVALID) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid setting name in 'password' entry '%s'"), setting); + goto failure; + } + + pwd_spec = g_strdup_printf ("%s.%s", setting, prop); + g_hash_table_insert (pwds_hash, pwd_spec, g_strdup (pwd)); + } + g_strfreev (lines); + g_free (contents); + return pwds_hash; + +failure: + g_strfreev (lines); + g_free (contents); + g_hash_table_destroy (pwds_hash); + return NULL; +} + + + +static gboolean +nmc_activate_connection (NmCli *nmc, + NMConnection *connection, + const char *ifname, + const char *ap, + const char *nsp, + const char *pwds, + GAsyncReadyCallback callback, + GError **error) +{ + ActivateConnectionInfo *info; + + GHashTable *pwds_hash; + NMDevice *device = NULL; + const char *spec_object = NULL; + gboolean device_found; + GError *local = NULL; + + g_return_val_if_fail (nmc != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (connection) { + device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &local); + + /* Virtual connection may not have their interfaces created yet */ + if (!device_found && !nm_connection_is_virtual (connection)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION, + "%s", local && local->message ? local->message : _("unknown error")); + g_clear_error (&local); + return FALSE; + } + g_clear_error (&local); + } else if (ifname) { + device = nm_client_get_device_by_iface (nmc->client, ifname); + if (!device) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, + _("unknown device '%s'."), ifname); + return FALSE; + } + } else { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, + _("neither a valid connection nor device given")); + return FALSE; + } + + /* Parse passwords given in passwords file */ + pwds_hash = parse_passwords (pwds, &local); + if (local) { + g_propagate_error (error, local); + return FALSE; + } + if (nmc->pwds_hash) + g_hash_table_destroy (nmc->pwds_hash); + nmc->pwds_hash = pwds_hash; + + /* Create secret agent */ + nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-connect"); + if (nmc->secret_agent) { + g_signal_connect (nmc->secret_agent, "request-secrets", G_CALLBACK (nmc_secrets_requested), nmc); + if (connection) { + nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent), + nm_object_get_path (NM_OBJECT (connection))); + } + } + + info = g_malloc0 (sizeof (ActivateConnectionInfo)); + info->nmc = nmc; + info->device = device; + + nm_client_activate_connection_async (nmc->client, + connection, + device, + spec_object, + NULL, + callback, + info); + return TRUE; +} + +static NMCResultCode +do_connection_up (NmCli *nmc, int argc, char **argv) +{ + NMConnection *connection = NULL; + const char *ifname = NULL; + const char *ap = NULL; + const char *nsp = NULL; + const char *pwds = NULL; + GError *error = NULL; + const char *selector = NULL; + const char *name = NULL; + char *line = NULL; + + /* + * Set default timeout for connection activation. + * Activation can take quite a long time, use 90 seconds. + */ + if (nmc->timeout == -1) + nmc->timeout = 90; + + if (argc == 0) { + if (nmc->ask) { + line = nmc_readline (PROMPT_CONNECTION); + name = line ? line : ""; + } + } else if (strcmp (*argv, "ifname") != 0) { + if ( strcmp (*argv, "id") == 0 + || strcmp (*argv, "uuid") == 0 + || strcmp (*argv, "path") == 0) { + + selector = *argv; + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + name = *argv; + } + name = *argv; + next_arg (&argc, &argv); + } + + if (name) { + connection = nmc_find_connection (nmc->connections, selector, name, NULL); + if (!connection) { + g_string_printf (nmc->return_text, _("Error: Connection '%s' does not exist."), name); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + } + + while (argc > 0) { + if (strcmp (*argv, "ifname") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + ifname = *argv; + } + else if (strcmp (*argv, "ap") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + ap = *argv; + } +#if WITH_WIMAX + else if (strcmp (*argv, "nsp") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + nsp = *argv; + } +#endif + else if (strcmp (*argv, "passwd-file") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + pwds = *argv; + } + else { + g_printerr (_("Unknown parameter: %s\n"), *argv); + } + + argc--; + argv++; + } + + /* Use nowait_flag instead of should_wait because exiting has to be postponed till + * active_connection_state_cb() is called. That gives NM time to check our permissions + * and we can follow activation progress. + */ + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + + if (!nmc_activate_connection (nmc, connection, ifname, ap, nsp, pwds, activate_connection_cb, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), + error ? error->message : _("unknown error")); + nmc->return_value = error ? error->code : NMC_RESULT_ERROR_CON_ACTIVATION; + g_clear_error (&error); + goto error; + } + + /* Start progress indication */ + if (nmc->print_output == NMC_PRINT_PRETTY) + progress_id = g_timeout_add (120, progress_cb, _("preparing")); + + g_free (line); + return nmc->return_value; +error: + nmc->should_wait = FALSE; + g_free (line); + return nmc->return_value; +} + +typedef struct { + NmCli *nmc; + GSList *queue; + guint timeout_id; +} DeactivateConnectionInfo; + +static void deactivate_connection_info_finish (DeactivateConnectionInfo *info, + NMActiveConnection *active); + +static gboolean +down_timeout_cb (gpointer user_data) +{ + DeactivateConnectionInfo *info = user_data; + + timeout_cb (info->nmc); + deactivate_connection_info_finish (info, NULL); + return G_SOURCE_REMOVE; +} + +static void +down_active_connection_state_cb (NMActiveConnection *active, + GParamSpec *pspec, + DeactivateConnectionInfo *info) +{ + if (nm_active_connection_get_state (active) < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) + return; + + if (info->nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("Connection '%s' successfully deactivated (D-Bus active path: %s)\n"), + nm_active_connection_get_id (active), nm_object_get_path (NM_OBJECT (active))); + + deactivate_connection_info_finish (info, active); +} + +static void +destroy_queue_element (gpointer data) +{ + g_signal_handlers_disconnect_matched (data, G_SIGNAL_MATCH_FUNC, 0, 0, 0, + down_active_connection_state_cb, NULL); + g_object_unref (data); +} + +static void +deactivate_connection_info_finish (DeactivateConnectionInfo *info, + NMActiveConnection *active) +{ + if (active) { + info->queue = g_slist_remove (info->queue, active); + g_signal_handlers_disconnect_by_func (active, + down_active_connection_state_cb, + info); + g_object_unref (active); + } else { + g_slist_free_full (info->queue, destroy_queue_element); + info->queue = NULL; + } + + if (info->queue) + return; + + if (info->timeout_id) + g_source_remove (info->timeout_id); + g_slice_free (DeactivateConnectionInfo, info); + quit (); +} + +static NMCResultCode +do_connection_down (NmCli *nmc, int argc, char **argv) +{ + NMActiveConnection *active; + DeactivateConnectionInfo *info = NULL; + const GPtrArray *active_cons; + GSList *queue = NULL, *iter; + char *line = NULL; + char **arg_arr = NULL; + char **arg_ptr = argv; + int arg_num = argc; + int idx = 0; + + if (argc == 0) { + if (nmc->ask) { + line = nmc_readline (PROMPT_CONNECTION); + nmc_string_to_arg_array (line, "", &arg_arr, &arg_num); + arg_ptr = arg_arr; + } + if (arg_num == 0) { + g_string_printf (nmc->return_text, _("Error: No connection specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + + if (nmc->timeout == -1) + nmc->timeout = 10; + + /* Get active connections */ + active_cons = nm_client_get_active_connections (nmc->client); + while (arg_num > 0) { + const char *selector = NULL; + + if ( strcmp (*arg_ptr, "id") == 0 + || strcmp (*arg_ptr, "uuid") == 0 + || strcmp (*arg_ptr, "path") == 0 + || strcmp (*arg_ptr, "apath") == 0) { + + selector = *arg_ptr; + if (next_arg (&arg_num, &arg_ptr) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + + active = find_active_connection (active_cons, nmc->connections, selector, *arg_ptr, &idx); + if (active) + queue = g_slist_prepend (queue, active); + else { + g_printerr (_("Error: '%s' is not an active connection.\n"), *arg_ptr); + g_string_printf (nmc->return_text, _("Error: not all active connections found.")); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + } + + if (idx == 0) + next_arg (&arg_num, &arg_ptr); + } + + if (!queue) { + g_string_printf (nmc->return_text, _("Error: no active connection provided.")); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + queue = g_slist_reverse (queue); + + if (nmc->timeout > 0) { + nmc->should_wait = TRUE; + + info = g_slice_new0 (DeactivateConnectionInfo); + info->nmc = nmc; + info->timeout_id = g_timeout_add_seconds (nmc->timeout, down_timeout_cb, info); + } + + for (iter = queue; iter; iter = g_slist_next (iter)) { + active = iter->data; + + if (info) { + info->queue = g_slist_prepend (info->queue, g_object_ref (active)); + g_signal_connect (active, + "notify::" NM_ACTIVE_CONNECTION_STATE, + G_CALLBACK (down_active_connection_state_cb), + info); + } + + /* Now deactivate the connection */ + nm_client_deactivate_connection (nmc->client, active, NULL, NULL); + } + +error: + g_strfreev (arg_arr); + g_slist_free (queue); + return nmc->return_value; +} + +/*----------------------------------------------------------------------------*/ + +typedef struct NameItem { + const char *name; + const char *alias; + const struct NameItem *settings; + gboolean mandatory; +} NameItem; + +static const NameItem nmc_generic_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_ethernet_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_DCB_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_infiniband_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_wifi_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", NULL, TRUE }, + { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-sec", NULL, FALSE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_wimax_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIMAX_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_gsm_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_GSM_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_cdma_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_CDMA_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_bluetooth_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_adsl_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_ADSL_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +/* PPPoE is a base connection type from historical reasons. + * See libnm-core/nm-setting.c:_nm_setting_is_base_type() + */ +static const NameItem nmc_pppoe_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, + { NM_SETTING_PPPOE_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_olpc_mesh_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_vpn_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_VPN_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_vlan_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, + { NM_SETTING_VLAN_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_bond_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_BOND_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_team_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_TEAM_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_bridge_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_BRIDGE_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_bond_slave_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_team_slave_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, + { NM_SETTING_TEAM_PORT_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_bridge_slave_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_BRIDGE_PORT_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + + +/* Available connection types */ +static const NameItem nmc_valid_connection_types[] = { + { NM_SETTING_GENERIC_SETTING_NAME, NULL, nmc_generic_settings }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", nmc_ethernet_settings }, + { NM_SETTING_PPPOE_SETTING_NAME, NULL, nmc_pppoe_settings }, + { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", nmc_wifi_settings }, + { NM_SETTING_WIMAX_SETTING_NAME, NULL, nmc_wimax_settings }, + { NM_SETTING_GSM_SETTING_NAME, NULL, nmc_gsm_settings }, + { NM_SETTING_CDMA_SETTING_NAME, NULL, nmc_cdma_settings }, + { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, nmc_infiniband_settings }, + { NM_SETTING_ADSL_SETTING_NAME, NULL, nmc_adsl_settings }, + { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, nmc_bluetooth_settings }, + { NM_SETTING_VPN_SETTING_NAME, NULL, nmc_vpn_settings }, + { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", nmc_olpc_mesh_settings }, + { NM_SETTING_VLAN_SETTING_NAME, NULL, nmc_vlan_settings }, + { NM_SETTING_BOND_SETTING_NAME, NULL, nmc_bond_settings }, + { NM_SETTING_TEAM_SETTING_NAME, NULL, nmc_team_settings }, + { NM_SETTING_BRIDGE_SETTING_NAME, NULL, nmc_bridge_settings }, + { "bond-slave", NULL, nmc_bond_slave_settings }, + { "team-slave", NULL, nmc_team_slave_settings }, + { "bridge-slave", NULL, nmc_bridge_slave_settings }, + { NULL, NULL, NULL } +}; + +/* + * Return an alias for the 'name' if exists, else return the 'name'. + * The returned string must not be freed. + */ +static const char * +get_name_alias (const char *name, const NameItem array[]) +{ + const NameItem *iter = &array[0]; + + if (!name) + return NULL; + + while (iter && iter->name) { + if (!strcmp (name, iter->name)) { + if (iter->alias) + return iter->alias; + else + return iter->name; + } + iter++; + } + return name; +} + +/* + * Construct a string with names and aliases from the array formatted as: + * "name (alias), name, name (alias), name, name" + * + * Returns: string; the caller is responsible for freeing it. + */ +static char * +get_valid_options_string (const NameItem array[]) +{ + const NameItem *iter = &array[0]; + GString *str; + + str = g_string_sized_new (150); + while (iter && iter->name) { + if (str->len) + g_string_append (str, ", "); + if (iter->alias) + g_string_append_printf (str, "%s (%s)", iter->name, iter->alias); + else + g_string_append (str, iter->name); + iter++; + } + return g_string_free (str, FALSE); +} + +/* + * Check if 'val' is valid string in either array->name or array->alias. + * It accepts shorter string provided they are not ambiguous. + * 'val' == NULL doesn't hurt. + * + * Returns: pointer to array->name string or NULL on failure. + * The returned string must not be freed. + */ +static const char * +check_valid_name (const char *val, const NameItem array[], GError **error) +{ + const NameItem *iter; + GPtrArray *tmp_arr; + const char *str; + GError *tmp_err = NULL; + + /* Create a temporary array that can be used in nmc_string_is_valid() */ + tmp_arr = g_ptr_array_sized_new (30); + iter = &array[0]; + while (iter && iter->name) { + g_ptr_array_add (tmp_arr, (gpointer) iter->name); + if (iter->alias) + g_ptr_array_add (tmp_arr, (gpointer) iter->alias); + iter++; + } + g_ptr_array_add (tmp_arr, (gpointer) NULL); + + /* Check string validity */ + str = nmc_string_is_valid (val, (const char **) tmp_arr->pdata, &tmp_err); + if (!str) { + if (tmp_err->code == 1) + g_propagate_error (error, tmp_err); + else { + /* We want to handle aliases, so construct own error message */ + char *err_str = get_valid_options_string (array); + g_set_error (error, 1, 0, _("'%s' not among [%s]"), + val ? val : "", err_str); + g_free (err_str); + g_clear_error (&tmp_err); + } + g_ptr_array_free (tmp_arr, TRUE); + return NULL; + } + + /* Return a pointer to the found string in passed 'array' */ + iter = &array[0]; + while (iter && iter->name) { + if ( (iter->name && g_strcmp0 (iter->name, str) == 0) + || (iter->alias && g_strcmp0 (iter->alias, str) == 0)) { + g_ptr_array_free (tmp_arr, TRUE); + return iter->name; + } + iter++; + } + /* We should not really come here */ + g_ptr_array_free (tmp_arr, TRUE); + g_set_error (error, 1, 0, _("Unknown error")); + return NULL; +} + +static const NameItem * +get_valid_settings_array (const char *con_type) +{ + guint i, num; + + if (!con_type) + return NULL; + + num = G_N_ELEMENTS (nmc_valid_connection_types); + for (i = 0; i < num; i++) { + if (!g_strcmp0 (con_type, nmc_valid_connection_types[i].name)) + return nmc_valid_connection_types[i].settings; + } + return NULL; +} + +static gboolean +is_setting_mandatory (NMConnection *connection, NMSetting *setting) +{ + NMSettingConnection *s_con; + const char *c_type; + const NameItem *item; + const char *name; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + c_type = nm_setting_connection_get_connection_type (s_con); + + name = nm_setting_get_name (setting); + + item = get_valid_settings_array (c_type); + while (item && item->name) { + if (!strcmp (name, item->name)) + return item->mandatory; + item++; + } + return FALSE; +} + +/*----------------------------------------------------------------------------*/ + +static gboolean +check_mac (const char *mac, + int type, + const char *keyword, + GError **error) +{ + g_return_val_if_fail (type == ARPHRD_ETHER || type == ARPHRD_INFINIBAND, FALSE); + + if (!mac) + return TRUE; + + if (!nm_utils_hwaddr_valid (mac, nm_utils_hwaddr_len (type))) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s': '%s' is not a valid %s MAC address."), + keyword, mac, type == ARPHRD_INFINIBAND ? _("InfiniBand") : _("Ethernet")); + return FALSE; + } + + return TRUE; +} + +static gboolean +check_and_convert_mtu (const char *mtu, guint32 *mtu_int, GError **error) +{ + unsigned long local_mtu_int; + + if (mtu_int) + *mtu_int = 0; + + if (!mtu) + return TRUE; + + if (!nmc_string_to_uint (mtu, TRUE, 0, G_MAXUINT32, &local_mtu_int)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'mtu': '%s' is not a valid MTU."), mtu); + return FALSE; + } + if (mtu_int) + *mtu_int = (guint32) local_mtu_int; + return TRUE; +} + +static gboolean +check_infiniband_parent (const char *parent, GError **error) +{ + if (!parent) + return TRUE; + + if (!nm_utils_iface_valid_name (parent)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'parent': '%s' is not a valid interface name."), parent); + return FALSE; + } + return TRUE; +} + + +static gboolean +check_infiniband_p_key (const char *p_key, guint32 *p_key_int, GError **error) +{ + unsigned long local_p_key_int; + gboolean p_key_valid = FALSE; + if (!p_key) + return TRUE; + + if (!strncmp (p_key, "0x", 2)) + p_key_valid = nmc_string_to_uint_base (p_key + 2, 16, TRUE, 0, G_MAXUINT16, &local_p_key_int); + else + p_key_valid = nmc_string_to_uint (p_key, TRUE, 0, G_MAXUINT16, &local_p_key_int); + if (!p_key_valid) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'p-key': '%s' is not a valid InfiniBand P_KEY."), p_key); + return FALSE; + } + if (p_key_int) + *p_key_int = (guint32) local_p_key_int; + return TRUE; +} + +/** + * check_valid_enumeration: + * @str: string to check against string array @strings + * @strings: string array to check @str againt + * @what: what parameter @str belongs to (used in error message) + * @what_desc: longer description of @what parameter (used in error message) + * @error: location to store an error, or %NULL + * + * Check whether @str is one of the string of @strings array. It accepts + * shortcuts and normalizes them (@str argument is modified on success). + * + * Returns: %TRUE on success, %FALSE on failure + */ +static gboolean +check_valid_enumeration (char **str, + const char *strings[], + const char *what, + const char *what_desc, + GError **error) +{ + char *tmp; + const char *checked_str; + + if (!str || !*str) + return TRUE; + + tmp = g_strstrip (g_strdup (*str)); + checked_str = nmc_string_is_valid (tmp, strings, NULL); + g_free (tmp); + if (checked_str) { + g_free (*str); + *str = g_strdup (checked_str); + } else { + char *options; + + options = nmc_util_strv_for_display (strings); + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s': '%s' is not a valid %s %s."), + what, *str, what_desc, options); + g_free (options); + } + return !!checked_str; +} + +/* Checks Wi-Fi mode. */ +static gboolean +check_wifi_mode (char **mode, GError **error) +{ + const char *modes[] = { "infrastructure", "ap", "adhoc", NULL }; + + return check_valid_enumeration (mode, modes, "mode", _("Wi-Fi mode"), error); +} + +/* Checks InfiniBand mode. */ +static gboolean +check_infiniband_mode (char **mode, GError **error) +{ + const char *modes[] = { "datagram", "connected", NULL }; + + return check_valid_enumeration (mode, modes, "mode", _("InfiniBand transport mode"), error); +} + +static gboolean +check_and_convert_vlan_flags (const char *flags, guint32 *flags_int, GError **error) +{ + unsigned long local_flags_int; + + if (!flags) + return TRUE; + + if (!nmc_string_to_uint (flags, TRUE, 0, 7, &local_flags_int)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'flags': '%s' is not valid; use <0-7>."), flags); + return FALSE; + } + if (flags_int) + *flags_int = (guint32) local_flags_int; + return TRUE; +} + +static gboolean +check_and_convert_vlan_prio_maps (const char *prio_map, + NMVlanPriorityMap type, + char ***prio_map_arr, + GError **error) +{ + char **local_prio_map_arr; + GError *local_err = NULL; + + if (!prio_map) + return TRUE; + + if (!(local_prio_map_arr = nmc_vlan_parse_priority_maps (prio_map, type, &local_err))) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s': '%s' is not valid; %s "), + type == NM_VLAN_INGRESS_MAP ? "ingress" : "egress", + prio_map, local_err->message); + return FALSE; + } + + if (prio_map_arr) + *prio_map_arr = local_prio_map_arr; + return TRUE; +} + +static gboolean +add_ip4_address_to_connection (NMIPAddress *ip4addr, NMConnection *connection) +{ + NMSettingIPConfig *s_ip4; + gboolean ret; + + if (!ip4addr) + return TRUE; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (!s_ip4) { + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + } + ret = nm_setting_ip_config_add_address (s_ip4, ip4addr); + nm_ip_address_unref (ip4addr); + + return ret; +} + +static gboolean +add_ip6_address_to_connection (NMIPAddress *ip6addr, NMConnection *connection) +{ + NMSettingIPConfig *s_ip6; + gboolean ret; + + if (!ip6addr) + return TRUE; + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (!s_ip6) { + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NULL); + } + ret = nm_setting_ip_config_add_address (s_ip6, ip6addr); + nm_ip_address_unref (ip6addr); + + return ret; +} + +static char * +unique_master_iface_ifname (const GPtrArray *connections, + const char *try_name) +{ + NMConnection *connection; + char *new_name; + unsigned int num = 1; + int i = 0; + const char *ifname = NULL; + + new_name = g_strdup (try_name); + while (i < connections->len) { + connection = NM_CONNECTION (connections->pdata[i]); + ifname = nm_connection_get_interface_name (connection); + if (g_strcmp0 (new_name, ifname) == 0) { + g_free (new_name); + new_name = g_strdup_printf ("%s%d", try_name, num++); + i = 0; + } else + i++; + } + return new_name; +} + +static const char * +_strip_master_prefix (const char *master, const char *(**func)(NMConnection *)) +{ + if (!master) + return NULL; + + if (g_str_has_prefix (master, "ifname/")) { + master = master + strlen ("ifname/"); + if (func) + *func = nm_connection_get_interface_name; + } else if (g_str_has_prefix (master, "uuid/")) { + master = master + strlen ("uuid/"); + if (func) + *func = nm_connection_get_uuid; + } else if (g_str_has_prefix (master, "id/")) { + master = master + strlen ("id/"); + if (func) + *func = nm_connection_get_id; + } + return master; +} + +/* verify_master_for_slave: + * @connections: list af all connections + * @master: UUID, ifname or ID of the master connection + * @type: virtual connection type (bond, team, bridge, ...) + * + * Check whether master is a valid interface name, UUID or ID of some @type connection. + * First UUID and ifname are checked. If they don't match, ID is checked + * and replaced by UUID on a match. + * + * Returns: identifier of master connection if found, %NULL otherwise + */ +static const char * +verify_master_for_slave (const GPtrArray *connections, + const char *master, + const char *type) +{ + NMConnection *connection; + NMSettingConnection *s_con; + const char *con_type, *id, *uuid, *ifname; + int i; + const char *found_by_id = NULL; + const char *out_master = NULL; + const char *(*func) (NMConnection *) = NULL; + + if (!master) + return NULL; + + master = _strip_master_prefix (master, &func); + for (i = 0; i < connections->len; i++) { + connection = NM_CONNECTION (connections->pdata[i]); + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + if (g_strcmp0 (con_type, type) != 0) + continue; + if (func) { + /* There was a prefix; only compare to that type. */ + if (g_strcmp0 (master, func (connection)) == 0) { + if (func == nm_connection_get_id) + out_master = nm_connection_get_uuid (connection); + else + out_master = master; + break; + } + } else { + id = nm_connection_get_id (connection); + uuid = nm_connection_get_uuid (connection); + ifname = nm_connection_get_interface_name (connection); + if ( g_strcmp0 (master, uuid) == 0 + || g_strcmp0 (master, ifname) == 0) { + out_master = master; + break; + } + if (!found_by_id && g_strcmp0 (master, id) == 0) + found_by_id = uuid; + } + } + return out_master ? out_master : found_by_id; +} + +static gboolean +bridge_prop_string_to_uint (const char *str, + const char *nmc_arg, + GType bridge_type, + const char *propname, + unsigned long *out_val, + GError **error) +{ + GParamSpecUInt *pspec; + + pspec = (GParamSpecUInt *) g_object_class_find_property (g_type_class_peek (bridge_type), + propname); + g_assert (G_IS_PARAM_SPEC_UINT (pspec)); + + if (!nmc_string_to_uint (str, TRUE, pspec->minimum, pspec->maximum, out_val)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s': '%s' is not valid; use <%u-%u>."), + nmc_arg, str, pspec->minimum, pspec->maximum); + return FALSE; + } + return TRUE; +} + +#define WORD_YES "yes" +#define WORD_NO "no" +#define WORD_LOC_YES _("yes") +#define WORD_LOC_NO _("no") +static const char * +prompt_yes_no (gboolean default_yes, char *delim) +{ + static char prompt[128] = { 0 }; + + if (!delim) + delim = ""; + + snprintf (prompt, sizeof (prompt), "(%s/%s) [%s]%s ", + WORD_LOC_YES, WORD_LOC_NO, + default_yes ? WORD_LOC_YES : WORD_LOC_NO, delim); + + return prompt; +} + +static gboolean +normalize_yes_no (char **yes_no) +{ + char *tmp; + const char *checked_yes_no; + const char *strv[] = { WORD_LOC_YES, WORD_LOC_NO, NULL }; + + if (!yes_no || !*yes_no) + return FALSE; + + tmp = g_strstrip (g_strdup (*yes_no)); + checked_yes_no = nmc_string_is_valid (tmp, strv, NULL); + g_free (tmp); + if (g_strcmp0 (checked_yes_no, WORD_LOC_YES) == 0) { + g_free (*yes_no); + *yes_no = g_strdup (WORD_YES); + } else if (g_strcmp0 (checked_yes_no, WORD_LOC_NO) == 0) { + g_free (*yes_no); + *yes_no = g_strdup (WORD_NO); + } + return !!checked_yes_no; +} + +static gboolean +want_provide_opt_args (const char *type, int num) +{ + char *answer; + gboolean ret = TRUE; + + /* Ask for optional arguments. */ + g_print (ngettext ("There is %d optional argument for '%s' connection type.\n", + "There are %d optional arguments for '%s' connection type.\n", num), + num, type); + answer = nmc_readline (ngettext ("Do you want to provide it? %s", + "Do you want to provide them? %s", num), + prompt_yes_no (TRUE, NULL)); + answer = answer ? g_strstrip (answer) : NULL; + if (answer && matches (answer, WORD_LOC_YES) != 0) + ret = FALSE; + g_free (answer); + return ret; +} + +static void +do_questionnaire_ethernet (gboolean ethernet, char **mtu, char **mac, char **cloned_mac) +{ + gboolean once_more; + GError *error = NULL; + + /* Ask for optional arguments */ + if (ethernet && !want_provide_opt_args (_("ethernet"), 3)) + return; + + if (!*mtu) { + do { + *mtu = nmc_readline (_("MTU [auto]: ")); + once_more = !check_and_convert_mtu (*mtu, NULL, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mtu); + } + } while (once_more); + } + if (!*mac) { + do { + *mac = nmc_readline (_("MAC [none]: ")); + once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } + if (!*cloned_mac) { + do { + *cloned_mac = nmc_readline (_("Cloned MAC [none]: ")); + once_more = !check_mac (*cloned_mac, ARPHRD_ETHER, "cloned-mac", &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*cloned_mac); + } + } while (once_more); + } +} + +#define WORD_DATAGRAM "datagram" +#define WORD_CONNECTED "connected" +#define PROMPT_IB_MODE "(" WORD_DATAGRAM "/" WORD_CONNECTED ") [" WORD_DATAGRAM "]: " +static void +do_questionnaire_infiniband (char **mtu, char **mac, char **mode, char **parent, char **p_key) +{ + gboolean once_more; + GError *error = NULL; + + /* Ask for optional arguments */ + if (!want_provide_opt_args (_("InfiniBand"), 5)) + return; + + if (!*mtu) { + do { + *mtu = nmc_readline (_("MTU [auto]: ")); + once_more = !check_and_convert_mtu (*mtu, NULL, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mtu); + } + } while (once_more); + } + if (!*mac) { + do { + *mac = nmc_readline (_("MAC [none]: ")); + once_more = !check_mac (*mac, ARPHRD_INFINIBAND, "mac", &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } + if (!*mode) { + do { + *mode = nmc_readline (_("Transport mode %s"), PROMPT_IB_MODE); + if (!*mode) + *mode = g_strdup ("datagram"); + once_more = !check_infiniband_mode (mode, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mode); + } + } while (once_more); + } + if (!*parent) { + do { + *parent = nmc_readline (_("Parent interface [none]: ")); + once_more = !check_infiniband_parent (*parent, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*parent); + } + } while (once_more); + } + if (!*p_key) { + do { + *p_key = nmc_readline (_("P_KEY [none]: ")); + once_more = !check_infiniband_p_key (*p_key, NULL, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*p_key); + } + /* If parent is specified, so has to be P_KEY */ + if (!once_more && *parent && !*p_key) { + once_more = TRUE; + g_print (_("Error: 'p-key' is mandatory when 'parent' is specified.\n")); + } + } while (once_more); + } +} + +#define WORD_INFRA "infrastructure" +#define WORD_AP "ap" +#define WORD_ADHOC "adhoc" +#define PROMPT_WIFI_MODE "(" WORD_INFRA "/" WORD_AP "/" WORD_ADHOC ") [" WORD_INFRA "]: " +static void +do_questionnaire_wifi (char **mtu, char **mac, char **cloned_mac, char **mode) +{ + gboolean once_more; + GError *error = NULL; + + /* Ask for optional arguments */ + if (!want_provide_opt_args (_("Wi-Fi"), 4)) + return; + + /* Most optional Wi-Fi arguments are the same as for ethernet. */ + do_questionnaire_ethernet (FALSE, mtu, mac, cloned_mac); + + if (!*mode) { + do { + *mode = nmc_readline (_("Mode %s"), PROMPT_WIFI_MODE); + if (!*mode) + *mode = g_strdup ("infrastructure"); + once_more = !check_wifi_mode (mode, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mode); + } + } while (once_more); + } +} + +static void +do_questionnaire_wimax (char **mac) +{ + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'wimax' arguments. */ + if (!want_provide_opt_args (_("WiMAX"), 1)) + return; + + if (!*mac) { + do { + *mac = nmc_readline (_("MAC [none]: ")); + once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } +} + +static void +do_questionnaire_pppoe (char **password, char **service, char **mtu, char **mac) +{ + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'pppoe' arguments. */ + if (!want_provide_opt_args (_("PPPoE"), 4)) + return; + + if (!*password) + *password = nmc_readline (_("Password [none]: ")); + if (!*service) + *service = nmc_readline (_("Service [none]: ")); + + if (!*mtu) { + do { + *mtu = nmc_readline (_("MTU [auto]: ")); + once_more = !check_and_convert_mtu (*mtu, NULL, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mtu); + } + } while (once_more); + } + if (!*mac) { + do { + *mac = nmc_readline (_("MAC [none]: ")); + once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } +} + +static void +do_questionnaire_mobile (char **user, char **password) +{ + /* Ask for optional 'gsm' or 'cdma' arguments. */ + if (!want_provide_opt_args (_("mobile broadband"), 2)) + return; + + if (!*user) + *user = nmc_readline (_("Username [none]: ")); + if (!*password) + *password = nmc_readline (_("Password [none]: ")); +} + +#define WORD_PANU "panu" +#define WORD_DUN_GSM "dun-gsm" +#define WORD_DUN_CDMA "dun-cdma" +#define PROMPT_BT_TYPE "(" WORD_PANU "/" WORD_DUN_GSM "/" WORD_DUN_CDMA ") [" WORD_PANU "]: " +static void +do_questionnaire_bluetooth (char **bt_type) +{ + gboolean once_more; + + /* Ask for optional 'bluetooth' arguments. */ + if (!want_provide_opt_args (_("bluetooth"), 1)) + return; + + if (!*bt_type) { + const char *types[] = { "dun", "dun-gsm", "dun-cdma", "panu", NULL }; + const char *tmp; + do { + *bt_type = nmc_readline (_("Bluetooth type %s"), PROMPT_BT_TYPE); + if (!*bt_type) + *bt_type = g_strdup ("panu"); + tmp = nmc_string_is_valid (*bt_type, types, NULL); + once_more = !tmp; + if (once_more) { + g_print (_("Error: 'bt-type': '%s' is not a valid bluetooth type.\n"), *bt_type); + g_free (*bt_type); + } + } while (once_more); + g_free (*bt_type); + *bt_type = g_strdup (tmp); + } +} + +static void +do_questionnaire_vlan (char **mtu, char **flags, char **ingress, char **egress) +{ + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'vlan' arguments. */ + if (!want_provide_opt_args (_("VLAN"), 4)) + return; + + if (!*mtu) { + do { + *mtu = nmc_readline (_("MTU [auto]: ")); + once_more = !check_and_convert_mtu (*mtu, NULL, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mtu); + } + } while (once_more); + } + if (!*flags) { + do { + *flags = nmc_readline (_("VLAN flags (<0-7>) [none]: ")); + once_more = !check_and_convert_vlan_flags (*flags, NULL, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*flags); + } + } while (once_more); + } + if (!*ingress) { + do { + *ingress = nmc_readline (_("Ingress priority maps [none]: ")); + once_more = !check_and_convert_vlan_prio_maps (*ingress, NM_VLAN_INGRESS_MAP, NULL, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*ingress); + } + } while (once_more); + } + if (!*egress) { + do { + *egress = nmc_readline (_("Egress priority maps [none]: ")); + once_more = !check_and_convert_vlan_prio_maps (*egress, NM_VLAN_EGRESS_MAP, NULL, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*egress); + } + } while (once_more); + } +} + +#define PROMPT_BOND_MODE _("Bonding mode [balance-rr]: ") +#define WORD_MIIMON "miimon" +#define WORD_ARP "arp" +#define PROMPT_BOND_MON_MODE "(" WORD_MIIMON "/" WORD_ARP ") [" WORD_MIIMON "]: " +static void +do_questionnaire_bond (char **mode, char **primary, char **miimon, + char **downdelay, char **updelay, + char **arpinterval, char **arpiptarget, + char **lacp_rate) +{ + char *monitor_mode; + unsigned long tmp; + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'bond' arguments. */ + if (!want_provide_opt_args (_("bond"), 7)) + return; + + if (!*mode) { + const char *mode_tmp; + do { + *mode = nmc_readline (PROMPT_BOND_MODE); + if (!*mode) + *mode = g_strdup ("balance-rr"); + mode_tmp = nmc_bond_validate_mode (*mode, &error); + g_free (*mode); + if (mode_tmp) { + *mode = g_strdup (mode_tmp); + } else { + g_print ("%s\n", error->message); + g_clear_error (&error); + } + } while (!mode_tmp); + } + + if (g_strcmp0 (*mode, "active-backup") == 0 && !*primary) { + do { + *primary = nmc_readline (_("Bonding primary interface [none]: ")); + once_more = *primary && !nm_utils_iface_valid_name (*primary); + if (once_more) { + g_print (_("Error: 'primary': '%s' is not a valid interface name.\n"), + *primary); + g_free (*primary); + } + } while (once_more); + } + + do { + monitor_mode = nmc_readline (_("Bonding monitoring mode %s"), PROMPT_BOND_MON_MODE); + if (!monitor_mode) + monitor_mode = g_strdup (WORD_MIIMON); + g_strstrip (monitor_mode); + once_more = matches (monitor_mode, WORD_MIIMON) != 0 && matches (monitor_mode, WORD_ARP) != 0; + if (once_more) { + g_print (_("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"), + monitor_mode, WORD_MIIMON, WORD_ARP); + g_free (monitor_mode); + } + } while (once_more); + + if (matches (monitor_mode, WORD_MIIMON) == 0) { + if (!*miimon) { + do { + *miimon = nmc_readline (_("Bonding miimon [100]: ")); + once_more = *miimon && !nmc_string_to_uint (*miimon, TRUE, 0, G_MAXUINT32, &tmp); + if (once_more) { + g_print (_("Error: 'miimon': '%s' is not a valid number <0-%u>.\n"), + *miimon, G_MAXUINT32); + g_free (*miimon); + } + } while (once_more); + } + if (!*downdelay) { + do { + *downdelay = nmc_readline (_("Bonding downdelay [0]: ")); + once_more = *downdelay && !nmc_string_to_uint (*downdelay, TRUE, 0, G_MAXUINT32, &tmp); + if (once_more) { + g_print (_("Error: 'downdelay': '%s' is not a valid number <0-%u>.\n"), + *downdelay, G_MAXUINT32); + g_free (*downdelay); + } + } while (once_more); + } + if (!*updelay) { + do { + *updelay = nmc_readline (_("Bonding updelay [0]: ")); + once_more = *updelay && !nmc_string_to_uint (*updelay, TRUE, 0, G_MAXUINT32, &tmp); + if (once_more) { + g_print (_("Error: 'updelay': '%s' is not a valid number <0-%u>.\n"), + *updelay, G_MAXUINT32); + g_free (*updelay); + } + } while (once_more); + } + } else { + if (!*arpinterval) { + do { + *arpinterval = nmc_readline (_("Bonding arp-interval [0]: ")); + once_more = *arpinterval && !nmc_string_to_uint (*arpinterval, TRUE, 0, G_MAXUINT32, &tmp); + if (once_more) { + g_print (_("Error: 'arp-interval': '%s' is not a valid number <0-%u>.\n"), + *arpinterval, G_MAXUINT32); + g_free (*arpinterval); + } + } while (once_more); + } + if (!*arpiptarget) { + //FIXME: verify the string + *arpiptarget = nmc_readline (_("Bonding arp-ip-target [none]: ")); + } + } + + if ( !*lacp_rate + && (g_strcmp0 (*mode, "802.3ad") == 0 || g_strcmp0 (*mode, "4") == 0)) { + do { + *lacp_rate = nmc_readline (_("LACP rate ('slow' or 'fast') [slow]: ")); + once_more = *lacp_rate && (strcmp (*lacp_rate, "slow") && + strcmp (*lacp_rate, "0") && + strcmp (*lacp_rate, "fast") && + strcmp (*lacp_rate, "1")); + if (once_more) { + printf (_("Error: 'lacp_rate': '%s' is invalid ('slow' or 'fast').\n"), + *lacp_rate); + g_free (*lacp_rate); + } + } while (once_more); + } + + g_free (monitor_mode); +} + +static void +do_questionnaire_team_common (const char *type_name, char **config) +{ + gboolean once_more; + char *json = NULL; + GError *error = NULL; + + /* Ask for optional arguments. */ + if (!want_provide_opt_args (type_name, 1)) + return; + + if (!*config) { + do { + *config = nmc_readline (_("Team JSON configuration [none]: ")); + once_more = !nmc_team_check_config (*config, &json, &error); + if (once_more) { + g_print ("Error: %s\n", error->message); + g_clear_error (&error); + g_free (*config); + } + } while (once_more); + } + + *config = json; +} + +/* Both team and team-slave curently have just ithe same one optional argument */ +static void +do_questionnaire_team (char **config) +{ + do_questionnaire_team_common (_("team"), config); +} + +static void +do_questionnaire_team_slave (char **config) +{ + do_questionnaire_team_common (_("team-slave"), config); +} + +static void +do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, char **hello_time, + char **max_age, char **ageing_time, char **mac) +{ + unsigned long tmp; + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'bridge' arguments. */ + if (!want_provide_opt_args (_("bridge"), 7)) + return; + + if (!*stp) { + gboolean stp_bool; + do { + *stp = nmc_readline (_("Enable STP %s"), prompt_yes_no (TRUE, ":")); + *stp = *stp ? *stp : g_strdup ("yes"); + normalize_yes_no (stp); + once_more = !nmc_string_to_bool (*stp, &stp_bool, &error); + if (once_more) { + g_print (_("Error: 'stp': %s.\n"), error->message); + g_clear_error (&error); + g_free (*stp); + } + } while (once_more); + } + if (!*priority) { + do { + *priority = nmc_readline (_("STP priority [32768]: ")); + *priority = *priority ? *priority : g_strdup ("32768"); + once_more = !nmc_string_to_uint (*priority, TRUE, 0, G_MAXUINT16, &tmp); + if (once_more) { + g_print (_("Error: 'priority': '%s' is not a valid number <0-%d>.\n"), + *priority, G_MAXUINT16); + g_free (*priority); + } + } while (once_more); + } + if (!*fwd_delay) { + do { + *fwd_delay = nmc_readline (_("Forward delay [15]: ")); + *fwd_delay = *fwd_delay ? *fwd_delay : g_strdup ("15"); + once_more = !nmc_string_to_uint (*fwd_delay, TRUE, 2, 30, &tmp); + if (once_more) { + g_print (_("Error: 'forward-delay': '%s' is not a valid number <2-30>.\n"), + *fwd_delay); + g_free (*fwd_delay); + } + } while (once_more); + } + + if (!*hello_time) { + do { + *hello_time = nmc_readline (_("Hello time [2]: ")); + *hello_time = *hello_time ? *hello_time : g_strdup ("2"); + once_more = !nmc_string_to_uint (*hello_time, TRUE, 1, 10, &tmp); + if (once_more) { + g_print (_("Error: 'hello-time': '%s' is not a valid number <1-10>.\n"), + *hello_time); + g_free (*hello_time); + } + } while (once_more); + } + if (!*max_age) { + do { + *max_age = nmc_readline (_("Max age [20]: ")); + *max_age = *max_age ? *max_age : g_strdup ("20"); + once_more = !nmc_string_to_uint (*max_age, TRUE, 6, 40, &tmp); + if (once_more) { + g_print (_("Error: 'max-age': '%s' is not a valid number <6-40>.\n"), + *max_age); + g_free (*max_age); + } + } while (once_more); + } + if (!*ageing_time) { + do { + *ageing_time = nmc_readline (_("MAC address ageing time [300]: ")); + *ageing_time = *ageing_time ? *ageing_time : g_strdup ("300"); + once_more = !nmc_string_to_uint (*ageing_time, TRUE, 0, 1000000, &tmp); + if (once_more) { + g_print (_("Error: 'ageing-time': '%s' is not a valid number <0-1000000>.\n"), + *ageing_time); + g_free (*ageing_time); + } + } while (once_more); + } + if (!*mac) { + do { + *mac = nmc_get_user_input (_("MAC [none]: ")); + once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } +} + +static void +do_questionnaire_bridge_slave (char **priority, char **path_cost, char **hairpin) +{ + unsigned long tmp; + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'bridge-slave' arguments. */ + if (!want_provide_opt_args (_("bridge-slave"), 3)) + return; + + if (!*priority) { + do { + *priority = nmc_readline (_("Bridge port priority [32]: ")); + *priority = *priority ? *priority : g_strdup ("32"); + once_more = !bridge_prop_string_to_uint (*priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT, + NM_SETTING_BRIDGE_PORT_PRIORITY, &tmp, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*priority); + } + } while (once_more); + } + if (!*path_cost) { + do { + *path_cost = nmc_readline (_("Bridge port STP path cost [100]: ")); + *path_cost = *path_cost ? *path_cost : g_strdup ("100"); + once_more = !bridge_prop_string_to_uint (*path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT, + NM_SETTING_BRIDGE_PORT_PATH_COST, &tmp, &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*path_cost); + } + } while (once_more); + } + if (!*hairpin) { + gboolean hairpin_bool; + do { + *hairpin = nmc_readline (_("Hairpin %s"), prompt_yes_no (TRUE, ":")); + *hairpin = *hairpin ? *hairpin : g_strdup ("yes"); + normalize_yes_no (hairpin); + once_more = !nmc_string_to_bool (*hairpin, &hairpin_bool, &error); + if (once_more) { + g_print (_("Error: 'hairpin': %s.\n"), error->message); + g_clear_error (&error); + g_free (*hairpin); + } + } while (once_more); + } +} + +static void +do_questionnaire_vpn (char **user) +{ + /* Ask for optional 'vpn' arguments. */ + if (!want_provide_opt_args (_("VPN"), 1)) + return; + + if (!*user) + *user = nmc_readline (_("Username [none]: ")); +} + +static void +do_questionnaire_olpc (char **channel, char **dhcp_anycast) +{ + unsigned long tmp; + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'olpc' arguments. */ + if (!want_provide_opt_args (_("OLPC Mesh"), 2)) + return; + + if (!*channel) { + do { + *channel = nmc_readline (_("OLPC Mesh channel [1]: ")); + once_more = *channel && !nmc_string_to_uint (*channel, TRUE, 1, 13, &tmp); + if (once_more) { + g_print (_("Error: 'channel': '%s' is not a valid number <1-13>.\n"), + *channel); + g_free (*channel); + } + } while (once_more); + } + if (!*dhcp_anycast) { + do { + *dhcp_anycast = nmc_readline (_("DHCP anycast MAC address [none]: ")); + once_more = !check_mac (*dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", &error); + if (once_more) { + g_print ("%s\n", error->message); + g_clear_error (&error); + g_free (*dhcp_anycast); + } + } while (once_more); + } +} + +static gboolean +split_address (char* str, char **ip, char **rest) +{ + size_t n1, n2, n3; + + *ip = *rest = NULL; + if (!str) + return FALSE; + + n1 = strspn (str, " \t"); + n2 = strcspn (str+n1, " \t\0") + n1; + n3 = strspn (str+n2, " \t") + n2; + + str[n2] = '\0'; + *ip = str[n1] ? str + n1 : NULL; + *rest = str[n3] ? str + n3 : NULL; + + return TRUE; +} + +static void +ask_for_ip_addresses (NMConnection *connection, int family) +{ + gboolean ip_loop; + GError *error = NULL; + char *str, *ip, *rest; + const char *prompt; + gboolean added; + NMIPAddress *ipaddr; + + if (family == AF_INET) + prompt =_("IPv4 address (IP[/plen]) [none]: "); + else + prompt =_("IPv6 address (IP[/plen]) [none]: "); + + ip_loop = TRUE; + do { + str = nmc_readline ("%s", prompt); + split_address (str, &ip, &rest); + if (ip) { + ipaddr = nmc_parse_and_build_address (family, ip, &error); + if (ipaddr) { + if (family == AF_INET) + added = add_ip4_address_to_connection (ipaddr, connection); + else + added = add_ip6_address_to_connection (ipaddr, connection); + if (added) + g_print (_(" Address successfully added: %s\n"), ip); + else + g_print (_(" Warning: address already present: %s\n"), ip); + if (rest) + g_print (_(" Warning: ignoring garbage at the end: '%s'\n"), rest); + } else { + g_prefix_error (&error, _("Error: ")); + g_print ("%s\n", error->message); + g_clear_error (&error); + } + } else + ip_loop = FALSE; + + g_free (str); + } while (ip_loop); +} + +static void +maybe_ask_for_gateway (NMConnection *connection, int family) +{ + gboolean gw_loop; + char *str, *gw, *rest; + const char *prompt; + NMSettingIPConfig *s_ip; + + if (family == AF_INET) { + prompt =_("IPv4 gateway [none]: "); + s_ip = nm_connection_get_setting_ip4_config (connection); + } else { + prompt =_("IPv6 gateway [none]: "); + s_ip = nm_connection_get_setting_ip6_config (connection); + } + if (s_ip == NULL) + return; + if ( nm_setting_ip_config_get_num_addresses (s_ip) == 0 + || nm_setting_ip_config_get_gateway (s_ip) != NULL) + return; + + gw_loop = TRUE; + do { + str = nmc_readline ("%s", prompt); + split_address (str, &gw, &rest); + if (gw) { + if (nm_utils_ipaddr_valid (family, gw)) { + g_object_set (s_ip, + NM_SETTING_IP_CONFIG_GATEWAY, gw, + NULL); + gw_loop = FALSE; + } else + g_print (_("Error: invalid gateway address '%s'\n"), gw); + } else + gw_loop = FALSE; + g_free (str); + } while (gw_loop); +} + +static void +do_questionnaire_ip (NMConnection *connection) +{ + char *answer; + + /* Ask for IP addresses */ + answer = nmc_readline (_("Do you want to add IP addresses? %s"), prompt_yes_no (TRUE, NULL)); + answer = answer ? g_strstrip (answer) : NULL; + if (answer && matches (answer, WORD_LOC_YES) != 0) { + g_free (answer); + return; + } + g_free (answer); + + g_print (_("Press <Enter> to finish adding addresses.\n")); + + ask_for_ip_addresses (connection, AF_INET); + maybe_ask_for_gateway (connection, AF_INET); + ask_for_ip_addresses (connection, AF_INET6); + maybe_ask_for_gateway (connection, AF_INET6); +} + +static gboolean +complete_connection_by_type (NMConnection *connection, + const char *con_type, + const GPtrArray *all_connections, + gboolean ask, + int argc, + char **argv, + GError **error) +{ + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingInfiniband *s_infiniband; + NMSettingWireless *s_wifi; + NMSettingWimax *s_wimax; + NMSettingPppoe *s_pppoe; + NMSettingGsm *s_gsm; + NMSettingCdma *s_cdma; + NMSettingBluetooth *s_bt; + NMSettingVlan *s_vlan; + NMSettingBond *s_bond; + NMSettingTeam *s_team; + NMSettingTeamPort *s_team_port; + NMSettingBridge *s_bridge; + NMSettingBridgePort *s_bridge_port; + NMSettingVpn *s_vpn; + NMSettingOlpcMesh *s_olpc_mesh; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) { + /* Build up the settings required for 'ethernet' */ + gboolean success = FALSE; + const char *mtu_c = NULL; + char *mtu = NULL; + guint32 mtu_int = 0; + const char *mac_c = NULL; + char *mac = NULL; + const char *cloned_mac_c = NULL; + char *cloned_mac = NULL; + nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, + {"cloned-mac", TRUE, &cloned_mac_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + mtu = g_strdup (mtu_c); + mac = g_strdup (mac_c); + cloned_mac = g_strdup (cloned_mac_c); + if (ask) + do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac); + + if (!check_and_convert_mtu (mtu, &mtu_int, error)) + goto cleanup_wired; + if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) + goto cleanup_wired; + if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error)) + goto cleanup_wired; + + /* Add ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + if (mtu) + g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); + if (mac) + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); + if (cloned_mac) + g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_mac, NULL); + + success = TRUE; +cleanup_wired: + g_free (mtu); + g_free (mac); + g_free (cloned_mac); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) { + /* Build up the settings required for 'infiniband' */ + gboolean success = FALSE; + const char *mtu_c = NULL; + char *mtu = NULL; + guint32 mtu_int = 0; + const char *mac_c = NULL; + char *mac = NULL; + const char *mode_c = NULL; + char *mode = NULL; + const char *parent_c = NULL; + char *parent = NULL; + const char *p_key_c = NULL; + char *p_key = NULL; + guint32 p_key_int = 0; + nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, + {"transport-mode", TRUE, &mode_c, FALSE}, + {"parent", TRUE, &parent_c, FALSE}, + {"p-key", TRUE, &p_key_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + mtu = g_strdup (mtu_c); + mac = g_strdup (mac_c); + mode = g_strdup (mode_c); + parent = g_strdup (parent_c); + p_key = g_strdup (p_key_c); + if (ask) + do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key); + + if (!check_and_convert_mtu (mtu, &mtu_int, error)) + goto cleanup_ib; + if (!check_mac (mac, ARPHRD_INFINIBAND, "mac", error)) + goto cleanup_ib; + if (!check_infiniband_mode (&mode, error)) + goto cleanup_ib; + if (p_key) { + if (!check_infiniband_p_key (p_key, &p_key_int, error)) + goto cleanup_ib; + if (!check_infiniband_parent (parent, error)) + goto cleanup_ib; + } else if (parent) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'parent': not valid without 'p-key'.")); + goto cleanup_ib; + } + + /* Add 'infiniband' setting */ + s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new (); + nm_connection_add_setting (connection, NM_SETTING (s_infiniband)); + + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL); + if (mtu) + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL); + if (mac) + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL); + if (p_key) + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL); + if (parent) + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL); + + + success = TRUE; +cleanup_ib: + g_free (mtu); + g_free (mac); + g_free (mode); + g_free (parent); + g_free (p_key); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) { + /* Build up the settings required for 'wifi' */ + gboolean success = FALSE; + char *ssid_ask = NULL; + const char *ssid = NULL; + GBytes *ssid_bytes; + const char *mtu_c = NULL; + char *mtu = NULL; + guint32 mtu_int = 0; + const char *mac_c = NULL; + char *mac = NULL; + const char *cloned_mac_c = NULL; + char *cloned_mac = NULL; + const char *mode_c = NULL; + char *mode = NULL; + nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask}, + {"mtu", TRUE, &mtu_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, + {"cloned-mac", TRUE, &cloned_mac_c, FALSE}, + {"mode", TRUE, &mode_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!ssid && ask) + ssid = ssid_ask = nmc_readline (_("SSID: ")); + if (!ssid) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'ssid' is required.")); + return FALSE; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + mtu = g_strdup (mtu_c); + mac = g_strdup (mac_c); + cloned_mac = g_strdup (cloned_mac_c); + mode = g_strdup (mode_c); + if (ask) + do_questionnaire_wifi (&mtu, &mac, &cloned_mac, &mode); + + if (!check_and_convert_mtu (mtu, &mtu_int, error)) + goto cleanup_wifi; + if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) + goto cleanup_wifi; + if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error)) + goto cleanup_wifi; + if (!check_wifi_mode (&mode, error)) + goto cleanup_wifi; + + /* Add wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid_bytes = g_bytes_new (ssid, strlen (ssid)); + g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_bytes, NULL); + + if (mtu) + g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL); + if (mac) + g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL); + if (cloned_mac) + g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac, NULL); + if (mode) + g_object_set (s_wifi, NM_SETTING_WIRELESS_MODE, mode, NULL); + + g_bytes_unref (ssid_bytes); + + success = TRUE; +cleanup_wifi: + g_free (ssid_ask); + g_free (mtu); + g_free (mac); + g_free (cloned_mac); + g_free (mode); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) { + /* Build up the settings required for 'wimax' */ + gboolean success = FALSE; + const char *nsp_name = NULL; + char *nsp_name_ask = NULL; + const char *mac_c = NULL; + char *mac = NULL; + nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask}, + {"mac", TRUE, &mac_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!nsp_name && ask) + nsp_name = nsp_name_ask = nmc_readline (_("WiMAX NSP name: ")); + if (!nsp_name) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'nsp' is required.")); + goto cleanup_wimax; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + mac = g_strdup (mac_c); + if (ask) + do_questionnaire_wimax (&mac); + + if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) + goto cleanup_wimax; + + /* Add 'wimax' setting */ + s_wimax = (NMSettingWimax *) nm_setting_wimax_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wimax)); + g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL); + + if (mac) + g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac, NULL); + + success = TRUE; +cleanup_wimax: + g_free (nsp_name_ask); + g_free (mac); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_PPPOE_SETTING_NAME)) { + /* Build up the settings required for 'pppoe' */ + gboolean success = FALSE; + const char *username = NULL; + char *username_ask = NULL; + const char *password_c = NULL; + char *password = NULL; + const char *service_c = NULL; + char *service = NULL; + const char *mtu_c = NULL; + char *mtu = NULL; + guint32 mtu_int = 0; + const char *mac_c = NULL; + char *mac = NULL; + nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask}, + {"password", TRUE, &password_c, FALSE}, + {"service", TRUE, &service_c, FALSE}, + {"mtu", TRUE, &mtu_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!username && ask) + username = username_ask = nmc_readline (_("PPPoE username: ")); + if (!username) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'username' is required.")); + goto cleanup_pppoe; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + password = g_strdup (password_c); + service = g_strdup (service_c); + mtu = g_strdup (mtu_c); + mac = g_strdup (mac_c); + if (ask) + do_questionnaire_pppoe (&password, &service, &mtu, &mac); + + if (!check_and_convert_mtu (mtu, &mtu_int, error)) + goto cleanup_pppoe; + if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) + goto cleanup_pppoe; + + /* Add 'pppoe' setting */ + s_pppoe = (NMSettingPppoe *) nm_setting_pppoe_new (); + nm_connection_add_setting (connection, NM_SETTING (s_pppoe)); + g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, username, NULL); + g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, password, NULL); + g_object_set (s_pppoe, NM_SETTING_PPPOE_SERVICE, service, NULL); + + /* Add ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + if (mtu) + g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); + if (mac) + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); + + success = TRUE; +cleanup_pppoe: + g_free (username_ask); + g_free (password); + g_free (service); + g_free (mtu); + g_free (mac); + if (!success) + return FALSE; + + } else if ( !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME) + || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) { + /* Build up the settings required for 'gsm' or 'cdma' mobile broadband */ + gboolean success = FALSE; + const char *apn = NULL; + char *apn_ask = NULL; + const char *user_c = NULL; + char *user = NULL; + const char *password_c = NULL; + char *password = NULL; + gboolean is_gsm; + int i = 0; + nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */ + {NULL} }; + + is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME); + + if (is_gsm) + gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask}; + gsm_args[i++] = (nmc_arg_t) {"user", TRUE, &user_c, FALSE}; + gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE}; + gsm_args[i++] = (nmc_arg_t) {NULL}; + + if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!apn && ask && is_gsm) + apn = apn_ask = nmc_readline (_("APN: ")); + if (!apn && is_gsm) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'apn' is required.")); + goto cleanup_mobile; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + user = g_strdup (user_c); + password = g_strdup (password_c); + if (ask) + do_questionnaire_mobile (&user, &password); + + if (is_gsm) { + g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL); + + /* Add 'gsm' setting */ + s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); + nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + g_object_set (s_gsm, + NM_SETTING_GSM_NUMBER, "*99#", + NM_SETTING_GSM_APN, apn, + NM_SETTING_GSM_USERNAME, user, + NM_SETTING_GSM_PASSWORD, password, + NULL); + g_free (apn_ask); + } else { + g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL); + + /* Add 'cdma' setting */ + s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); + nm_connection_add_setting (connection, NM_SETTING (s_cdma)); + g_object_set (s_cdma, + NM_SETTING_CDMA_NUMBER, "#777", + NM_SETTING_CDMA_USERNAME, user, + NM_SETTING_CDMA_PASSWORD, password, + NULL); + } + + success = TRUE; +cleanup_mobile: + g_free (user); + g_free (password); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) { + /* Build up the settings required for 'bluetooth' */ + gboolean success = FALSE; + const char *addr = NULL; + char *addr_ask = NULL; + const char *bt_type_c = NULL; + char *bt_type = NULL; + nmc_arg_t exp_args[] = { {"addr", TRUE, &addr, !ask}, + {"bt-type", TRUE, &bt_type_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!addr && ask) + addr = addr_ask = nmc_readline (_("Bluetooth device address: ")); + if (!addr) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'addr' is required.")); + return FALSE; + } + if (!check_mac (addr, ARPHRD_ETHER, "addr", error)) + goto cleanup_bt; + + /* Also ask for all optional arguments if '--ask' is specified. */ + bt_type = g_strdup (bt_type_c); + if (ask) + do_questionnaire_bluetooth (&bt_type); + + /* Default to 'panu' if bt-type is not provided. */ + if (!bt_type) + bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU); + + /* Add 'bluetooth' setting */ + s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bt)); + + if (addr) + g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, addr, NULL); + + /* 'dun' type requires adding 'gsm' or 'cdma' setting */ + if ( !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN) + || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) { + bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN); + s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); + nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL); +// g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL; + + } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) { + bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN); + s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); + nm_connection_add_setting (connection, NM_SETTING (s_cdma)); + g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL); + + } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) { + /* no op */ + } else { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."), + bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN, + NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma"); + goto cleanup_bt; + } + g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL); + + success = TRUE; +cleanup_bt: + g_free (addr_ask); + g_free (bt_type); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) { + /* Build up the settings required for 'vlan' */ + gboolean success = FALSE; + const char *parent = NULL; + char *parent_ask = NULL; + const char *vlan_id = NULL; + char *vlan_id_ask = NULL; + unsigned long id = 0; + const char *flags_c = NULL; + char *flags = NULL; + guint32 flags_int = 0; + const char *ingress_c = NULL, *egress_c = NULL; + char *ingress = NULL, *egress = NULL; + char **ingress_arr = NULL, **egress_arr = NULL, **p; + const char *mtu_c = NULL; + char *mtu = NULL; + guint32 mtu_int; + GByteArray *addr_array = NULL; + nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask}, + {"id", TRUE, &vlan_id, !ask}, + {"flags", TRUE, &flags_c, FALSE}, + {"ingress", TRUE, &ingress_c, FALSE}, + {"egress", TRUE, &egress_c, FALSE}, + {"mtu", TRUE, &mtu_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!parent && ask) + parent = parent_ask = nmc_readline (_("VLAN parent device or connection UUID: ")); + if (!parent) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'dev' is required.")); + return FALSE; + } + if (!vlan_id && ask) + vlan_id = vlan_id_ask = nmc_readline (_("VLAN ID <0-4095>: ")); + if (!vlan_id) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'id' is required.")); + goto cleanup_vlan; + } + if (vlan_id) { + if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4095, &id)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'id': '%s' is not valid; use <0-4095>."), + vlan_id); + goto cleanup_vlan; + } + } + + if ( !(addr_array = nm_utils_hwaddr_atoba (parent, ETH_ALEN)) + && !nm_utils_is_uuid (parent) + && !nm_utils_iface_valid_name (parent)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."), + parent); + goto cleanup_vlan; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + mtu = g_strdup (mtu_c); + flags = g_strdup (flags_c); + ingress = g_strdup (ingress_c); + egress = g_strdup (egress_c); + if (ask) + do_questionnaire_vlan (&mtu, &flags, &ingress, &egress); + + if (!check_and_convert_mtu (mtu, &mtu_int, error)) + goto cleanup_vlan; + if (!check_and_convert_vlan_flags (flags, &flags_int, error)) + goto cleanup_vlan; + if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error)) + goto cleanup_vlan; + if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &egress_arr, error)) + goto cleanup_vlan; + + /* Add 'vlan' setting */ + s_vlan = (NMSettingVlan *) nm_setting_vlan_new (); + nm_connection_add_setting (connection, NM_SETTING (s_vlan)); + + /* Add 'wired' setting if necessary */ + if (mtu || addr_array) { + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + if (mtu) + g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); + if (addr_array) + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, addr_array, NULL); + } + + /* Set 'vlan' properties */ + if (!addr_array) + g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL); + + g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL); + + if (flags) + g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL); + for (p = ingress_arr; p && *p; p++) + nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p); + for (p = egress_arr; p && *p; p++) + nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p); + + success = TRUE; +cleanup_vlan: + g_free (mtu); + g_free (flags); + g_free (ingress); + g_free (egress); + if (addr_array) + g_byte_array_free (addr_array, TRUE); + g_free (parent_ask); + g_free (vlan_id_ask); + g_strfreev (ingress_arr); + g_strfreev (egress_arr); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) { + /* Build up the settings required for 'bond' */ + gboolean success = FALSE; + const char *ifname = NULL; + const char *bond_mode_c = NULL; + char *bond_mode = NULL; + const char *bond_primary_c = NULL; + char *bond_primary = NULL; + const char *bond_miimon_c = NULL; + char *bond_miimon = NULL; + const char *bond_downdelay_c = NULL; + char *bond_downdelay = NULL; + const char *bond_updelay_c = NULL; + char *bond_updelay = NULL; + const char *bond_arpinterval_c = NULL; + char *bond_arpinterval = NULL; + const char *bond_arpiptarget_c = NULL; + char *bond_arpiptarget = NULL; + const char *bond_lacp_rate_c = NULL; + char *bond_lacp_rate = NULL; + nmc_arg_t exp_args[] = { {"mode", TRUE, &bond_mode_c, FALSE}, + {"primary", TRUE, &bond_primary_c, FALSE}, + {"miimon", TRUE, &bond_miimon_c, FALSE}, + {"downdelay", TRUE, &bond_downdelay_c, FALSE}, + {"updelay", TRUE, &bond_updelay_c, FALSE}, + {"arp-interval", TRUE, &bond_arpinterval_c, FALSE}, + {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE}, + {"lacp-rate", TRUE, &bond_lacp_rate_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + bond_mode = g_strdup (bond_mode_c); + bond_primary = g_strdup (bond_primary_c); + bond_miimon = g_strdup (bond_miimon_c); + bond_downdelay = g_strdup (bond_downdelay_c); + bond_updelay = g_strdup (bond_updelay_c); + bond_arpinterval = g_strdup (bond_arpinterval_c); + bond_arpiptarget = g_strdup (bond_arpiptarget_c); + bond_lacp_rate = g_strdup (bond_lacp_rate_c); + if (ask) + do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon, + &bond_downdelay, &bond_updelay, + &bond_arpinterval, &bond_arpiptarget, + &bond_lacp_rate); + + /* Generate ifname if connection doesn't have one */ + ifname = nm_setting_connection_get_interface_name (s_con); + if (!ifname) { + char *bond_ifname = unique_master_iface_ifname (all_connections, "nm-bond"); + + g_object_set (s_con, + NM_SETTING_CONNECTION_INTERFACE_NAME, bond_ifname, + NULL); + g_free (bond_ifname); + } + + /* Add 'bond' setting */ + s_bond = (NMSettingBond *) nm_setting_bond_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bond)); + + /* Set bond options */ + if (bond_mode) { + GError *err = NULL; + const char *bm; + if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'mode': %s."), err->message); + g_clear_error (&err); + goto cleanup_bond; + } + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm); + } + if (bond_primary) { + if (!nm_utils_iface_valid_name (bond_primary)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'primary': '%s' is not a valid interface name."), + bond_primary); + goto cleanup_bond; + } + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary); + } + if (bond_miimon) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon); + if (bond_downdelay && strcmp (bond_downdelay, "0") != 0) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay); + if (bond_updelay && strcmp (bond_updelay, "0") != 0) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay); + if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval); + if (bond_arpiptarget) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget); + if (bond_lacp_rate) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_LACP_RATE, bond_lacp_rate); + + success = TRUE; +cleanup_bond: + g_free (bond_mode); + g_free (bond_primary); + g_free (bond_miimon); + g_free (bond_downdelay); + g_free (bond_updelay); + g_free (bond_arpinterval); + g_free (bond_arpiptarget); + g_free (bond_lacp_rate); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, "bond-slave")) { + /* Build up the settings required for 'bond-slave' */ + const char *master = NULL; + char *master_ask = NULL; + const char *checked_master = NULL; + const char *type = NULL; + nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, + {"type", TRUE, &type, FALSE}, + {NULL} }; + + /* Set global variables for use in TAB completion */ + nmc_tab_completion.con_type = NM_SETTING_BOND_SETTING_NAME; + + if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) + return FALSE; + + if (!master && ask) + master = master_ask = nmc_readline (PROMPT_BOND_MASTER); + if (!master) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'master' is required.")); + return FALSE; + } + /* Verify master argument */ + checked_master = verify_master_for_slave (all_connections, master, NM_SETTING_BOND_SETTING_NAME); + if (!checked_master) + g_print (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); + + if (type) + g_print (_("Warning: 'type' is currently ignored. " + "We only support ethernet slaves for now.\n")); + + /* Change properties in 'connection' setting */ + g_object_set (s_con, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, + NULL); + + /* Add ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + g_free (master_ask); + + } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) { + /* Build up the settings required for 'team' */ + gboolean success = FALSE; + const char *ifname = NULL; + const char *config_c = NULL; + char *config = NULL; + char *json = NULL; + nmc_arg_t exp_args[] = { {"config", TRUE, &config_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + config = g_strdup (config_c); + if (ask) + do_questionnaire_team (&config); + + /* Generate ifname if conneciton doesn't have one */ + ifname = nm_setting_connection_get_interface_name (s_con); + if (!ifname) { + char *team_ifname = unique_master_iface_ifname (all_connections, "nm-team"); + + g_object_set (s_con, + NM_SETTING_CONNECTION_INTERFACE_NAME, team_ifname, + NULL); + g_free (team_ifname); + } + + /* Add 'team' setting */ + s_team = (NMSettingTeam *) nm_setting_team_new (); + nm_connection_add_setting (connection, NM_SETTING (s_team)); + + if (!nmc_team_check_config (config, &json, error)) { + g_prefix_error (error, _("Error: ")); + goto cleanup_team; + } + + /* Set team options */ + g_object_set (s_team, NM_SETTING_TEAM_CONFIG, json, NULL); + + success = TRUE; +cleanup_team: + g_free (config); + g_free (json); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, "team-slave")) { + /* Build up the settings required for 'team-slave' */ + gboolean success = FALSE; + const char *master = NULL; + char *master_ask = NULL; + const char *checked_master = NULL; + const char *type = NULL; + const char *config_c = NULL; + char *config = NULL; + char *json = NULL; + nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, + {"type", TRUE, &type, FALSE}, + {"config", TRUE, &config_c, FALSE}, + {NULL} }; + + /* Set global variables for use in TAB completion */ + nmc_tab_completion.con_type = NM_SETTING_TEAM_SETTING_NAME; + + if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) + return FALSE; + + if (!master && ask) + master = master_ask = nmc_readline (PROMPT_TEAM_MASTER); + if (!master) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'master' is required.")); + return FALSE; + } + /* Verify master argument */ + checked_master = verify_master_for_slave (all_connections, master, NM_SETTING_TEAM_SETTING_NAME); + if (!checked_master) + g_print (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); + + /* Also ask for all optional arguments if '--ask' is specified. */ + config = g_strdup (config_c); + if (ask) + do_questionnaire_team_slave (&config); + + if (type) + g_print (_("Warning: 'type' is currently ignored. " + "We only support ethernet slaves for now.\n")); + + /* Add 'team-port' setting */ + s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new (); + nm_connection_add_setting (connection, NM_SETTING (s_team_port)); + + if (!nmc_team_check_config (config, &json, error)) { + g_prefix_error (error, _("Error: ")); + goto cleanup_team_slave; + } + + /* Set team-port options */ + g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL); + + /* Change properties in 'connection' setting */ + g_object_set (s_con, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, + NULL); + + /* Add ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + success = TRUE; +cleanup_team_slave: + g_free (master_ask); + g_free (config); + g_free (json); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) { + /* Build up the settings required for 'bridge' */ + gboolean success = FALSE; + const char *ifname = NULL; + const char *stp_c = NULL; + char *stp = NULL; + const char *priority_c = NULL; + char *priority = NULL; + const char *fwd_delay_c = NULL; + char *fwd_delay = NULL; + const char *hello_time_c = NULL; + char *hello_time = NULL; + const char *max_age_c = NULL; + char *max_age = NULL; + const char *ageing_time_c = NULL; + char *ageing_time = NULL; + gboolean stp_bool; + unsigned long stp_prio_int, fwd_delay_int, hello_time_int, + max_age_int, ageing_time_int; + const char *mac_c = NULL; + char *mac = NULL; + nmc_arg_t exp_args[] = { {"stp", TRUE, &stp_c, FALSE}, + {"priority", TRUE, &priority_c, FALSE}, + {"forward-delay", TRUE, &fwd_delay_c, FALSE}, + {"hello-time", TRUE, &hello_time_c, FALSE}, + {"max-age", TRUE, &max_age_c, FALSE}, + {"ageing-time", TRUE, &ageing_time_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + stp = g_strdup (stp_c); + priority = g_strdup (priority_c); + fwd_delay = g_strdup (fwd_delay_c); + hello_time = g_strdup (hello_time_c); + max_age = g_strdup (max_age_c); + ageing_time = g_strdup (ageing_time_c); + mac = g_strdup (mac_c); + if (ask) + do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time, + &max_age, &ageing_time, &mac); + + /* Generate ifname if conneciton doesn't have one */ + ifname = nm_setting_connection_get_interface_name (s_con); + if (!ifname) { + char *bridge_ifname = unique_master_iface_ifname (all_connections, "nm-bridge"); + + g_object_set (s_con, + NM_SETTING_CONNECTION_INTERFACE_NAME, bridge_ifname, + NULL); + g_free (bridge_ifname); + } + + if (stp) { + GError *tmp_err = NULL; + if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'stp': %s."), tmp_err->message); + g_clear_error (&tmp_err); + goto cleanup_bridge; + } + } + + /* Add 'bond' setting */ + /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */ + s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bridge)); + + if (priority) + if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE, + NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error)) + goto cleanup_bridge; + if (fwd_delay) + if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE, + NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error)) + goto cleanup_bridge; + if (hello_time) + if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE, + NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error)) + goto cleanup_bridge; + if (max_age) + if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE, + NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error)) + goto cleanup_bridge; + if (ageing_time) + if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE, + NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error)) + goto cleanup_bridge; + if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) + goto cleanup_bridge; + + /* Set bridge options */ + if (stp) + g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL); + if (priority) + g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL); + if (fwd_delay) + g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL); + if (hello_time) + g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL); + if (max_age) + g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL); + if (ageing_time) + g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL); + if (mac) + g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NULL); + + success = TRUE; +cleanup_bridge: + g_free (stp); + g_free (priority); + g_free (fwd_delay); + g_free (hello_time); + g_free (max_age); + g_free (ageing_time); + g_free (mac); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, "bridge-slave")) { + /* Build up the settings required for 'bridge-slave' */ + gboolean success = FALSE; + const char *master = NULL; + char *master_ask = NULL; + const char *checked_master = NULL; + const char *type = NULL; + const char *priority_c = NULL; + char *priority = NULL; + const char *path_cost_c = NULL; + char *path_cost = NULL; + const char *hairpin_c = NULL; + char *hairpin = NULL; + unsigned long prio_int, path_cost_int; + gboolean hairpin_bool; + nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, + {"type", TRUE, &type, FALSE}, + {"priority", TRUE, &priority_c, FALSE}, + {"path-cost", TRUE, &path_cost_c, FALSE}, + {"hairpin", TRUE, &hairpin_c, FALSE}, + {NULL} }; + + /* Set global variables for use in TAB completion */ + nmc_tab_completion.con_type = NM_SETTING_BRIDGE_SETTING_NAME; + + if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) + return FALSE; + + if (!master && ask) + master = master_ask = nmc_readline (PROMPT_BRIDGE_MASTER); + if (!master) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'master' is required.")); + return FALSE; + } + /* Verify master argument */ + checked_master = verify_master_for_slave (all_connections, master, NM_SETTING_BRIDGE_SETTING_NAME); + if (!checked_master) + g_print (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); + + if (type) + g_print (_("Warning: 'type' is currently ignored. " + "We only support ethernet slaves for now.\n")); + + /* Add 'bridge-port' setting */ + /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */ + s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bridge_port)); + + /* Also ask for all optional arguments if '--ask' is specified. */ + priority = g_strdup (priority_c); + path_cost = g_strdup (path_cost_c); + hairpin = g_strdup (hairpin_c); + if (ask) + do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin); + + if (priority) + if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT, + NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error)) + goto cleanup_bridge_slave; + if (path_cost) + if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT, + NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error)) + goto cleanup_bridge_slave; + if (hairpin) { + GError *tmp_err = NULL; + if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'hairpin': %s."), tmp_err->message); + g_clear_error (&tmp_err); + goto cleanup_bridge_slave; + } + } + + /* Change properties in 'connection' setting */ + g_object_set (s_con, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, + NULL); + + /* Add ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + if (priority) + g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL); + if (path_cost) + g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL); + if (hairpin) + g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL); + + success = TRUE; +cleanup_bridge_slave: + g_free (master_ask); + g_free (priority); + g_free (path_cost); + g_free (hairpin); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) { + /* Build up the settings required for 'vpn' */ + gboolean success = FALSE; + const char *vpn_type = NULL; + char *vpn_type_ask = NULL; + const char *user_c = NULL; + char *user = NULL; + const char *st; + char *service_type = NULL; + nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask}, + {"user", TRUE, &user_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!vpn_type && ask) + vpn_type = vpn_type_ask = nmc_readline (PROMPT_VPN_TYPE); + if (!vpn_type) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'vpn-type' is required.")); + goto cleanup_vpn; + } + if (vpn_type_ask) + vpn_type = g_strstrip (vpn_type_ask); + + if (!(st = nmc_string_is_valid (vpn_type, nmc_known_vpns, NULL))) { + g_print (_("Warning: 'vpn-type': %s not known.\n"), vpn_type); + st = vpn_type; + } + service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st); + + /* Also ask for all optional arguments if '--ask' is specified. */ + user = g_strdup (user_c); + if (ask) + do_questionnaire_vpn (&user); + + /* Add 'vpn' setting */ + s_vpn = (NMSettingVpn *) nm_setting_vpn_new (); + nm_connection_add_setting (connection, NM_SETTING (s_vpn)); + + g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL); + g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL); + + success = TRUE; +cleanup_vpn: + g_free (vpn_type_ask); + g_free (service_type); + g_free (user); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) { + /* Build up the settings required for 'olpc' */ + gboolean success = FALSE; + char *ssid_ask = NULL; + const char *ssid = NULL; + GBytes *ssid_bytes; + const char *channel_c = NULL; + char *channel = NULL; + unsigned long chan; + const char *dhcp_anycast_c = NULL; + char *dhcp_anycast = NULL; + nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask}, + {"channel", TRUE, &channel_c, FALSE}, + {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!ssid && ask) + ssid = ssid_ask = nmc_readline (_("SSID: ")); + if (!ssid) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'ssid' is required.")); + goto cleanup_olpc; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + channel = g_strdup (channel_c); + dhcp_anycast = g_strdup (dhcp_anycast_c); + if (ask) + do_questionnaire_olpc (&channel, &dhcp_anycast); + + if (channel) { + if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'channel': '%s' is not valid; use <1-13>."), + channel); + goto cleanup_olpc; + } + } + if (!check_mac (dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", error)) + goto cleanup_olpc; + + /* Add OLPC mesh setting */ + s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new (); + nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh)); + + ssid_bytes = g_bytes_new (ssid, strlen (ssid)); + g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_bytes, NULL); + if (channel) + g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL); + else + g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL); + if (dhcp_anycast) + g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, dhcp_anycast, NULL); + g_bytes_unref (ssid_bytes); + + success = TRUE; +cleanup_olpc: + g_free (ssid_ask); + g_free (channel); + g_free (dhcp_anycast); + if (!success) + return FALSE; + + } else { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' is not a valid connection type."), + con_type); + return FALSE; + } + + /* Read and add IP configuration */ + if ( strcmp (con_type, "bond-slave") != 0 + && strcmp (con_type, "team-slave") != 0 + && strcmp (con_type, "bridge-slave") != 0) { + + NMIPAddress *ip4addr = NULL, *ip6addr = NULL; + const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL; + nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE}, + {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE}, + {NULL} }; + + while (argc) { + nmc_arg_t *p; + + /* reset 'found' flag */ + for (p = exp_args; p->name; p++) + p->found = FALSE; + + ip4 = gw4 = ip6 = gw6 = NULL; + + if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) + return FALSE; + + /* coverity[dead_error_begin] */ + if (ip4) { + ip4addr = nmc_parse_and_build_address (AF_INET, ip4, error); + if (!ip4addr) { + g_prefix_error (error, _("Error: ")); + return FALSE; + } + add_ip4_address_to_connection (ip4addr, connection); + } + + /* coverity[dead_error_begin] */ + if (gw4) { + NMSettingIPConfig *s_ip = nm_connection_get_setting_ip4_config (connection); + + if (!s_ip) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: IPv4 gateway specified without IPv4 addresses")); + return FALSE; + } else if (nm_setting_ip_config_get_gateway (s_ip)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: multiple IPv4 gateways specified")); + return FALSE; + } else if (!nm_utils_ipaddr_valid (AF_INET, gw4)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: Invalid IPv4 gateway '%s'"), + gw4); + } + + g_object_set (s_ip, + NM_SETTING_IP_CONFIG_GATEWAY, gw4, + NULL); + } + + /* coverity[dead_error_begin] */ + if (ip6) { + ip6addr = nmc_parse_and_build_address (AF_INET6, ip6, error); + if (!ip6addr) { + g_prefix_error (error, _("Error: ")); + return FALSE; + } + add_ip6_address_to_connection (ip6addr, connection); + } + + /* coverity[dead_error_begin] */ + if (gw6) { + NMSettingIPConfig *s_ip = nm_connection_get_setting_ip6_config (connection); + + if (!s_ip) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: IPv6 gateway specified without IPv6 addresses")); + return FALSE; + } else if (nm_setting_ip_config_get_gateway (s_ip)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: multiple IPv6 gateways specified")); + return FALSE; + } else if (!nm_utils_ipaddr_valid (AF_INET, gw6)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: Invalid IPv6 gateway '%s'"), + gw6); + } + + g_object_set (s_ip, + NM_SETTING_IP_CONFIG_GATEWAY, gw6, + NULL); + } + } + + /* Ask for addresses if '--ask' is specified. */ + if (ask) + do_questionnaire_ip (connection); + } + + return TRUE; +} + +static char * +unique_connection_name (const GPtrArray *connections, const char *try_name) +{ + NMConnection *connection; + const char *name; + char *new_name; + unsigned int num = 1; + int i = 0; + + new_name = g_strdup (try_name); + while (i < connections->len) { + connection = NM_CONNECTION (connections->pdata[i]); + + name = nm_connection_get_id (connection); + if (g_strcmp0 (new_name, name) == 0) { + g_free (new_name); + new_name = g_strdup_printf ("%s-%d", try_name, num++); + i = 0; + } else + i++; + } + return new_name; +} + +typedef struct { + NmCli *nmc; + char *con_name; +} AddConnectionInfo; + +static void +add_connection_cb (GObject *client, + GAsyncResult *result, + gpointer user_data) +{ + AddConnectionInfo *info = (AddConnectionInfo *) user_data; + NmCli *nmc = info->nmc; + NMRemoteConnection *connection; + GError *error = NULL; + + connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error); + if (error) { + g_string_printf (nmc->return_text, + _("Error: Failed to add '%s' connection: %s"), + info->con_name, error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + } else { + g_print (_("Connection '%s' (%s) successfully added.\n"), + nm_connection_get_id (NM_CONNECTION (connection)), + nm_connection_get_uuid (NM_CONNECTION (connection))); + g_object_unref (connection); + } + + g_free (info->con_name); + g_free (info); + quit (); +} + +static void +add_new_connection (gboolean persistent, + NMClient *client, + NMConnection *connection, + GAsyncReadyCallback callback, + gpointer user_data) +{ + nm_client_add_connection_async (client, connection, persistent, + NULL, callback, user_data); +} + +static void +update_connection (gboolean persistent, + NMRemoteConnection *connection, + GAsyncReadyCallback callback, + gpointer user_data) +{ + nm_remote_connection_commit_changes_async (connection, persistent, + NULL, callback, user_data); +} + +static char * +gen_func_vpn_types (const char *text, int state) +{ + return nmc_rl_gen_func_basic (text, state, nmc_known_vpns); +} + +static char * +gen_func_bool_values_l10n (const char *text, int state) +{ + const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_wifi_mode (const char *text, int state) +{ + const char *words[] = { "infrastructure", "ap", "adhoc", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_ib_type (const char *text, int state) +{ + const char *words[] = { "datagram", "connected", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_bt_type (const char *text, int state) +{ + const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_bond_mode (const char *text, int state) +{ + const char *words[] = { "balance-rr", "active-backup", "balance-xor", "broadcast", + "802.3ad", "balance-tlb", "balance-alb", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} +static char * +gen_func_bond_mon_mode (const char *text, int state) +{ + const char *words[] = { "miimon", "arp", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_master_ifnames (const char *text, int state) +{ + int i; + GPtrArray *ifnames; + char *ret; + NMConnection *con; + NMSettingConnection *s_con; + const char *con_type, *ifname; + + if (!nm_cli.connections) + return NULL; + + /* Disable appending space after completion */ + rl_completion_append_character = '\0'; + + ifnames = g_ptr_array_sized_new (20); + for (i = 0; i < nm_cli.connections->len; i++) { + con = NM_CONNECTION (nm_cli.connections->pdata[i]); + s_con = nm_connection_get_setting_connection (con); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0) + continue; + ifname = nm_connection_get_interface_name (con); + g_ptr_array_add (ifnames, (gpointer) ifname); + } + g_ptr_array_add (ifnames, (gpointer) NULL); + + ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata); + + g_ptr_array_free (ifnames, TRUE); + return ret; +} + +static gboolean +is_single_word (const char* line) +{ + size_t n1, n2, n3; + + n1 = strspn (line, " \t"); + n2 = strcspn (line+n1, " \t\0") + n1; + n3 = strspn (line+n2, " \t"); + + if (n3 == 0) + return TRUE; + else + return FALSE; +} + +static char ** +nmcli_con_add_tab_completion (const char *text, int start, int end) +{ + char **match_array = NULL; + rl_compentry_func_t *generator_func = NULL; + + /* Disable readline's default filename completion */ + rl_attempted_completion_over = 1; + + /* Restore standard append character to space */ + rl_completion_append_character = ' '; + + if (!is_single_word (rl_line_buffer)) + return NULL; + + if (g_strcmp0 (rl_prompt, PROMPT_CON_TYPE) == 0) + generator_func = gen_connection_types; + else if (g_strcmp0 (rl_prompt, PROMPT_VPN_TYPE) == 0) + generator_func = gen_func_vpn_types; + else if ( g_strcmp0 (rl_prompt, PROMPT_BOND_MASTER) == 0 + || g_strcmp0 (rl_prompt, PROMPT_TEAM_MASTER) == 0 + || g_strcmp0 (rl_prompt, PROMPT_BRIDGE_MASTER) == 0) + generator_func = gen_func_master_ifnames; + else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL)) + || g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, ":")) + || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL)) + || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, ":"))) + generator_func = gen_func_bool_values_l10n; + else if (g_str_has_suffix (rl_prompt, PROMPT_WIFI_MODE)) + generator_func = gen_func_wifi_mode; + else if (g_str_has_suffix (rl_prompt, PROMPT_IB_MODE)) + generator_func = gen_func_ib_type; + else if (g_str_has_suffix (rl_prompt, PROMPT_BT_TYPE)) + generator_func = gen_func_bt_type; + else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MODE)) + generator_func = gen_func_bond_mode; + else if (g_str_has_suffix (rl_prompt, PROMPT_BOND_MON_MODE)) + generator_func = gen_func_bond_mon_mode; + + if (generator_func) + match_array = rl_completion_matches (text, generator_func); + + return match_array; +} + +static NMCResultCode +do_connection_add (NmCli *nmc, int argc, char **argv) +{ + NMConnection *connection = NULL; + NMSettingConnection *s_con; + char *uuid; + char *default_name = NULL; + const char *type = NULL; + char *type_ask = NULL; + const char *con_name = NULL; + const char *autoconnect = NULL; + gboolean auto_bool = TRUE; + const char *ifname = NULL; + char *ifname_ask = NULL; + gboolean ifname_mandatory = TRUE; + const char *save = NULL; + gboolean save_bool = TRUE; + AddConnectionInfo *info = NULL; + const char *setting_name; + GError *error = NULL; + nmc_arg_t exp_args[] = { {"type", TRUE, &type, !nmc->ask}, + {"con-name", TRUE, &con_name, FALSE}, + {"autoconnect", TRUE, &autoconnect, FALSE}, + {"ifname", TRUE, &ifname, FALSE}, + {"save", TRUE, &save, FALSE}, + {NULL} }; + + rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion; + + nmc->return_value = NMC_RESULT_SUCCESS; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { + g_string_assign (nmc->return_text, error->message); + nmc->return_value = error->code; + g_clear_error (&error); + goto error; + } + + if (!type && nmc->ask) { + char *types_tmp = get_valid_options_string (nmc_valid_connection_types); + g_print ("Valid types: [%s]\n", types_tmp); + type = type_ask = nmc_readline (PROMPT_CON_TYPE); + g_free (types_tmp); + } + if (!type) { + g_string_printf (nmc->return_text, _("Error: 'type' argument is required.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + if (type_ask) + type = g_strstrip (type_ask); + + if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, &error))) { + g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."), + error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_clear_error (&error); + goto error; + } + if (autoconnect) { + GError *tmp_err = NULL; + if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) { + g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."), + tmp_err->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_clear_error (&tmp_err); + goto error; + } + } + if (save) { + GError *tmp_err = NULL; + if (!nmc_string_to_bool (save, &save_bool, &tmp_err)) { + g_string_printf (nmc->return_text, _("Error: 'save': %s."), + tmp_err->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_clear_error (&tmp_err); + goto error; + } + } + + /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */ + if ( strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0 + || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0 + || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0 + || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0) + ifname_mandatory = FALSE; + + if (!ifname && ifname_mandatory && nmc->ask) { + ifname = ifname_ask = nmc_readline (_("Interface name [*]: ")); + if (!ifname) + ifname = ifname_ask = g_strdup ("*"); + } + if (!ifname && ifname_mandatory) { + g_string_printf (nmc->return_text, _("Error: 'ifname' argument is required.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + if (ifname) { + if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) { + g_string_printf (nmc->return_text, + _("Error: 'ifname': '%s' is not a valid interface nor '*'."), + ifname); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + /* Special value of '*' means no specific interface name */ + if (strcmp (ifname, "*") == 0) + ifname = NULL; + } + + /* Create a new connection object */ + connection = nm_simple_connection_new (); + + /* Build up the 'connection' setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + uuid = nm_utils_uuid_generate (); + if (con_name) + default_name = g_strdup (con_name); + else { + char *try_name = ifname ? + g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname) + : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types)); + default_name = unique_connection_name (nmc->connections, try_name); + g_free (try_name); + } + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, default_name, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, setting_name, + NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool, + NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, + NULL); + g_free (uuid); + g_free (default_name); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + if (!complete_connection_by_type (connection, + setting_name, + nmc->connections, + nmc->ask, + argc, + argv, + &error)) { + g_string_assign (nmc->return_text, error->message); + nmc->return_value = error->code; + g_clear_error (&error); + goto error; + } + + nmc->should_wait = TRUE; + + info = g_malloc0 (sizeof (AddConnectionInfo)); + info->nmc = nmc; + info->con_name = g_strdup (nm_connection_get_id (connection)); + + /* Tell the settings service to add the new connection */ + add_new_connection (save_bool, + nmc->client, + connection, + add_connection_cb, + info); + + if (connection) + g_object_unref (connection); + + return nmc->return_value; + +error: + if (connection) + g_object_unref (connection); + g_free (type_ask); + g_free (ifname_ask); + + nmc->should_wait = FALSE; + return nmc->return_value; +} + + +/*----------------------------------------------------------------------------*/ +/* Functions for readline TAB completion in editor */ + +static void +uuid_display_hook (char **array, int len, int max_len) +{ + NMConnection *con; + int i, max = 0; + char *tmp; + const char *id; + + for (i = 1; i <= len; i++) { + con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL); + id = con ? nm_connection_get_id (con) : NULL; + if (id) { + tmp = g_strdup_printf ("%s (%s)", array[i], id); + g_free (array[i]); + array[i] = tmp; + if (max < strlen (id)) + max = strlen (id); + } + } + rl_display_match_list (array, len, max_len + max + 3); + rl_forced_update_display (); +} + +static char * +gen_nmcli_cmds_menu (const char *text, int state) +{ + const char *words[] = { "goto", "set", "remove", "describe", "print", "verify", + "save", "activate", "back", "help", "quit", "nmcli", + NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_nmcli_cmds_submenu (const char *text, int state) +{ + const char *words[] = { "set", "add", "change", "remove", "describe", + "print", "back", "help", "quit", + NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_cmd_nmcli (const char *text, int state) +{ + const char *words[] = { "status-line", "save-confirmation", "show-secrets", "prompt-color", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_cmd_nmcli_prompt_color (const char *text, int state) +{ + const char *words[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_bool_values (const char *text, int state) +{ + const char *words[] = { "yes", "no", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_cmd_verify0 (const char *text, int state) +{ + const char *words[] = { "all", "fix", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_cmd_print0 (const char *text, int state) +{ + static char **words = NULL; + char *ret = NULL; + + if (!state) { + GVariant *settings; + GVariantIter iter; + const char *setting_name; + int i = 0; + + settings = nm_connection_to_dbus (nmc_tab_completion.connection, NM_CONNECTION_SERIALIZE_NO_SECRETS); + words = g_new (char *, g_variant_n_children (settings) + 2); + g_variant_iter_init (&iter, settings); + while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL)) + words [i++] = g_strdup (setting_name); + words[i++] = g_strdup ("all"); + words[i] = NULL; + g_variant_unref (settings); + } + + if (words) { + ret = nmc_rl_gen_func_basic (text, state, (const char **) words); + if (ret == NULL) { + g_strfreev (words); + words = NULL; + } + } + return ret; +} + +static char * +gen_cmd_print2 (const char *text, int state) +{ + const char *words[] = { "setting", "connection", "all", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_cmd_save (const char *text, int state) +{ + const char *words[] = { "persistent", "temporary", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_connection_types (const char *text, int state) +{ + static int list_idx, len; + const char *c_type, *a_type; + + if (!state) { + list_idx = 0; + len = strlen (text); + } + + while (nmc_valid_connection_types[list_idx].name) { + a_type = nmc_valid_connection_types[list_idx].alias; + c_type = nmc_valid_connection_types[list_idx].name; + list_idx++; + if (a_type && !strncmp (text, a_type, len)) + return g_strdup (a_type); + if (c_type && !strncmp (text, c_type, len)) + return g_strdup (c_type); + } + + return NULL; +} + +static char * +gen_setting_names (const char *text, int state) +{ + static int list_idx, len; + const char *s_name, *a_name; + const NameItem *valid_settings_arr; + + if (!state) { + list_idx = 0; + len = strlen (text); + } + + valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type); + if (!valid_settings_arr) + return NULL; + while (valid_settings_arr[list_idx].name) { + a_name = valid_settings_arr[list_idx].alias; + s_name = valid_settings_arr[list_idx].name; + list_idx++; + if (len == 0 && a_name) + return g_strdup_printf ("%s (%s)", s_name, a_name); + if (a_name && !strncmp (text, a_name, len)) + return g_strdup (a_name); + if (s_name && !strncmp (text, s_name, len)) + return g_strdup (s_name); + } + return NULL; +} + +static char * +gen_property_names (const char *text, int state) +{ + NMSetting *setting = NULL; + char **valid_props = NULL; + char *ret = NULL; + const char *line = rl_line_buffer; + const char *setting_name; + char **strv = NULL; + const NameItem *valid_settings_arr; + const char *p1; + + /* Try to get the setting from 'line' - setting_name.property */ + p1 = strchr (line, '.'); + if (p1) { + while (p1 > line && !g_ascii_isspace (*p1)) + p1--; + + strv = g_strsplit (p1+1, ".", 2); + + valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type); + setting_name = check_valid_name (strv[0], valid_settings_arr, NULL); + setting = nmc_setting_new_for_name (setting_name); + } else { + /* Else take the current setting, if any */ + setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL; + } + + if (setting) { + valid_props = nmc_setting_get_valid_properties (setting); + ret = nmc_rl_gen_func_basic (text, state, (const char **) valid_props); + } + + g_strfreev (strv); + g_strfreev (valid_props); + if (setting) + g_object_unref (setting); + return ret; +} + +static char * +gen_compat_devices (const char *text, int state) +{ + int i, j = 0; + const GPtrArray *devices; + const char **compatible_devices; + char *ret; + + devices = nm_client_get_devices (nmc_tab_completion.nmc->client); + if (devices->len == 0) + return NULL; + + compatible_devices = g_new (const char *, devices->len + 1); + for (i = 0; i < devices->len; i++) { + NMDevice *dev = g_ptr_array_index (devices, i); + const char *ifname = nm_device_get_iface (dev); + NMDevice *device = NULL; + const char *spec_object = NULL; + + if (find_device_for_connection (nmc_tab_completion.nmc, nmc_tab_completion.connection, + ifname, NULL, NULL, &device, &spec_object, NULL)) { + compatible_devices[j++] = ifname; + } + } + compatible_devices[j] = NULL; + + ret = nmc_rl_gen_func_basic (text, state, compatible_devices); + + g_free (compatible_devices); + return ret; +} + +static char * +gen_vpn_uuids (const char *text, int state) +{ + const GPtrArray *connections = nmc_tab_completion.nmc->connections; + int c, u = 0; + const char **uuids; + char *ret; + + if (connections->len < 1) + return NULL; + + uuids = g_new (const char *, connections->len + 1); + for (c = 0; c < connections->len; c++) { + NMConnection *connection = NM_CONNECTION (connections->pdata[c]); + const char *type = nm_connection_get_connection_type (connection); + + if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) == 0) + uuids[u++] = nm_connection_get_uuid (connection); + } + uuids[u] = NULL; + + ret = nmc_rl_gen_func_basic (text, state, uuids); + + g_free (uuids); + return ret; +} + +static rl_compentry_func_t * +get_gen_func_cmd_nmcli (const char *str) +{ + if (!str) + return NULL; + if (matches (str, "status-line") == 0) + return gen_func_bool_values; + if (matches (str, "save-confirmation") == 0) + return gen_func_bool_values; + if (matches (str, "show-secrets") == 0) + return gen_func_bool_values; + if (matches (str, "prompt-color") == 0) + return gen_cmd_nmcli_prompt_color; + return NULL; +} + +/* + * Helper function parsing line for completion. + * IN: + * line : the whole line to be parsed + * end : the position of cursor in the line + * cmd : command to match + * OUT: + * cw_num : is set to the word number being completed (1, 2, 3, 4). + * prev_word : returns the previous word (so that we have some context). + * + * Returns TRUE when the first word of the 'line' matches 'cmd'. + * + * Examples: + * line="rem" cmd="remove" -> TRUE cw_num=1 + * line="set con" cmd="set" -> TRUE cw_num=2 + * line="go ipv4.method" cmd="goto" -> TRUE cw_num=2 + * line=" des eth.mtu " cmd="describe" -> TRUE cw_num=3 + * line=" bla ipv4.method" cmd="goto" -> FALSE + */ +static gboolean +should_complete_cmd (const char *line, int end, const char *cmd, + int *cw_num, char **prev_word) +{ + char *tmp; + const char *word1, *word2, *word3; + size_t n1, n2, n3, n4, n5, n6; + gboolean word1_done, word2_done, word3_done; + gboolean ret = FALSE; + + if (!line) + return FALSE; + + tmp = g_strdup (line); + + n1 = strspn (tmp, " \t"); + n2 = strcspn (tmp+n1, " \t\0") + n1; + n3 = strspn (tmp+n2, " \t") + n2; + n4 = strcspn (tmp+n3, " \t\0") + n3; + n5 = strspn (tmp+n4, " \t") + n4; + n6 = strcspn (tmp+n5, " \t\0") + n5; + + word1_done = end > n2; + word2_done = end > n4; + word3_done = end > n6; + tmp[n2] = tmp[n4] = tmp[n6] = '\0'; + + word1 = tmp[n1] ? tmp + n1 : NULL; + word2 = tmp[n3] ? tmp + n3 : NULL; + word3 = tmp[n5] ? tmp + n5 : NULL; + + if (!word1_done) { + if (cw_num) + *cw_num = 1; + if (prev_word) + *prev_word = NULL; + } else if (!word2_done) { + if (cw_num) + *cw_num = 2; + if (prev_word) + *prev_word = g_strdup (word1); + } else if (!word3_done) { + if (cw_num) + *cw_num = 3; + if (prev_word) + *prev_word = g_strdup (word2); + } else { + if (cw_num) + *cw_num = 4; + if (prev_word) + *prev_word = g_strdup (word3); + } + + if (word1 && matches (word1, cmd) == 0) + ret = TRUE; + + g_free (tmp); + return ret; +} + +static char * +extract_property_name (const char *prompt, const char *line) +{ + char *prop = NULL; + + /* If prompt is set take the property name from it, else extract it from line */ + if (!prompt) { + const char *p1; + size_t num; + p1 = strchr (line, '.'); + if (p1) { + p1++; + } else { + size_t n1, n2, n3; + n1 = strspn (line, " \t"); + n2 = strcspn (line+n1, " \t\0") + n1; + n3 = strspn (line+n2, " \t") + n2; + p1 = line + n3; + } + num = strcspn (p1, " \t\0"); + prop = g_strndup (p1, num); + } else { + const char *p1, *dot; + size_t num; + p1 = strchr (prompt, ' '); + /* prompt looks like this: "nmcli 802-1x>" or "nmcli 802-1x.pac-file>" */ + if (p1) { + dot = strchr (p1 + 1, '.'); + p1 = dot ? dot + 1 : p1; + num = strcspn (p1, ">"); + prop = g_strndup (p1, num); + } + } + + return prop; +} + +static gboolean +should_complete_files (const char *prompt, const char *line) +{ + char *prop; + gboolean found = FALSE; + const char *file_properties[] = { + /* '802-1x' properties */ + "ca-cert", + "ca-path", + "client-cert", + "pac-file", + "phase2-ca-cert", + "phase2-ca-path", + "phase2-client-cert", + "private-key", + "phase2-private-key", + /* 'team' and 'team-port' properties */ + "config", + NULL + }; + + prop = extract_property_name (prompt, line); + if (prop) { + found = !!nmc_string_is_valid (prop, file_properties, NULL); + g_free (prop); + } + return found; +} + +static gboolean +should_complete_vpn_uuids (const char *prompt, const char *line) +{ + char *prop; + gboolean found = FALSE; + const char *uuid_properties[] = { + /* 'connection' properties */ + "secondaries", + NULL + }; + + prop = extract_property_name (prompt, line); + if (prop) { + found = !!nmc_string_is_valid (prop, uuid_properties, NULL); + g_free (prop); + } + return found; +} + +/* from readline */ +extern int rl_complete_with_tilde_expansion; + +/* + * Attempt to complete on the contents of TEXT. START and END show the + * region of TEXT that contains the word to complete. We can use the + * entire line in case we want to do some simple parsing. Return the + * array of matches, or NULL if there aren't any. + */ +static char ** +nmcli_editor_tab_completion (const char *text, int start, int end) +{ + char **match_array = NULL; + const char *line = rl_line_buffer; + const char *prompt = rl_prompt; + rl_compentry_func_t *generator_func = NULL; + gboolean copy_char; + const char *p1; + char *p2, *prompt_tmp; + char *word = NULL; + size_t n1; + int num; + + /* Restore standard append character to space */ + rl_completion_append_character = ' '; + + /* Restore standard function for displaying matches */ + rl_completion_display_matches_hook = NULL; + + /* Disable default filename completion */ + rl_attempted_completion_over = 1; + + /* Enable tilde expansion when filenames are completed */ + rl_complete_with_tilde_expansion = 1; + + /* Filter out possible ANSI color escape sequences */ + p1 = prompt; + p2 = prompt_tmp = g_strdup (prompt); + copy_char = TRUE; + while (*p1) { + if (*p1 == '\33') + copy_char = FALSE; + if (copy_char) + *p2++ = *p1; + if (!copy_char && *p1 == 'm') + copy_char = TRUE; + p1++; + } + *p2 = '\0'; + + /* Find the first non-space character */ + n1 = strspn (line, " \t"); + + /* Choose the right generator function */ + if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0) + generator_func = gen_connection_types; + else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0) + generator_func = gen_setting_names; + else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0) + generator_func = gen_property_names; + else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL)) + || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL))) + generator_func = gen_func_bool_values_l10n; + else if (g_str_has_prefix (prompt_tmp, "nmcli")) { + if (!strchr (prompt_tmp, '.')) { + int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1; + const char *dot = strchr (line, '.'); + + /* Main menu - level 0,1 */ + if (start == n1) + generator_func = gen_nmcli_cmds_menu; + else { + if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) { + if (level == 0 && (!dot || dot >= line + end)) + generator_func = gen_setting_names; + else + generator_func = gen_property_names; + } else if (should_complete_cmd (line, end, "set", &num, NULL)) { + if (num < 3) { + if (level == 0 && (!dot || dot >= line + end)) { + generator_func = gen_setting_names; + rl_completion_append_character = '.'; + } else + generator_func = gen_property_names; + } else if (num >= 3) { + if (num == 3 && should_complete_files (NULL, line)) + rl_attempted_completion_over = 0; + if (should_complete_vpn_uuids (NULL, line)) { + rl_completion_display_matches_hook = uuid_display_hook; + generator_func = gen_vpn_uuids; + } + } + } else if ( ( should_complete_cmd (line, end, "remove", &num, NULL) + || should_complete_cmd (line, end, "describe", &num, NULL)) + && num <= 2) { + if (level == 0 && (!dot || dot >= line + end)) { + generator_func = gen_setting_names; + rl_completion_append_character = '.'; + } else + generator_func = gen_property_names; + } else if (should_complete_cmd (line, end, "nmcli", &num, &word)) { + if (num < 3) + generator_func = gen_cmd_nmcli; + else if (num == 3) + generator_func = get_gen_func_cmd_nmcli (word); + } else if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) { + if (level == 0 && (!dot || dot >= line + end)) + generator_func = gen_cmd_print0; + else + generator_func = gen_property_names; + } else if (should_complete_cmd (line, end, "verify", &num, NULL) && num <= 2) { + generator_func = gen_cmd_verify0; + } else if (should_complete_cmd (line, end, "activate", &num, NULL) && num <= 2) { + generator_func = gen_compat_devices; + } else if (should_complete_cmd (line, end, "save", &num, NULL) && num <= 2) { + generator_func = gen_cmd_save; + } else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2) + generator_func = gen_nmcli_cmds_menu; + } + } else { + /* Submenu - level 2 */ + if (start == n1) + generator_func = gen_nmcli_cmds_submenu; + else { + if ( should_complete_cmd (line, end, "add", &num, NULL) + || should_complete_cmd (line, end, "set", &num, NULL)) { + if (num <= 2 && should_complete_files (prompt_tmp, line)) + rl_attempted_completion_over = 0; + else if (should_complete_vpn_uuids (prompt_tmp, line)) { + rl_completion_display_matches_hook = uuid_display_hook; + generator_func = gen_vpn_uuids; + } + } + if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) + generator_func = gen_cmd_print2; + else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2) + generator_func = gen_nmcli_cmds_submenu; + } + } + } + + if (generator_func) + match_array = rl_completion_matches (text, generator_func); + + g_free (prompt_tmp); + g_free (word); + return match_array; +} + +#define NMCLI_EDITOR_HISTORY ".nmcli-history" + +static void +load_history_cmds (const char *uuid) +{ + GKeyFile *kf; + char *filename; + char **keys; + char *line; + size_t i; + GError *err = NULL; + + filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL); + kf = g_key_file_new (); + if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) { + if (err->code == G_KEY_FILE_ERROR_PARSE) + g_print ("Warning: %s parse error: %s\n", filename, err->message); + g_key_file_free (kf); + g_free (filename); + return; + } + keys = g_key_file_get_keys (kf, uuid, NULL, NULL); + for (i = 0; keys && keys[i]; i++) { + line = g_key_file_get_string (kf, uuid, keys[i], NULL); + if (line && *line) + add_history (line); + g_free (line); + } + g_strfreev (keys); + g_key_file_free (kf); + g_free (filename); +} + +static void +save_history_cmds (const char *uuid) +{ + HIST_ENTRY **hist = NULL; + GKeyFile *kf; + char *filename; + size_t i; + char *key; + char *data; + gsize len = 0; + GError *err = NULL; + + hist = history_list (); + if (hist) { + filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL); + kf = g_key_file_new (); + if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) { + if ( err->code != G_FILE_ERROR_NOENT + && err->code != G_KEY_FILE_ERROR_NOT_FOUND) { + g_print ("Warning: %s parse error: %s\n", filename, err->message); + g_key_file_free (kf); + g_free (filename); + g_clear_error (&err); + return; + } + g_clear_error (&err); + } + + /* Remove previous history group and save new history entries */ + g_key_file_remove_group (kf, uuid, NULL); + for (i = 0; hist[i]; i++) + { + key = g_strdup_printf ("%zd", i); + g_key_file_set_string (kf, uuid, key, hist[i]->line); + g_free (key); + } + + /* Write history to file */ + data = g_key_file_to_data (kf, &len, NULL); + if (data) { + g_file_set_contents (filename, data, len, NULL); + g_free (data); + } + g_key_file_free (kf); + g_free (filename); + } +} + +/*----------------------------------------------------------------------------*/ + +static void +editor_show_connection (NMConnection *connection, NmCli *nmc) +{ + nmc->print_output = NMC_PRINT_PRETTY; + nmc->multiline_output = TRUE; + nmc->escape_values = 0; + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + nmc_connection_profile_details (connection, nmc, nmc->editor_show_secrets); +} + +static void +editor_show_setting (NMSetting *setting, NmCli *nmc) +{ + g_print (_("['%s' setting values]\n"), + nm_setting_get_name (setting)); + + nmc->print_output = NMC_PRINT_NORMAL; + nmc->multiline_output = TRUE; + nmc->escape_values = 0; + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + setting_details (setting, nmc, NULL, nmc->editor_show_secrets); +} + +typedef enum { + NMC_EDITOR_MAIN_CMD_UNKNOWN = 0, + NMC_EDITOR_MAIN_CMD_GOTO, + NMC_EDITOR_MAIN_CMD_REMOVE, + NMC_EDITOR_MAIN_CMD_SET, + NMC_EDITOR_MAIN_CMD_DESCRIBE, + NMC_EDITOR_MAIN_CMD_PRINT, + NMC_EDITOR_MAIN_CMD_VERIFY, + NMC_EDITOR_MAIN_CMD_SAVE, + NMC_EDITOR_MAIN_CMD_ACTIVATE, + NMC_EDITOR_MAIN_CMD_BACK, + NMC_EDITOR_MAIN_CMD_HELP, + NMC_EDITOR_MAIN_CMD_NMCLI, + NMC_EDITOR_MAIN_CMD_QUIT, +} NmcEditorMainCmd; + +static NmcEditorMainCmd +parse_editor_main_cmd (const char *cmd, char **cmd_arg) +{ + NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN; + char **vec; + + vec = nmc_strsplit_set (cmd, " \t", 2); + if (g_strv_length (vec) < 1) { + if (cmd_arg) + *cmd_arg = NULL; + return NMC_EDITOR_MAIN_CMD_UNKNOWN; + } + + if (matches (vec[0], "goto") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO; + else if (matches (vec[0], "remove") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE; + else if (matches (vec[0], "set") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_SET; + else if (matches (vec[0], "describe") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE; + else if (matches (vec[0], "print") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT; + else if (matches (vec[0], "verify") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY; + else if (matches (vec[0], "save") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE; + else if (matches (vec[0], "activate") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE; + else if (matches (vec[0], "back") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_BACK; + else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_HELP; + else if (matches (vec[0], "quit") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT; + else if (matches (vec[0], "nmcli") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI; + + /* set pointer to command argument */ + if (cmd_arg) + *cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL; + + g_strfreev (vec); + return editor_cmd; +} + +static void +editor_main_usage (void) +{ + g_print ("------------------------------------------------------------------------------\n"); + /* TRANSLATORS: do not translate command names and keywords before :: + * However, you should translate terms enclosed in <>. + */ + g_print (_("---[ Main menu ]---\n" + "goto [<setting> | <prop>] :: go to a setting or property\n" + "remove <setting>[.<prop>] | <prop> :: remove setting or reset property value\n" + "set [<setting>.<prop> <value>] :: set property value\n" + "describe [<setting>.<prop>] :: describe property\n" + "print [all | <setting>[.<prop>]] :: print the connection\n" + "verify [all | fix] :: verify the connection\n" + "save [persistent|temporary] :: save the connection\n" + "activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n" + "back :: go one level up (back)\n" + "help/? [<command>] :: print this help\n" + "nmcli <conf-option> <value> :: nmcli configuration\n" + "quit :: exit nmcli\n")); + g_print ("------------------------------------------------------------------------------\n"); +} + +static void +editor_main_help (const char *command) +{ + if (!command) + editor_main_usage (); + else { + /* detailed command descriptions */ + NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL); + + switch (cmd) { + case NMC_EDITOR_MAIN_CMD_GOTO: + g_print (_("goto <setting>[.<prop>] | <prop> :: enter setting/property for editing\n\n" + "This command enters into a setting or property for editing it.\n\n" + "Examples: nmcli> goto connection\n" + " nmcli connection> goto secondaries\n" + " nmcli> goto ipv4.addresses\n")); + break; + case NMC_EDITOR_MAIN_CMD_REMOVE: + g_print (_("remove <setting>[.<prop>] :: remove setting or reset property value\n\n" + "This command removes an entire setting from the connection, or if a property\n" + "is given, resets that property to the default value.\n\n" + "Examples: nmcli> remove wifi-sec\n" + " nmcli> remove eth.mtu\n")); + break; + case NMC_EDITOR_MAIN_CMD_SET: + g_print (_("set [<setting>.<prop> <value>] :: set property value\n\n" + "This command sets property value.\n\n" + "Example: nmcli> set con.id My connection\n")); + break; + case NMC_EDITOR_MAIN_CMD_DESCRIBE: + g_print (_("describe [<setting>.<prop>] :: describe property\n\n" + "Shows property description. You can consult nm-settings(5) " + "manual page to see all NM settings and properties.\n")); + break; + case NMC_EDITOR_MAIN_CMD_PRINT: + g_print (_("print [all] :: print setting or connection values\n\n" + "Shows current property or the whole connection.\n\n" + "Example: nmcli ipv4> print all\n")); + break; + case NMC_EDITOR_MAIN_CMD_VERIFY: + g_print (_("verify [all | fix] :: verify setting or connection validity\n\n" + "Verifies whether the setting or connection is valid and can be saved later.\n" + "It indicates invalid values on error. Some errors may be fixed automatically\n" + "by 'fix' option.\n\n" + "Examples: nmcli> verify\n" + " nmcli> verify fix\n" + " nmcli bond> verify\n")); + break; + case NMC_EDITOR_MAIN_CMD_SAVE: + g_print (_("save [persistent|temporary] :: save the connection\n\n" + "Sends the connection profile to NetworkManager that either will save it\n" + "persistently, or will only keep it in memory. 'save' without an argument\n" + "means 'save persistent'.\n" + "Note that once you save the profile persistently those settings are saved\n" + "across reboot or restart. Subsequent changes can also be temporary or\n" + "persistent, but any temporary changes will not persist across reboot or\n" + "restart. If you want to fully remove the persistent connection, the connection\n" + "profile must be deleted.\n")); + break; + case NMC_EDITOR_MAIN_CMD_ACTIVATE: + g_print (_("activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n\n" + "Activates the connection.\n\n" + "Available options:\n" + "<ifname> - device the connection will be activated on\n" + "/<ap>|<nsp> - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when <ifname> is not specified)\n")); + break; + case NMC_EDITOR_MAIN_CMD_BACK: + g_print (_("back :: go to upper menu level\n\n")); + break; + case NMC_EDITOR_MAIN_CMD_HELP: + g_print (_("help/? [<command>] :: help for the nmcli commands\n\n")); + break; + case NMC_EDITOR_MAIN_CMD_NMCLI: + g_print (_("nmcli [<conf-option> <value>] :: nmcli configuration\n\n" + "Configures nmcli. The following options are available:\n" + "status-line yes | no [default: no]\n" + "save-confirmation yes | no [default: yes]\n" + "show-secrets yes | no [default: no]\n" + "prompt-color <0-8> [default: 0]\n" + " 0 = normal\n" + " 1 = \33[30mblack\33[0m\n" + " 2 = \33[31mred\33[0m\n" + " 3 = \33[32mgreen\33[0m\n" + " 4 = \33[33myellow\33[0m\n" + " 5 = \33[34mblue\33[0m\n" + " 6 = \33[35mmagenta\33[0m\n" + " 7 = \33[36mcyan\33[0m\n" + " 8 = \33[37mwhite\33[0m\n" + "\n" + "Examples: nmcli> nmcli status-line yes\n" + " nmcli> nmcli save-confirmation no\n" + " nmcli> nmcli prompt-color 3\n")); + break; + case NMC_EDITOR_MAIN_CMD_QUIT: + g_print (_("quit :: exit nmcli\n\n" + "This command exits nmcli. When the connection being edited " + "is not saved, the user is asked to confirm the action.\n")); + break; + default: + g_print (_("Unknown command: '%s'\n"), command); + break; + } + } +} + +typedef enum { + NMC_EDITOR_SUB_CMD_UNKNOWN = 0, + NMC_EDITOR_SUB_CMD_SET, + NMC_EDITOR_SUB_CMD_ADD, + NMC_EDITOR_SUB_CMD_CHANGE, + NMC_EDITOR_SUB_CMD_REMOVE, + NMC_EDITOR_SUB_CMD_DESCRIBE, + NMC_EDITOR_SUB_CMD_PRINT, + NMC_EDITOR_SUB_CMD_BACK, + NMC_EDITOR_SUB_CMD_HELP, + NMC_EDITOR_SUB_CMD_QUIT +} NmcEditorSubCmd; + +static NmcEditorSubCmd +parse_editor_sub_cmd (const char *cmd, char **cmd_arg) +{ + NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN; + char **vec; + + vec = nmc_strsplit_set (cmd, " \t", 2); + if (g_strv_length (vec) < 1) { + if (cmd_arg) + *cmd_arg = NULL; + return NMC_EDITOR_SUB_CMD_UNKNOWN; + } + + if (matches (vec[0], "set") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_SET; + else if (matches (vec[0], "add") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_ADD; + else if (matches (vec[0], "change") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE; + else if (matches (vec[0], "remove") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE; + else if (matches (vec[0], "describe") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE; + else if (matches (vec[0], "print") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_PRINT; + else if (matches (vec[0], "back") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_BACK; + else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_HELP; + else if (matches (vec[0], "quit") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_QUIT; + + /* set pointer to command argument */ + if (cmd_arg) + *cmd_arg = g_strdup (vec[1]); + + g_strfreev (vec); + return editor_cmd; +} + +static void +editor_sub_help (void) +{ + g_print ("------------------------------------------------------------------------------\n"); + /* TRANSLATORS: do not translate command names and keywords before :: + * However, you should translate terms enclosed in <>. + */ + g_print (_("---[ Property menu ]---\n" + "set [<value>] :: set new value\n" + "add [<value>] :: add new option to the property\n" + "change :: change current value\n" + "remove [<index> | <option>] :: delete the value\n" + "describe :: describe property\n" + "print [setting | connection] :: print property (setting/connection) value(s)\n" + "back :: go to upper level\n" + "help/? [<command>] :: print this help or command description\n" + "quit :: exit nmcli\n")); + g_print ("------------------------------------------------------------------------------\n"); +} + +static void +editor_sub_usage (const char *command) +{ + + if (!command) + editor_sub_help (); + else { + /* detailed command descriptions */ + NmcEditorSubCmd cmdsub = parse_editor_sub_cmd (command, NULL); + + switch (cmdsub) { + case NMC_EDITOR_SUB_CMD_SET: + g_print (_("set [<value>] :: set new value\n\n" + "This command sets provided <value> to this property\n")); + break; + case NMC_EDITOR_SUB_CMD_ADD: + g_print (_("add [<value>] :: append new value to the property\n\n" + "This command adds provided <value> to this property, if " + "the property is of a container type. For single-valued " + "properties the property value is replaced (same as 'set').\n")); + break; + case NMC_EDITOR_SUB_CMD_CHANGE: + g_print (_("change :: change current value\n\n" + "Displays current value and allows editing it.\n")); + break; + case NMC_EDITOR_SUB_CMD_REMOVE: + g_print (_("remove [<value>|<index>|<option name>] :: delete the value\n\n" + "Removes the property value. For single-valued properties, this sets the\n" + "property back to its default value. For container-type properties, this removes\n" + "all the values of that property, or you can specify an argument to remove just\n" + "a single item or option. The argument is either a value or index of the item to\n" + "remove, or an option name (for properties with named options).\n\n" + "Examples: nmcli ipv4.dns> remove 8.8.8.8\n" + " nmcli ipv4.dns> remove 2\n" + " nmcli bond.options> remove downdelay\n\n")); + break; + case NMC_EDITOR_SUB_CMD_DESCRIBE: + g_print (_("describe :: describe property\n\n" + "Shows property description. You can consult nm-settings(5) " + "manual page to see all NM settings and properties.\n")); + break; + case NMC_EDITOR_SUB_CMD_PRINT: + g_print (_("print [property|setting|connection] :: print property (setting, connection) value(s)\n\n" + "Shows property value. Providing an argument you can also display " + "values for the whole setting or connection.\n")); + break; + case NMC_EDITOR_SUB_CMD_BACK: + g_print (_("back :: go to upper menu level\n\n")); + break; + case NMC_EDITOR_SUB_CMD_HELP: + g_print (_("help/? [<command>] :: help for nmcli commands\n\n")); + break; + case NMC_EDITOR_SUB_CMD_QUIT: + g_print (_("quit :: exit nmcli\n\n" + "This command exits nmcli. When the connection being edited " + "is not saved, the user is asked to confirm the action.\n")); + break; + default: + g_print (_("Unknown command: '%s'\n"), command); + break; + } + } +} + +/*----------------------------------------------------------------------------*/ + +typedef struct { + NMDevice *device; + NMActiveConnection *ac; + guint monitor_id; +} MonitorACInfo; + +static gboolean nmc_editor_cb_called; +static GError *nmc_editor_error; +static MonitorACInfo *nmc_editor_monitor_ac; +static GMutex nmc_editor_mutex; +static GCond nmc_editor_cond; + +/* + * Store 'error' to shared 'nmc_editor_error' and monitoring info to + * 'nmc_editor_monitor_ac' and signal the condition so that + * the 'editor-thread' thread could process that. + */ +static void +set_info_and_signal_editor_thread (GError *error, MonitorACInfo *monitor_ac_info) +{ + g_mutex_lock (&nmc_editor_mutex); + nmc_editor_cb_called = TRUE; + nmc_editor_error = error ? g_error_copy (error) : NULL; + nmc_editor_monitor_ac = monitor_ac_info; + g_cond_signal (&nmc_editor_cond); + g_mutex_unlock (&nmc_editor_mutex); +} + +static void +add_connection_editor_cb (GObject *client, + GAsyncResult *result, + gpointer user_data) +{ + NMRemoteConnection *connection; + GError *error = NULL; + + connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error); + set_info_and_signal_editor_thread (error, NULL); + + g_clear_object (&connection); + g_clear_error (&error); +} + +static void +update_connection_editor_cb (GObject *connection, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + + nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection), + result, &error); + set_info_and_signal_editor_thread (error, NULL); + g_clear_error (&error); +} + +static gboolean +progress_activation_editor_cb (gpointer user_data) +{ + MonitorACInfo *info = (MonitorACInfo *) user_data; + NMDevice *device = info->device; + NMActiveConnection *ac = info->ac; + NMActiveConnectionState ac_state; + NMDeviceState dev_state; + + if (!device || !ac) + goto finish; + + ac_state = nm_active_connection_get_state (ac); + dev_state = nm_device_get_state (device); + + nmc_terminal_show_progress (nmc_device_state_to_string (dev_state)); + + if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED + || dev_state == NM_DEVICE_STATE_ACTIVATED) { + nmc_terminal_erase_line (); + g_print (_("Connection successfully activated (D-Bus active path: %s)\n"), + nm_object_get_path (NM_OBJECT (ac))); + goto finish; /* we are done */ + } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED + || dev_state == NM_DEVICE_STATE_FAILED) { + nmc_terminal_erase_line (); + g_print (_("Error: Connection activation failed.\n")); + goto finish; /* we are done */ + } + + return TRUE; + +finish: + if (device) + g_object_unref (device); + if (ac) + g_object_unref (ac); + return FALSE; +} + +static void +activate_connection_editor_cb (GObject *client, + GAsyncResult *result, + gpointer user_data) +{ + ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data; + NMDevice *device = info->device; + const GPtrArray *ac_devs; + MonitorACInfo *monitor_ac_info = NULL; + NMActiveConnection *active; + GError *error = NULL; + + active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error); + + if (!error) { + if (!device) { + ac_devs = nm_active_connection_get_devices (active); + device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL; + } + if (device) { + monitor_ac_info = g_malloc0 (sizeof (AddConnectionInfo)); + monitor_ac_info->device = g_object_ref (device); + monitor_ac_info->ac = active; + monitor_ac_info->monitor_id = g_timeout_add (120, progress_activation_editor_cb, monitor_ac_info); + } else + g_object_unref (active); + } + set_info_and_signal_editor_thread (error, monitor_ac_info); + g_clear_error (&error); +} + +/*----------------------------------------------------------------------------*/ + +static void +print_property_description (NMSetting *setting, const char *prop_name) +{ + char *desc; + + desc = nmc_setting_get_property_desc (setting, prop_name); + g_print ("\n=== [%s] ===\n%s\n", prop_name, desc); + g_free (desc); +} + +static void +print_setting_description (NMSetting *setting) +{ + /* Show description of all properties */ + char **all_props; + int i; + + all_props = nmc_setting_get_valid_properties (setting); + g_print (("<<< %s >>>\n"), nm_setting_get_name (setting)); + for (i = 0; all_props && all_props[i]; i++) + print_property_description (setting, all_props[i]); + g_strfreev (all_props); +} + +static gboolean +connection_remove_setting (NMConnection *connection, NMSetting *setting) +{ + gboolean mandatory; + + g_return_val_if_fail (setting, FALSE); + + mandatory = is_setting_mandatory (connection, setting); + if (!mandatory) { + nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting)); + return TRUE; + } + g_print (_("Error: setting '%s' is mandatory and cannot be removed.\n"), + nm_setting_get_name (setting)); + return FALSE; +} + +static void +editor_show_status_line (NMConnection *connection, gboolean dirty, gboolean temp) +{ + NMSettingConnection *s_con; + const char *con_type, *con_id, *con_uuid; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + con_id = nm_connection_get_id (connection); + con_uuid = nm_connection_get_uuid (connection); + + /* TRANSLATORS: status line in nmcli connection editor */ + g_print (_("[ Type: %s | Name: %s | UUID: %s | Dirty: %s | Temp: %s ]\n"), + con_type, con_id, con_uuid, + dirty ? _("yes") : _("no"), + temp ? _("yes") : _("no")); +} + +static gboolean +refresh_remote_connection (GWeakRef *weak, NMRemoteConnection **remote) +{ + gboolean previous; + + g_return_val_if_fail (remote != NULL, FALSE); + + previous = (*remote != NULL); + if (*remote) + g_object_unref (*remote); + *remote = g_weak_ref_get (weak); + + return (previous && !*remote); +} + +static gboolean +is_connection_dirty (NMConnection *connection, NMRemoteConnection *remote) +{ + return !nm_connection_compare (connection, + remote ? NM_CONNECTION (remote) : NULL, + NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS | + NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP); +} + +static gboolean +confirm_quit (void) +{ + char *answer; + gboolean want_quit = FALSE; + + answer = nmc_readline (_("The connection is not saved. " + "Do you really want to quit? %s"), + prompt_yes_no (FALSE, NULL)); + answer = answer ? g_strstrip (answer) : NULL; + if (answer && matches (answer, WORD_LOC_YES) == 0) + want_quit = TRUE; + + g_free (answer); + return want_quit; +} + +/* + * Submenu for detailed property editing + * Return: TRUE - continue; FALSE - should quit + */ +static gboolean +property_edit_submenu (NmCli *nmc, + NMConnection *connection, + NMRemoteConnection **rem_con, + GWeakRef *rem_con_weak, + NMSetting *curr_setting, + const char *prop_name) +{ + NmcEditorSubCmd cmdsub; + gboolean cmd_property_loop = TRUE; + gboolean should_quit = FALSE; + char *prop_val_user; + gboolean set_result; + GError *tmp_err = NULL; + char *prompt; + gboolean dirty; + GValue prop_g_value = G_VALUE_INIT; + gboolean temp_changes; + gboolean removed; + + prompt = nmc_colorize (nmc->editor_prompt_color, "nmcli %s.%s> ", + nm_setting_get_name (curr_setting), prop_name); + + while (cmd_property_loop) { + char *cmd_property_user; + char *cmd_property_arg; + + /* Get the remote connection again, it may have disapeared */ + removed = refresh_remote_connection (rem_con_weak, rem_con); + if (removed) + g_print (_("The connection profile has been removed from another client. " + "You may type 'save' in the main menu to restore it.\n")); + + /* Connection is dirty? (not saved or differs from the saved) */ + dirty = is_connection_dirty (connection, *rem_con); + temp_changes = *rem_con ? nm_remote_connection_get_unsaved (*rem_con) : TRUE; + if (nmc->editor_status_line) + editor_show_status_line (connection, dirty, temp_changes); + + cmd_property_user = nmc_readline ("%s", prompt); + if (!cmd_property_user || *cmd_property_user == '\0') + continue; + cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg); + + switch (cmdsub) { + case NMC_EDITOR_SUB_CMD_SET: + case NMC_EDITOR_SUB_CMD_ADD: + /* list, arrays,...: SET replaces the whole property value + * ADD adds the new value(s) + * single values: : both SET and ADD sets the new value + */ + if (!cmd_property_arg) + prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name); + else + prop_val_user = g_strdup (cmd_property_arg); + + /* nmc_setting_set_property() only adds new value, thus we have to + * remove the original value and save it for error cases. + */ + if (cmdsub == NMC_EDITOR_SUB_CMD_SET) { + nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value); + nmc_property_set_default_value (curr_setting, prop_name); + } + + set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err); + g_free (prop_val_user); + if (!set_result) { + g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message); + g_clear_error (&tmp_err); + if (cmdsub == NMC_EDITOR_SUB_CMD_SET) { + /* Block change signals and restore original value */ + g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL); + nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value); + g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL); + } + } + if (G_IS_VALUE (&prop_g_value)) + g_value_unset (&prop_g_value); + break; + + case NMC_EDITOR_SUB_CMD_CHANGE: + rl_startup_hook = nmc_rl_set_deftext; + nmc_rl_pre_input_deftext = nmc_setting_get_property_out2in (curr_setting, prop_name, NULL); + prop_val_user = nmc_readline (_("Edit '%s' value: "), prop_name); + + nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value); + nmc_property_set_default_value (curr_setting, prop_name); + + if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) { + g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message); + g_clear_error (&tmp_err); + g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL); + nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value); + g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL); + } + g_free (prop_val_user); + if (G_IS_VALUE (&prop_g_value)) + g_value_unset (&prop_g_value); + break; + + case NMC_EDITOR_SUB_CMD_REMOVE: + if (cmd_property_arg) { + unsigned long val_int = G_MAXUINT32; + char *option = NULL; + + if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int)) + option = g_strdup (cmd_property_arg); + + if (!nmc_setting_remove_property_option (curr_setting, prop_name, + option ? g_strstrip (option) : NULL, + (guint32) val_int, + &tmp_err)) { + g_print (_("Error: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + } + g_free (option); + } else { + if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) { + g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name, + tmp_err->message); + g_clear_error (&tmp_err); + } + } + break; + + case NMC_EDITOR_SUB_CMD_DESCRIBE: + /* Show property description */ + print_property_description (curr_setting, prop_name); + break; + + case NMC_EDITOR_SUB_CMD_PRINT: + /* Print current connection settings/properties */ + if (cmd_property_arg) { + if (matches (cmd_property_arg, "setting") == 0) + editor_show_setting (curr_setting, nmc); + else if ( matches (cmd_property_arg, "connection") == 0 + || matches (cmd_property_arg, "all") == 0) + editor_show_connection (connection, nmc); + else + g_print (_("Unknown command argument: '%s'\n"), cmd_property_arg); + } else { + char *prop_val = nmc_setting_get_property (curr_setting, prop_name, NULL); + g_print ("%s: %s\n", prop_name, prop_val); + g_free (prop_val); + } + break; + + case NMC_EDITOR_SUB_CMD_BACK: + cmd_property_loop = FALSE; + break; + + case NMC_EDITOR_SUB_CMD_HELP: + editor_sub_usage (cmd_property_arg); + break; + + case NMC_EDITOR_SUB_CMD_QUIT: + if (is_connection_dirty (connection, *rem_con)) { + if (confirm_quit ()) { + cmd_property_loop = FALSE; + should_quit = TRUE; /* we will quit nmcli */ + } + } else { + cmd_property_loop = FALSE; + should_quit = TRUE; /* we will quit nmcli */ + } + break; + + case NMC_EDITOR_SUB_CMD_UNKNOWN: + default: + g_print (_("Unknown command: '%s'\n"), cmd_property_user); + break; + } + g_free (cmd_property_user); + g_free (cmd_property_arg); + } + g_free (prompt); + + return !should_quit; +} + +/* + * Split 'str' in the following format: [[[setting.]property] [value]] + * and return the components in 'setting', 'property' and 'value' + * Use g_free() to deallocate the returned strings. + */ +static void +split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value) +{ + char **args, **items; + + if (!str) + return; + + args = nmc_strsplit_set (str, " \t", 2); + if (args[0]) { + items = nmc_strsplit_set (args[0], ".", 2); + if (g_strv_length (items) == 2) { + if (setting) + *setting = g_strdup (items[0]); + if (property) + *property = g_strdup (items[1]); + } else { + if (property) + *property = g_strdup (items[0]); + } + g_strfreev (items); + + if (value && args[1]) + *value = g_strstrip (g_strdup (args[1])); + } + g_strfreev (args); +} + +static NMSetting * +is_setting_valid (NMConnection *connection, const NameItem *valid_settings, char *setting) +{ + const char *setting_name; + + if (!(setting_name = check_valid_name (setting, valid_settings, NULL))) + return NULL; + return nm_connection_get_setting_by_name (connection, setting_name); +} + +static char * +is_property_valid (NMSetting *setting, const char *property, GError **error) +{ + char **valid_props = NULL; + const char *prop_name; + char *ret; + + valid_props = nmc_setting_get_valid_properties (setting); + prop_name = nmc_string_is_valid (property, (const char **) valid_props, error); + ret = g_strdup (prop_name); + g_strfreev (valid_props); + return ret; +} + +static NMSetting * +create_setting_by_name (const char *name, const NameItem *valid_settings) +{ + const char *setting_name; + NMSetting *setting = NULL; + + /* Get a valid setting name */ + setting_name = check_valid_name (name, valid_settings, NULL); + + if (setting_name) { + setting = nmc_setting_new_for_name (setting_name); + if (!setting) + return NULL; /* This should really not happen */ + nmc_setting_custom_init (setting); + } + return setting; +} + +static const char * +ask_check_setting (const char *arg, + const NameItem *valid_settings_arr, + const char *valid_settings_str) +{ + char *setting_name_user; + const char *setting_name; + GError *err = NULL; + + if (!arg) { + g_print (_("Available settings: %s\n"), valid_settings_str); + setting_name_user = nmc_readline (EDITOR_PROMPT_SETTING); + } else + setting_name_user = g_strdup (arg); + + if (setting_name_user) + g_strstrip (setting_name_user); + + if (!(setting_name = check_valid_name (setting_name_user, valid_settings_arr, &err))) { + g_print (_("Error: invalid setting name; %s\n"), err->message); + g_clear_error (&err); + } + g_free (setting_name_user); + return setting_name; +} + +static const char * +ask_check_property (const char *arg, + const char **valid_props, + const char *valid_props_str) +{ + char *prop_name_user; + const char *prop_name; + GError *tmp_err = NULL; + + if (!arg) { + g_print (_("Available properties: %s\n"), valid_props_str); + prop_name_user = nmc_readline (EDITOR_PROMPT_PROPERTY); + if (prop_name_user) + g_strstrip (prop_name_user); + } else + prop_name_user = g_strdup (arg); + + if (!(prop_name = nmc_string_is_valid (prop_name_user, valid_props, &tmp_err))) { + g_print (_("Error: property %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + } + g_free (prop_name_user); + return prop_name; +} + +/* Copy timestamp from src do dst */ +static void +update_connection_timestamp (NMConnection *src, NMConnection *dst) +{ + NMSettingConnection *s_con_src, *s_con_dst; + + s_con_src = nm_connection_get_setting_connection (src); + s_con_dst = nm_connection_get_setting_connection (dst); + if (s_con_src && s_con_dst) { + guint64 timestamp = nm_setting_connection_get_timestamp (s_con_src); + g_object_set (s_con_dst, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL); + } +} + +static gboolean +confirm_connection_saving (NMConnection *local, NMConnection *remote) +{ + NMSettingConnection *s_con_loc, *s_con_rem; + gboolean ac_local, ac_remote; + gboolean confirmed = TRUE; + + s_con_loc = nm_connection_get_setting_connection (local); + g_assert (s_con_loc); + ac_local = nm_setting_connection_get_autoconnect (s_con_loc); + + if (remote) { + s_con_rem = nm_connection_get_setting_connection (remote); + g_assert (s_con_rem); + ac_remote = nm_setting_connection_get_autoconnect (s_con_rem); + } else + ac_remote = FALSE; + + if (ac_local && !ac_remote) { + char *answer; + answer = nmc_readline (_("Saving the connection with 'autoconnect=yes'. " + "That might result in an immediate activation of the connection.\n" + "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL)); + answer = answer ? g_strstrip (answer) : NULL; + if (!answer || matches (answer, WORD_LOC_YES) == 0) + confirmed = TRUE; + else + confirmed = FALSE; + g_free (answer); + } + return confirmed; +} + +typedef struct { + guint level; + char *main_prompt; + NMSetting *curr_setting; + char **valid_props; + char *valid_props_str; +} NmcEditorMenuContext; + +static void +menu_switch_to_level0 (NmcEditorMenuContext *menu_ctx, + const char *prompt, + NmcTermColor prompt_color) +{ + menu_ctx->level = 0; + g_free (menu_ctx->main_prompt); + menu_ctx->main_prompt = nmc_colorize (prompt_color, "%s", prompt); + menu_ctx->curr_setting = NULL; + g_strfreev (menu_ctx->valid_props); + menu_ctx->valid_props = NULL; + g_free (menu_ctx->valid_props_str); + menu_ctx->valid_props_str = NULL; +} + +static void +menu_switch_to_level1 (NmcEditorMenuContext *menu_ctx, + NMSetting *setting, + const char *setting_name, + NmcTermColor prompt_color) +{ + menu_ctx->level = 1; + g_free (menu_ctx->main_prompt); + menu_ctx->main_prompt = nmc_colorize (prompt_color, "nmcli %s> ", setting_name); + menu_ctx->curr_setting = setting; + g_strfreev (menu_ctx->valid_props); + menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting); + g_free (menu_ctx->valid_props_str); + menu_ctx->valid_props_str = g_strjoinv (", ", menu_ctx->valid_props); +} + +static gboolean +editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type) +{ + NMRemoteConnection *rem_con; + NMRemoteConnection *con_tmp; + GWeakRef weak = { { NULL } }; + gboolean removed; + NmcEditorMainCmd cmd; + char *cmd_user; + gboolean cmd_loop = TRUE; + char *cmd_arg = NULL; + char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v; + const char *BASE_PROMPT = "nmcli> "; + const NameItem *valid_settings_arr = NULL; + char *valid_settings_str = NULL; + AddConnectionInfo *info = NULL; + gboolean dirty; + gboolean temp_changes; + GError *err1 = NULL; + NmcEditorMenuContext menu_ctx; + + valid_settings_arr = get_valid_settings_array (connection_type); + valid_settings_str = get_valid_options_string (valid_settings_arr); + g_print (_("You may edit the following settings: %s\n"), valid_settings_str); + + menu_ctx.level = 0; + menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, BASE_PROMPT); + menu_ctx.curr_setting = NULL; + menu_ctx.valid_props = NULL; + menu_ctx.valid_props_str = NULL; + + /* Get remote connection */ + con_tmp = nm_client_get_connection_by_uuid (nmc->client, + nm_connection_get_uuid (connection)); + g_weak_ref_init (&weak, con_tmp); + rem_con = g_weak_ref_get (&weak); + + while (cmd_loop) { + /* Connection is dirty? (not saved or differs from the saved) */ + dirty = is_connection_dirty (connection, rem_con); + temp_changes = rem_con ? nm_remote_connection_get_unsaved (rem_con) : TRUE; + if (nmc->editor_status_line) + editor_show_status_line (connection, dirty, temp_changes); + + /* Read user input */ + cmd_user = nmc_readline ("%s", menu_ctx.main_prompt); + + /* Get the remote connection again, it may have disapeared */ + removed = refresh_remote_connection (&weak, &rem_con); + if (removed) + g_print (_("The connection profile has been removed from another client. " + "You may type 'save' to restore it.\n")); + + if (!cmd_user || *cmd_user == '\0') + continue; + cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg); + + cmd_arg_s = NULL; + cmd_arg_p = NULL; + cmd_arg_v = NULL; + split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v); + switch (cmd) { + case NMC_EDITOR_MAIN_CMD_SET: + /* Set property value */ + if (!cmd_arg) { + if (menu_ctx.level == 1) { + const char *prop_name; + char *prop_val_user = NULL; + const char *avals; + GError *tmp_err = NULL; + + prop_name = ask_check_property (cmd_arg, + (const char **) menu_ctx.valid_props, + menu_ctx.valid_props_str); + if (!prop_name) + break; + + avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name); + if (avals) + g_print (_("Allowed values for '%s' property: %s\n"), prop_name, avals); + + prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name); + + /* Set property value */ + if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) { + g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message); + g_clear_error (&tmp_err); + } + } else { + g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str); + g_print (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n")); + } + } else { + NMSetting *ss = NULL; + gboolean created_ss = FALSE; + char *prop_name; + GError *tmp_err = NULL; + + if (cmd_arg_s) { + /* setting provided as "setting.property" */ + ss = is_setting_valid (connection, valid_settings_arr, cmd_arg_s); + if (!ss) { + ss = create_setting_by_name (cmd_arg_s, valid_settings_arr); + if (!ss) { + g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"), + cmd_arg_s, valid_settings_str); + break; + } + created_ss = TRUE; + } + } else { + if (menu_ctx.curr_setting) + ss = menu_ctx.curr_setting; + else { + g_print (_("Error: missing setting for '%s' property\n"), cmd_arg_p); + break; + } + } + + prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err); + if (!prop_name) { + g_print (_("Error: invalid property: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + if (created_ss) + g_object_unref (ss); + break; + } + + + + /* Ask for value */ + if (!cmd_arg_v) { + const char *avals = nmc_setting_get_property_allowed_values (ss, prop_name); + if (avals) + g_print (_("Allowed values for '%s' property: %s\n"), prop_name, avals); + + cmd_arg_v = nmc_readline (_("Enter '%s' value: "), prop_name); + } + + /* Set property value */ + if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) { + g_print (_("Error: failed to set '%s' property: %s\n"), + prop_name, tmp_err->message); + g_clear_error (&tmp_err); + } + + if (created_ss) + nm_connection_add_setting (connection, ss); + g_free (prop_name); + } + break; + + case NMC_EDITOR_MAIN_CMD_GOTO: + /* cmd_arg_s != NULL means 'setting.property' argument */ + if (menu_ctx.level == 0 || cmd_arg_s) { + /* in top level - no setting selected yet */ + const char *setting_name; + NMSetting *setting; + const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p; + + setting_name = ask_check_setting (user_arg, valid_settings_arr, valid_settings_str); + if (!setting_name) + break; + + setting = nm_connection_get_setting_by_name (connection, setting_name); + if (!setting) { + setting = nmc_setting_new_for_name (setting_name); + if (!setting) { + g_print (_("Error: unknown setting '%s'\n"), setting_name); + break; + } + nmc_setting_custom_init (setting); + nm_connection_add_setting (connection, setting); + } + /* Set global variable for use in TAB completion */ + nmc_tab_completion.setting = setting; + + /* Switch to level 1 */ + menu_switch_to_level1 (&menu_ctx, setting, setting_name, nmc->editor_prompt_color); + + if (!cmd_arg_s) { + g_print (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str); + break; + } + } + if (menu_ctx.level == 1 || cmd_arg_s) { + /* level 1 - setting selected */ + const char *prop_name; + + prop_name = ask_check_property (cmd_arg_p, + (const char **) menu_ctx.valid_props, + menu_ctx.valid_props_str); + if (!prop_name) + break; + + /* submenu - level 2 - editing properties */ + cmd_loop = property_edit_submenu (nmc, + connection, + &rem_con, + &weak, + menu_ctx.curr_setting, + prop_name); + } + break; + + case NMC_EDITOR_MAIN_CMD_REMOVE: + /* Remove setting from connection, or delete value of a property */ + if (!cmd_arg) { + if (menu_ctx.level == 1) { + GError *tmp_err = NULL; + const char *prop_name; + + prop_name = ask_check_property (cmd_arg, + (const char **) menu_ctx.valid_props, + menu_ctx.valid_props_str); + if (!prop_name) + break; + + /* Delete property value */ + if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) { + g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name, + tmp_err->message); + g_clear_error (&tmp_err); + } + } else + g_print (_("Error: no argument given; valid are [%s]\n"), valid_settings_str); + } else { + NMSetting *ss = NULL; + gboolean descr_all; + char *user_s; + + /* cmd_arg_s != NULL means argument is "setting.property" */ + descr_all = !cmd_arg_s && !menu_ctx.curr_setting; + user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL; + if (user_s) { + ss = is_setting_valid (connection, valid_settings_arr, user_s); + if (!ss) { + if (check_valid_name (user_s, valid_settings_arr, NULL)) + g_print (_("Setting '%s' is not present in the connection.\n"), user_s); + else + g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"), + user_s, valid_settings_str); + break; + } + } else + ss = menu_ctx.curr_setting; + + if (descr_all) { + /* Remove setting from the connection */ + connection_remove_setting (connection, ss); + if (ss == menu_ctx.curr_setting) { + /* If we removed the setting we are in, go up */ + menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color); + nmc_tab_completion.setting = NULL; /* for TAB completion */ + } + } else { + GError *tmp_err = NULL; + char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err); + if (prop_name) { + /* Delete property value */ + if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) { + g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name, + tmp_err->message); + g_clear_error (&tmp_err); + } + } else { + /* If the string is not a property, try it as a setting */ + NMSetting *s_tmp; + s_tmp = is_setting_valid (connection, valid_settings_arr, cmd_arg_p); + if (s_tmp) { + /* Remove setting from the connection */ + connection_remove_setting (connection, s_tmp); + /* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */ + if (ss == menu_ctx.curr_setting) { + /* If we removed the setting we are in, go up */ + menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color); + nmc_tab_completion.setting = NULL; /* for TAB completion */ + } + } else + g_print (_("Error: %s properties, nor it is a setting name.\n"), + tmp_err->message); + g_clear_error (&tmp_err); + } + g_free (prop_name); + } + } + break; + + case NMC_EDITOR_MAIN_CMD_DESCRIBE: + /* Print property description */ + if (!cmd_arg) { + if (menu_ctx.level == 1) { + const char *prop_name; + + prop_name = ask_check_property (cmd_arg, + (const char **) menu_ctx.valid_props, + menu_ctx.valid_props_str); + if (!prop_name) + break; + + /* Show property description */ + print_property_description (menu_ctx.curr_setting, prop_name); + } else { + g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str); + g_print (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n")); + } + } else { + NMSetting *ss = NULL; + gboolean unref_ss = FALSE; + gboolean descr_all; + char *user_s; + + /* cmd_arg_s != NULL means argument is "setting.property" */ + descr_all = !cmd_arg_s && !menu_ctx.curr_setting; + user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL; + if (user_s) { + ss = is_setting_valid (connection, valid_settings_arr, user_s); + if (!ss) { + ss = create_setting_by_name (user_s, valid_settings_arr); + if (!ss) { + g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"), + user_s, valid_settings_str); + break; + } + unref_ss = TRUE; + } + } else + ss = menu_ctx.curr_setting; + + if (descr_all) { + /* Show description for all properties */ + print_setting_description (ss); + } else { + GError *tmp_err = NULL; + char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err); + if (prop_name) { + /* Show property description */ + print_property_description (ss, prop_name); + } else { + /* If the string is not a property, try it as a setting */ + NMSetting *s_tmp; + s_tmp = is_setting_valid (connection, valid_settings_arr, cmd_arg_p); + if (s_tmp) + print_setting_description (s_tmp); + else + g_print (_("Error: invalid property: %s, " + "neither a valid setting name.\n"), + tmp_err->message); + g_clear_error (&tmp_err); + } + g_free (prop_name); + } + if (unref_ss) + g_object_unref (ss); + } + break; + + case NMC_EDITOR_MAIN_CMD_PRINT: + /* Print current connection settings/properties */ + if (cmd_arg) { + if (strcmp (cmd_arg, "all") == 0) + editor_show_connection (connection, nmc); + else { + NMSetting *ss = NULL; + gboolean whole_setting; + char *user_s; + + /* cmd_arg_s != NULL means argument is "setting.property" */ + whole_setting = !cmd_arg_s && !menu_ctx.curr_setting; + user_s = whole_setting ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL; + if (user_s) { + const char *s_name; + s_name = check_valid_name (user_s, valid_settings_arr, NULL); + if (!s_name) { + g_print (_("Error: unknown setting: '%s'\n"), user_s); + break; + } + ss = nm_connection_get_setting_by_name (connection, s_name); + if (!ss) { + g_print (_("Error: '%s' setting not present in the connection\n"), s_name); + break; + } + } else + ss = menu_ctx.curr_setting; + + if (whole_setting) { + /* Print the whole setting */ + editor_show_setting (ss, nmc); + } else { + GError *err = NULL; + char *prop_name = is_property_valid (ss, cmd_arg_p, &err); + if (prop_name) { + /* Print one property */ + char *prop_val = nmc_setting_get_property (ss, prop_name, NULL); + g_print ("%s.%s: %s\n", nm_setting_get_name (ss),prop_name , prop_val); + g_free (prop_val); + } else { + /* If the string is not a property, try it as a setting */ + NMSetting *s_tmp; + s_tmp = is_setting_valid (connection, valid_settings_arr, cmd_arg_p); + if (s_tmp) { + /* Print the whole setting */ + editor_show_setting (s_tmp, nmc); + } else + g_print (_("Error: invalid property: %s%s\n"), + err->message, + cmd_arg_s ? "" : _(", neither a valid setting name")); + g_clear_error (&err); + } + g_free (prop_name); + } + } + } else { + if (menu_ctx.curr_setting) + editor_show_setting (menu_ctx.curr_setting, nmc); + else + editor_show_connection (connection, nmc); + } + break; + + case NMC_EDITOR_MAIN_CMD_VERIFY: + /* Verify current setting or the whole connection */ + if (cmd_arg && strcmp (cmd_arg, "all") && strcmp (cmd_arg, "fix")) { + g_print (_("Invalid verify option: %s\n"), cmd_arg); + break; + } + + if ( menu_ctx.curr_setting + && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) { + GError *tmp_err = NULL; + (void) nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err); + g_print (_("Verify setting '%s': %s\n"), + nm_setting_get_name (menu_ctx.curr_setting), + tmp_err ? tmp_err->message : "OK"); + g_clear_error (&tmp_err); + } else { + GError *tmp_err = NULL; + gboolean valid, modified; + gboolean fixed = TRUE; + + valid = nm_connection_verify (connection, &tmp_err); + if (!valid && (g_strcmp0 (cmd_arg, "fix") == 0)) { + /* Try to fix normalizable errors */ + g_clear_error (&tmp_err); + fixed = nm_connection_normalize (connection, NULL, &modified, &tmp_err); + } + g_print (_("Verify connection: %s\n"), + tmp_err ? tmp_err->message : "OK"); + if (!fixed) + g_print (_("The error cannot be fixed automatically.\n")); + g_clear_error (&tmp_err); + } + break; + + case NMC_EDITOR_MAIN_CMD_SAVE: + /* Save the connection */ + if (nm_connection_verify (connection, &err1)) { + gboolean persistent = TRUE; + + /* parse argument */ + if (cmd_arg) { + if (matches (cmd_arg, "temporary") == 0) + persistent = FALSE; + else if (matches (cmd_arg, "persistent") == 0) + persistent = TRUE; + else { + g_print (_("Error: invalid argument '%s'\n"), cmd_arg); + break; + } + } + + /* Ask for save confirmation if the connection changes to autoconnect=yes */ + if (nmc->editor_save_confirmation) + if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con))) + break; + + if (!rem_con) { + /* Tell the settings service to add the new connection */ + info = g_malloc0 (sizeof (AddConnectionInfo)); + info->nmc = nmc; + info->con_name = g_strdup (nm_connection_get_id (connection)); + add_new_connection (persistent, + nmc->client, + connection, + add_connection_editor_cb, + info); + } else { + /* Save/update already saved (existing) connection */ + nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con), + connection); + update_connection (persistent, rem_con, update_connection_editor_cb, NULL); + } + + g_mutex_lock (&nmc_editor_mutex); + //FIXME: add also a timeout for cases the callback is not called + while (!nmc_editor_cb_called) + g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex); + + if (nmc_editor_error) { + g_print (_("Error: Failed to save '%s' (%s) connection: %s\n"), + nm_connection_get_id (connection), + nm_connection_get_uuid (connection), + nmc_editor_error->message); + g_error_free (nmc_editor_error); + } else { + g_print (!rem_con ? + _("Connection '%s' (%s) successfully saved.\n") : + _("Connection '%s' (%s) successfully updated.\n"), + nm_connection_get_id (connection), + nm_connection_get_uuid (connection)); + + con_tmp = nm_client_get_connection_by_uuid (nmc->client, + nm_connection_get_uuid (connection)); + g_weak_ref_set (&weak, con_tmp); + refresh_remote_connection (&weak, &rem_con); + + /* Replace local connection with the remote one to be sure they are equal. + * This mitigates problems with plugins not preserving some properties or + * adding ipv{4,6} settings when not present. + */ + if (con_tmp) { + char *s_name = NULL; + if (menu_ctx.curr_setting) + s_name = g_strdup (nm_setting_get_name (menu_ctx.curr_setting)); + + /* Update settings in the local connection */ + nm_connection_replace_settings_from_connection (connection, + NM_CONNECTION (con_tmp)); + + /* Also update setting for menu context and TAB-completion */ + menu_ctx.curr_setting = s_name ? nm_connection_get_setting_by_name (connection, s_name) : NULL; + nmc_tab_completion.setting = menu_ctx.curr_setting; + g_free (s_name); + } + } + + nmc_editor_cb_called = FALSE; + nmc_editor_error = NULL; + g_mutex_unlock (&nmc_editor_mutex); + } else { + g_print (_("Error: connection verification failed: %s\n"), + err1 ? err1->message : _("(unknown error)")); + g_print (_("You may try running 'verify fix' to fix errors.\n")); + } + + g_clear_error (&err1); + break; + + case NMC_EDITOR_MAIN_CMD_ACTIVATE: + { + GError *tmp_err = NULL; + const char *ifname = cmd_arg_p; + const char *ap_nsp = cmd_arg_v; + + /* When only AP/NSP is specified it is prepended with '/' */ + if (!cmd_arg_v) { + if (ifname && ifname[0] == '/') { + ap_nsp = ifname + 1; + ifname = NULL; + } + } else + ap_nsp = ap_nsp && ap_nsp[0] == '/' ? ap_nsp + 1 : ap_nsp; + + if (is_connection_dirty (connection, rem_con)) { + g_print (_("Error: connection is not saved. Type 'save' first.\n")); + break; + } + if (!nm_connection_verify (NM_CONNECTION (rem_con), &tmp_err)) { + g_print (_("Error: connection is not valid: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + break; + } + + nmc->nowait_flag = FALSE; + nmc->should_wait = TRUE; + nmc->print_output = NMC_PRINT_PRETTY; + if (!nmc_activate_connection (nmc, NM_CONNECTION (rem_con), ifname, ap_nsp, ap_nsp, NULL, + activate_connection_editor_cb, &tmp_err)) { + g_print (_("Error: Cannot activate connection: %s.\n"), tmp_err->message); + g_clear_error (&tmp_err); + break; + } + + g_mutex_lock (&nmc_editor_mutex); + while (!nmc_editor_cb_called) + g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex); + + if (nmc_editor_error) { + g_print (_("Error: Failed to activate '%s' (%s) connection: %s\n"), + nm_connection_get_id (connection), + nm_connection_get_uuid (connection), + nmc_editor_error->message); + g_error_free (nmc_editor_error); + } else { + g_print (_("Monitoring connection activation (press any key to continue)\n")); + nmc_get_user_input (""); + } + + if (nmc_editor_monitor_ac) { + if (nmc_editor_monitor_ac->monitor_id) + g_source_remove (nmc_editor_monitor_ac->monitor_id); + g_free (nmc_editor_monitor_ac); + } + nmc_editor_cb_called = FALSE; + nmc_editor_error = NULL; + nmc_editor_monitor_ac = NULL; + g_mutex_unlock (&nmc_editor_mutex); + + /* Update timestamp in local connection */ + update_connection_timestamp (NM_CONNECTION (rem_con), connection); + + } + break; + + case NMC_EDITOR_MAIN_CMD_BACK: + /* Go back (up) an the menu */ + if (menu_ctx.level == 1) { + menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color); + nmc_tab_completion.setting = NULL; /* for TAB completion */ + } + break; + + case NMC_EDITOR_MAIN_CMD_HELP: + /* Print command help */ + editor_main_help (cmd_arg); + break; + + case NMC_EDITOR_MAIN_CMD_NMCLI: + if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) { + GError *tmp_err = NULL; + gboolean bb; + if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) { + g_print (_("Error: status-line: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + } else + nmc->editor_status_line = bb; + } else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) { + GError *tmp_err = NULL; + gboolean bb; + if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) { + g_print (_("Error: save-confirmation: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + } else + nmc->editor_save_confirmation = bb; + } else if (cmd_arg_p && matches (cmd_arg_p, "show-secrets") == 0) { + GError *tmp_err = NULL; + gboolean bb; + if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) { + g_print (_("Error: show-secrets: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + } else + nmc->editor_show_secrets = bb; + } else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) { + unsigned long color; + if (!nmc_string_to_uint (cmd_arg_v ? g_strstrip (cmd_arg_v) : "X", + TRUE, 0, 8, &color)) + g_print (_("Error: bad color number: '%s'; use <0-8>\n"), + cmd_arg_v ? cmd_arg_v : ""); + else { + nmc->editor_prompt_color = color; + g_free (menu_ctx.main_prompt); + if (menu_ctx.level == 0) + menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, BASE_PROMPT); + else + menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, "nmcli %s> ", + nm_setting_get_name (menu_ctx.curr_setting)); + } + } else if (!cmd_arg_p) { + g_print (_("Current nmcli configuration:\n")); + g_print ("status-line: %s\n" + "save-confirmation: %s\n" + "show-secrets: %s\n" + "prompt-color: %d\n", + nmc->editor_status_line ? "yes" : "no", + nmc->editor_save_confirmation ? "yes" : "no", + nmc->editor_show_secrets ? "yes" : "no", + nmc->editor_prompt_color); + } else + g_print (_("Invalid configuration option '%s'; allowed [%s]\n"), + cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, show-secrets, prompt-color"); + + break; + + case NMC_EDITOR_MAIN_CMD_QUIT: + if (is_connection_dirty (connection, rem_con)) { + if (confirm_quit ()) + cmd_loop = FALSE; /* quit command loop */ + } else + cmd_loop = FALSE; /* quit command loop */ + break; + + case NMC_EDITOR_MAIN_CMD_UNKNOWN: + default: + g_print (_("Unknown command: '%s'\n"), cmd_user); + break; + } + + g_free (cmd_user); + g_free (cmd_arg); + g_free (cmd_arg_s); + g_free (cmd_arg_p); + g_free (cmd_arg_v); + } + g_free (valid_settings_str); + g_free (menu_ctx.main_prompt); + g_strfreev (menu_ctx.valid_props); + g_free (menu_ctx.valid_props_str); + if (rem_con) + g_object_unref (rem_con); + g_weak_ref_clear (&weak); + + /* Save history file */ + save_history_cmds (nm_connection_get_uuid (connection)); + + return TRUE; +} + +static const char * +get_ethernet_device_name (NmCli *nmc) +{ + const GPtrArray *devices; + int i; + + devices = nm_client_get_devices (nmc->client); + for (i = 0; i < devices->len; i++) { + NMDevice *dev = g_ptr_array_index (devices, i); + if (NM_IS_DEVICE_ETHERNET (dev)) + return nm_device_get_iface (dev); + } + return NULL; +} + +static void +editor_init_new_connection (NmCli *nmc, NMConnection *connection) +{ + NMSetting *setting, *base_setting; + NMSettingConnection *s_con; + const char *con_type; + const char *slave_type = NULL; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + + /* Initialize new connection according to its type using sensible defaults. */ + + nmc_setting_connection_connect_handlers (s_con, connection); + + if (g_strcmp0 (con_type, "bond-slave") == 0) + slave_type = NM_SETTING_BOND_SETTING_NAME; + if (g_strcmp0 (con_type, "team-slave") == 0) + slave_type = NM_SETTING_TEAM_SETTING_NAME; + if (g_strcmp0 (con_type, "bridge-slave") == 0) + slave_type = NM_SETTING_BRIDGE_SETTING_NAME; + + if (slave_type) { + const char *dev_ifname = get_ethernet_device_name (nmc); + + /* For bond/team/bridge slaves add 'wired' setting */ + setting = nm_setting_wired_new (); + nm_connection_add_setting (connection, setting); + + g_object_set (s_con, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0", + NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, + NULL); + } else { + /* Add a "base" setting to the connection by default */ + base_setting = nmc_setting_new_for_name (con_type); + if (!base_setting) + return; + nm_connection_add_setting (connection, base_setting); + + /* Set a sensible bond/team/bridge interface name by default */ + if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0) + g_object_set (s_con, + NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bond", + NULL); + if (g_strcmp0 (con_type, NM_SETTING_TEAM_SETTING_NAME) == 0) + g_object_set (s_con, + NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-team", + NULL); + if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0) + g_object_set (s_con, + NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bridge", + NULL); + + /* Set sensible initial VLAN values */ + if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) { + const char *dev_ifname = get_ethernet_device_name (nmc); + + g_object_set (NM_SETTING_VLAN (base_setting), + NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0", + NM_SETTING_VLAN_ID, 1, + NULL); + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME, + NULL); + } + + /* Initialize 'transport-mode' so that 'infiniband' is valid */ + if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0) + g_object_set (NM_SETTING_INFINIBAND (base_setting), + NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", + NULL); + + /* Initialize 'number' so that 'cdma' is valid */ + if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0) + g_object_set (NM_SETTING_CDMA (base_setting), + NM_SETTING_CDMA_NUMBER, "#777", + NULL); + + /* Initialize 'number' so that 'gsm' is valid */ + if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0) + g_object_set (NM_SETTING_GSM (base_setting), + NM_SETTING_GSM_NUMBER, "*99#", + NULL); + + /* Wi-Fi */ + if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0) { + /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL + * is regarded as "infrastructure", explicit value makes no doubts. + */ + g_object_set (NM_SETTING_WIRELESS (base_setting), + NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, + NULL); + + /* Do custom initialization for wifi setting */ + nmc_setting_custom_init (base_setting); + } + + /* Always add IPv4 and IPv6 settings for non-slave connections */ + setting = nm_setting_ip4_config_new (); + nmc_setting_custom_init (setting); + nm_connection_add_setting (connection, setting); + + setting = nm_setting_ip6_config_new (); + nmc_setting_custom_init (setting); + nm_connection_add_setting (connection, setting); + } +} + +static void +editor_init_existing_connection (NMConnection *connection) +{ + NMSettingIPConfig *s_ip4, *s_ip6; + NMSettingWireless *s_wireless; + NMSettingConnection *s_con; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + s_ip6 = nm_connection_get_setting_ip6_config (connection); + s_wireless = nm_connection_get_setting_wireless (connection); + s_con = nm_connection_get_setting_connection (connection); + + if (s_ip4) + nmc_setting_ip4_connect_handlers (s_ip4); + if (s_ip6) + nmc_setting_ip6_connect_handlers (s_ip6); + if (s_wireless) + nmc_setting_wireless_connect_handlers (s_wireless); + if (s_con) + nmc_setting_connection_connect_handlers (s_con, connection); +} + +static NMCResultCode +do_connection_edit (NmCli *nmc, int argc, char **argv) +{ + NMConnection *connection = NULL; + NMSettingConnection *s_con; + const char *connection_type; + char *uuid; + char *default_name = NULL; + const char *type = NULL; + char *type_ask = NULL; + const char *con_name = NULL; + const char *con = NULL; + const char *con_id = NULL; + const char *con_uuid = NULL; + const char *con_path = NULL; + const char *selector = NULL; + char *tmp_str; + GError *error = NULL; + GError *err1 = NULL; + nmc_arg_t exp_args[] = { {"type", TRUE, &type, FALSE}, + {"con-name", TRUE, &con_name, FALSE}, + {"id", TRUE, &con_id, FALSE}, + {"uuid", TRUE, &con_uuid, FALSE}, + {"path", TRUE, &con_path, FALSE}, + {NULL} }; + + nmc->return_value = NMC_RESULT_SUCCESS; + + if (argc == 1) + con = *argv; + else { + if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) { + g_string_assign (nmc->return_text, error->message); + nmc->return_value = error->code; + g_clear_error (&error); + goto error; + } + } + + /* Setup some readline completion stuff */ + /* Set a pointer to an alternative function to create matches */ + rl_attempted_completion_function = (rl_completion_func_t *) nmcli_editor_tab_completion; + /* Use ' ' and '.' as word break characters */ + rl_completer_word_break_characters = ". "; + + if (!con) { + if (con_id && !con_uuid && !con_path) { + con = con_id; + selector = "id"; + } else if (con_uuid && !con_id && !con_path) { + con = con_uuid; + selector = "uuid"; + } else if (con_path && !con_id && !con_uuid) { + con = con_path; + selector = "path"; + } else if (!con_path && !con_id && !con_uuid) { + /* no-op */ + } else { + g_string_printf (nmc->return_text, + _("Error: only one of 'id', uuid, or 'path' can be provided.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + + if (con) { + /* Existing connection */ + NMConnection *found_con; + + found_con = nmc_find_connection (nmc->connections, selector, con, NULL); + if (!found_con) { + g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + /* Duplicate the connection and use that so that we need not + * differentiate existing vs. new later + */ + connection = nm_simple_connection_new_clone (found_con); + + /* Merge secrets into the connection */ + update_secrets_in_connection (NM_REMOTE_CONNECTION (found_con), connection); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + connection_type = nm_setting_connection_get_connection_type (s_con); + + if (type) + g_print (_("Warning: editing existing connection '%s'; 'type' argument is ignored\n"), + nm_connection_get_id (connection)); + if (con_name) + g_print (_("Warning: editing existing connection '%s'; 'con-name' argument is ignored\n"), + nm_connection_get_id (connection)); + + /* Load previously saved history commands for the connection */ + load_history_cmds (nm_connection_get_uuid (connection)); + + editor_init_existing_connection (connection); + } else { + /* New connection */ + connection_type = check_valid_name (type, nmc_valid_connection_types, &err1); + tmp_str = get_valid_options_string (nmc_valid_connection_types); + + while (!connection_type) { + if (!type) + g_print (_("Valid connection types: %s\n"), tmp_str); + else + g_print (_("Error: invalid connection type; %s\n"), err1->message); + g_clear_error (&err1); + + type_ask = nmc_readline (EDITOR_PROMPT_CON_TYPE); + type = type_ask = type_ask ? g_strstrip (type_ask) : NULL; + connection_type = check_valid_name (type_ask, nmc_valid_connection_types, &err1); + g_free (type_ask); + } + g_free (tmp_str); + + /* Create a new connection object */ + connection = nm_simple_connection_new (); + + /* Build up the 'connection' setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + uuid = nm_utils_uuid_generate (); + if (con_name) + default_name = g_strdup (con_name); + else + default_name = unique_connection_name (nmc->connections, + get_name_alias (connection_type, nmc_valid_connection_types)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, default_name, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, connection_type, + NULL); + g_free (uuid); + g_free (default_name); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + /* Initialize the new connection so that it is valid from the start */ + editor_init_new_connection (nmc, connection); + } + + /* nmcli runs the editor */ + nmc->in_editor = TRUE; + + g_print ("\n"); + g_print (_("===| nmcli interactive connection editor |===")); + g_print ("\n\n"); + if (con) + g_print (_("Editing existing '%s' connection: '%s'"), connection_type, con); + else + g_print (_("Adding a new '%s' connection"), connection_type); + g_print ("\n\n"); + g_print (_("Type 'help' or '?' for available commands.")); + g_print ("\n"); + g_print (_("Type 'describe [<setting>.<prop>]' for detailed property description.")); + g_print ("\n\n"); + + /* Set global variables for use in TAB completion */ + nmc_tab_completion.nmc = nmc; + nmc_tab_completion.con_type = g_strdup (connection_type); + nmc_tab_completion.connection = connection; + + /* Run menu loop */ + editor_menu_main (nmc, connection, connection_type); + + if (connection) + g_object_unref (connection); + g_free (nmc_tab_completion.con_type); + + nmc->should_wait = TRUE; + return nmc->return_value; + +error: + g_assert (!connection); + g_free (type_ask); + + nmc->should_wait = FALSE; + return nmc->return_value; +} + + +static void +modify_connection_cb (GObject *connection, + GAsyncResult *result, + gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + GError *error = NULL; + + if (!nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection), + result, &error)) { + g_string_printf (nmc->return_text, + _("Error: Failed to modify connection '%s': %s"), + nm_connection_get_id (NM_CONNECTION (connection)), + error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + } else { + if (nmc->print_output == NMC_PRINT_PRETTY) + g_print (_("Connection '%s' (%s) successfully modified.\n"), + nm_connection_get_id (NM_CONNECTION (connection)), + nm_connection_get_uuid (NM_CONNECTION (connection))); + } + quit (); +} + +static NMCResultCode +do_connection_modify (NmCli *nmc, + gboolean temporary, + int argc, + char **argv) +{ + NMConnection *connection = NULL; + NMRemoteConnection *rc = NULL; + NMSetting *setting; + NMSettingConnection *s_con; + const char *con_type; + const char *name; + const char *selector = NULL; + const char *s_dot_p; + const char *value; + char **strv = NULL; + const char *setting_name; + char *property_name = NULL; + gboolean append = FALSE; + gboolean remove = FALSE; + GError *error = NULL; + + nmc->should_wait = FALSE; + + if (argc == 0) { + g_string_printf (nmc->return_text, _("Error: No arguments provided.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + if ( strcmp (*argv, "id") == 0 + || strcmp (*argv, "uuid") == 0 + || strcmp (*argv, "path") == 0) { + + selector = *argv; + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), + selector); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + name = *argv; + } + name = *argv; + if (!name) { + g_string_printf (nmc->return_text, _("Error: connection ID is missing.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + connection = nmc_find_connection (nmc->connections, selector, name, NULL); + if (!connection) { + g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto finish; + } + rc = nm_client_get_connection_by_uuid (nmc->client, + nm_connection_get_uuid (connection)); + if (!rc) { + g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto finish; + } + s_con = nm_connection_get_setting_connection (NM_CONNECTION (rc)); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + + /* Go through arguments and set properties */ + while (argc) { + s_dot_p = *argv; + next_arg (&argc, &argv); + value = *argv; + next_arg (&argc, &argv); + + if (!s_dot_p) { + g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + if (!value) { + g_string_printf (nmc->return_text, _("Error: value for '%s' is missing."), s_dot_p); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + /* Empty string will reset the value to default */ + if (value[0] == '\0') + value = NULL; + + if (s_dot_p[0] == '+') { + s_dot_p++; + append = TRUE; + } else if (s_dot_p[0] == '-') { + s_dot_p++; + remove = TRUE; + } + + strv = g_strsplit (s_dot_p, ".", 2); + if (g_strv_length (strv) != 2) { + g_string_printf (nmc->return_text, _("Error: invalid <setting>.<property> '%s'."), + s_dot_p); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + + setting_name = check_valid_name (strv[0], get_valid_settings_array (con_type), &error); + if (!setting_name) { + g_string_printf (nmc->return_text, _("Error: invalid or not allowed setting '%s': %s."), + strv[0], error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + setting = nm_connection_get_setting_by_name (NM_CONNECTION (rc), setting_name); + if (!setting) { + setting = nmc_setting_new_for_name (setting_name); + if (!setting) { + /* This should really not happen */ + g_string_printf (nmc->return_text, + "Error: don't know how to create '%s' setting.", + setting_name); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto finish; + } + nm_connection_add_setting (NM_CONNECTION (rc), setting); + } + + property_name = is_property_valid (setting, strv[1], &error); + if (!property_name) { + g_string_printf (nmc->return_text, _("Error: invalid property '%s': %s."), + strv[1], error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + + if (!remove) { + /* Set/add value */ + if (!append) + nmc_setting_reset_property (setting, property_name, NULL); + if (!nmc_setting_set_property (setting, property_name, value, &error)) { + g_string_printf (nmc->return_text, _("Error: failed to modify %s.%s: %s."), + strv[0], strv[1], error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + } else { + /* Remove value + * - either empty: remove whole value + * - or specified by index <0-n>: remove item at the index + * - or option name: remove item with the option name + */ + if (value) { + unsigned long idx; + if (nmc_string_to_uint (value, TRUE, 0, G_MAXUINT32, &idx)) + nmc_setting_remove_property_option (setting, property_name, NULL, idx, &error); + else + nmc_setting_remove_property_option (setting, property_name, value, 0, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: failed to remove a value from %s.%s: %s."), + strv[0], strv[1], error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + } else + nmc_setting_reset_property (setting, property_name, NULL); + } + + g_strfreev (strv); + strv = NULL; + } + + update_connection (!temporary, rc, modify_connection_cb, nmc); + +finish: + nmc->should_wait = (nmc->return_value == NMC_RESULT_SUCCESS); + g_free (property_name); + if (strv) + g_strfreev (strv); + g_clear_error (&error); + return nmc->return_value; +} + + +typedef struct { + NmCli *nmc; + int counter; +} DeleteStateInfo; + +static void +delete_cb (GObject *con, GAsyncResult *result, gpointer user_data) +{ + DeleteStateInfo *info = (DeleteStateInfo *) user_data; + GError *error = NULL; + + if (!nm_remote_connection_delete_finish (NM_REMOTE_CONNECTION (con), result, &error)) { + g_string_printf (info->nmc->return_text, _("Error: Connection deletion failed: %s"), + error->message); + g_error_free (error); + info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL; + } + + info->counter--; + if (info->counter == 0) { + g_free (info); + quit (); + } +} + +static NMCResultCode +do_connection_delete (NmCli *nmc, int argc, char **argv) +{ + NMConnection *connection = NULL; + DeleteStateInfo *del_info = NULL; + char *line = NULL; + char **arg_arr = NULL; + char **arg_ptr = argv; + int arg_num = argc; + GString *invalid_cons = NULL; + gboolean del_info_free = FALSE; + int pos = 0; + + nmc->return_value = NMC_RESULT_SUCCESS; + nmc->should_wait = FALSE; + + if (argc == 0) { + if (nmc->ask) { + line = nmc_readline (PROMPT_CONNECTION); + nmc_string_to_arg_array (line, "", &arg_arr, &arg_num); + arg_ptr = arg_arr; + } + if (arg_num == 0) { + g_string_printf (nmc->return_text, _("Error: No connection specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + } + + del_info = g_malloc0 (sizeof (DeleteStateInfo)); + del_info->nmc = nmc; + del_info->counter = 0; + del_info_free = TRUE; + + while (arg_num > 0) { + const char *selector = NULL; + + if ( strcmp (*arg_ptr, "id") == 0 + || strcmp (*arg_ptr, "uuid") == 0 + || strcmp (*arg_ptr, "path") == 0) { + selector = *arg_ptr; + if (next_arg (&arg_num, &arg_ptr) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + } + + connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos); + if (!connection) { + if (nmc->print_output != NMC_PRINT_TERSE) + g_print (_("Error: unknown connection: %s\n"), *arg_ptr); + + if (!invalid_cons) + invalid_cons = g_string_new (NULL); + g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr); + + /* take the next argument and continue */ + next_arg (&arg_num, &arg_ptr); + continue; + } + + /* We need to wait a bit so that nmcli's permissions can be checked. + * We will exit when D-Bus return (error) messages are received. + */ + nmc->should_wait = TRUE; + + /* del_info deallocation is handled in delete_cb() */ + del_info_free = FALSE; + + del_info->counter++; + + /* Delete the connection */ + nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (connection), + NULL, delete_cb, del_info); + + /* Take next argument (if there's no other connection of the same name) */ + if (!pos) + next_arg (&arg_num, &arg_ptr); + } + +finish: + if (del_info_free) + g_free (del_info); + g_strfreev (arg_arr); + + if (invalid_cons) { + g_string_truncate (invalid_cons, invalid_cons->len-2); /* truncate trailing ", " */ + g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."), + invalid_cons->str); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + g_string_free (invalid_cons, TRUE); + } + return nmc->return_value; +} + +static NMCResultCode +do_connection_reload (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + + nmc->return_value = NMC_RESULT_SUCCESS; + nmc->should_wait = FALSE; + + if (!nm_client_get_nm_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return nmc->return_value; + } + + if (!nm_client_reload_connections (nmc->client, NULL, &error)) { + g_string_printf (nmc->return_text, _("Error: failed to reload connections: %s."), + error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_clear_error (&error); + } + + return nmc->return_value; +} + +static NMCResultCode +do_connection_load (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + char **filenames, **failures = NULL; + int i; + + nmc->return_value = NMC_RESULT_SUCCESS; + nmc->should_wait = FALSE; + + if (!nm_client_get_nm_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return nmc->return_value; + } + + if (argc == 0) { + g_string_printf (nmc->return_text, _("Error: No connection specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + + filenames = g_new (char *, argc + 1); + for (i = 0; i < argc; i++) + filenames[i] = argv[i]; + filenames[i] = NULL; + + nm_client_load_connections (nmc->client, filenames, &failures, NULL, &error); + g_free (filenames); + if (error) { + g_string_printf (nmc->return_text, _("Error: failed to load connection: %s."), + error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_error_free (error); + } + + if (failures) { + for (i = 0; failures[i]; i++) + g_printerr (_("Could not load file '%s'\n"), failures[i]); + g_strfreev (failures); + } + + return nmc->return_value; +} + + +typedef struct { + NmCli *nmc; + int argc; + char **argv; +} NmcEditorThreadData; + +static GThread *editor_thread; +static NmcEditorThreadData editor_thread_data; + +/* + * We need to run do_connection_edit() in a thread so that + * glib main loop is not blocked and could receive and process D-Bus + * return messages. + */ +static gpointer +connection_editor_thread_func (gpointer data) +{ + NmcEditorThreadData *td = (NmcEditorThreadData *) data; + + /* run editor for editing/adding connections */ + td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv); + + /* quit glib main loop now that we are done with this thread */ + quit (); + + return NULL; +} + +static char * +gen_func_connection_names (const char *text, int state) +{ + int i; + const char **connections; + char *ret; + + if (nm_cli.connections->len == 0) + return NULL; + + connections = g_new (const char *, nm_cli.connections->len + 1); + for (i = 0; i < nm_cli.connections->len; i++) { + NMConnection *con = NM_CONNECTION (nm_cli.connections->pdata[i]); + const char *id = nm_connection_get_id (con); + connections[i++] = id; + } + connections[i] = NULL; + + ret = nmc_rl_gen_func_basic (text, state, connections); + + g_free (connections); + return ret; +} + +static char ** +nmcli_con_tab_completion (const char *text, int start, int end) +{ + char **match_array = NULL; + rl_compentry_func_t *generator_func = NULL; + + /* Disable readline's default filename completion */ + rl_attempted_completion_over = 1; + + /* Disable appending space after completion */ + rl_completion_append_character = '\0'; + + if (!is_single_word (rl_line_buffer)) + return NULL; + + if (g_strcmp0 (rl_prompt, PROMPT_CONNECTION) == 0) + generator_func = gen_func_connection_names; + + if (generator_func) + match_array = rl_completion_matches (text, generator_func); + + return match_array; +} + +/* Entry point function for connections-related commands: 'nmcli connection' */ +NMCResultCode +do_connections (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + + /* Set completion function for 'nmcli con' */ + rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion; + + /* Exit early on help */ + if (nmc_arg_is_help (*argv)) { + usage (); + return nmc->return_value; + } + if (argc != 0 && nmc_arg_is_help (*(argv+1))) { + if (usage_connection_second_level (*argv)) + return nmc->return_value; + } + + /* Get NMClient object early */ + nmc->get_client (nmc); + + /* Check whether NetworkManager is running */ + if (!nm_client_get_nm_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return nmc->return_value; + } + /* Compare NM and nmcli versions */ + if (!nmc_versions_match (nmc)) + return nmc->return_value; + + /* Get the connection list */ + nmc->connections = nm_client_get_connections (nmc->client); + + /* Now parse the command line and perform the required operation */ + if (argc == 0) { + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) + goto opt_error; + nmc->return_value = do_connections_show (nmc, FALSE, FALSE, argc, argv); + } else { + if (matches (*argv, "show") == 0) { + gboolean active = FALSE; + gboolean show_secrets = FALSE; + int i; + + next_arg (&argc, &argv); + /* check connection show options [--active] [--show-secrets] */ + for (i = 0; i < 2; i++) { + if (!active && nmc_arg_is_option (*argv, "active")) { + active = TRUE; + next_arg (&argc, &argv); + } + if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) { + show_secrets = TRUE; + next_arg (&argc, &argv); + } + } + nmc->return_value = do_connections_show (nmc, active, show_secrets, argc, argv); + } else if (matches(*argv, "up") == 0) { + nmc->return_value = do_connection_up (nmc, argc-1, argv+1); + } else if (matches(*argv, "down") == 0) { + nmc->return_value = do_connection_down (nmc, argc-1, argv+1); + } else if (matches(*argv, "add") == 0) { + nmc->return_value = do_connection_add (nmc, argc-1, argv+1); + } else if (matches(*argv, "edit") == 0) { + nmc->should_wait = TRUE; + editor_thread_data.nmc = nmc; + editor_thread_data.argc = argc - 1; + editor_thread_data.argv = argv + 1; + editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data); + g_thread_unref (editor_thread); + } else if (matches(*argv, "delete") == 0) { + nmc->return_value = do_connection_delete (nmc, argc-1, argv+1); + } else if (matches(*argv, "reload") == 0) { + nmc->return_value = do_connection_reload (nmc, argc-1, argv+1); + } else if (matches(*argv, "load") == 0) { + nmc->return_value = do_connection_load (nmc, argc-1, argv+1); + } else if (matches (*argv, "modify") == 0) { + gboolean temporary = FALSE; + + next_arg (&argc, &argv); + if (nmc_arg_is_option (*argv, "temporary")) { + temporary = TRUE; + next_arg (&argc, &argv); + } + nmc->return_value = do_connection_modify (nmc, temporary, argc, argv); + } else { + usage (); + g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + + return nmc->return_value; + +opt_error: + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_error_free (error); + return nmc->return_value; +} diff --git a/clients/cli/connections.h b/clients/cli/connections.h new file mode 100644 index 00000000..c7368597 --- /dev/null +++ b/clients/cli/connections.h @@ -0,0 +1,27 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2010 - 2014 Red Hat, Inc. + */ + +#ifndef NMC_CONNECTIONS_H +#define NMC_CONNECTIONS_H + +#include "nmcli.h" + +NMCResultCode do_connections (NmCli *nmc, int argc, char **argv); + +#endif /* NMC_CONNECTIONS_H */ diff --git a/clients/cli/devices.c b/clients/cli/devices.c new file mode 100644 index 00000000..5530adc7 --- /dev/null +++ b/clients/cli/devices.c @@ -0,0 +1,2925 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <readline/readline.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "nm-secret-agent-simple.h" +#include "polkit-agent.h" +#include "utils.h" +#include "common.h" +#include "devices.h" + +/* define some prompts */ +#define PROMPT_INTERFACE _("Interface: ") + +/* Available fields for 'device status' */ +static NmcOutputField nmc_fields_dev_status[] = { + {"DEVICE", N_("DEVICE"), 10}, /* 0 */ + {"TYPE", N_("TYPE"), 17}, /* 1 */ + {"STATE", N_("STATE"), 13}, /* 2 */ + {"DBUS-PATH", N_("DBUS-PATH"), 43}, /* 3 */ + {"CONNECTION", N_("CONNECTION"), 20}, /* 4 */ + {"CON-UUID", N_("CON-UUID"), 38}, /* 5 */ + {"CON-PATH", N_("CON-PATH"), 51}, /* 6 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_STATUS_ALL "DEVICE,TYPE,STATE,DBUS-PATH,CONNECTION,CON-UUID,CON-PATH" +#define NMC_FIELDS_DEV_STATUS_COMMON "DEVICE,TYPE,STATE,CONNECTION" + + +/* Available fields for 'device show' - GENERAL part */ +static NmcOutputField nmc_fields_dev_show_general[] = { + {"NAME", N_("NAME"), 10}, /* 0 */ + {"DEVICE", N_("DEVICE"), 10}, /* 1 */ + {"TYPE", N_("TYPE"), 17}, /* 2 */ + {"VENDOR", N_("VENDOR"), 20}, /* 3 */ + {"PRODUCT", N_("PRODUCT"), 50}, /* 4 */ + {"DRIVER", N_("DRIVER"), 9}, /* 5 */ + {"DRIVER-VERSION", N_("DRIVER-VERSION"), 18}, /* 6 */ + {"FIRMWARE-VERSION", N_("FIRMWARE-VERSION"), 18}, /* 7 */ + {"HWADDR", N_("HWADDR"), 19}, /* 8 */ + {"MTU", N_("MTU"), 10}, /* 9 */ + {"STATE", N_("STATE"), 14}, /* 10 */ + {"REASON", N_("REASON"), 25}, /* 11 */ + {"UDI", N_("UDI"), 64}, /* 12 */ + {"IP-IFACE", N_("IP-IFACE"), 10}, /* 13 */ + {"IS-SOFTWARE", N_("IS-SOFTWARE"), 15}, /* 14 */ + {"NM-MANAGED", N_("NM-MANAGED"), 15}, /* 15 */ + {"AUTOCONNECT", N_("AUTOCONNECT"), 15}, /* 16 */ + {"FIRMWARE-MISSING", N_("FIRMWARE-MISSING"), 18}, /* 17 */ + {"CONNECTION", N_("CONNECTION"), 20}, /* 18 */ + {"CON-UUID", N_("CON-UUID"), 38}, /* 19 */ + {"CON-PATH", N_("CON-PATH"), 51}, /* 20 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_SHOW_GENERAL_ALL "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,DRIVER-VERSION,FIRMWARE-VERSION,HWADDR,MTU,STATE,REASON,"\ + "UDI,IP-IFACE,IS-SOFTWARE,NM-MANAGED,AUTOCONNECT,FIRMWARE-MISSING,CONNECTION,CON-UUID,CON-PATH" +#define NMC_FIELDS_DEV_SHOW_GENERAL_COMMON "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,HWADDR,STATE" + +/* Available fields for 'device show' - CONNECTIONS part */ +static NmcOutputField nmc_fields_dev_show_connections[] = { + {"NAME", N_("NAME"), 10}, /* 0 */ + {"AVAILABLE-CONNECTION-PATHS", N_("AVAILABLE-CONNECTION-PATHS"), 80}, /* 1 */ + {"AVAILABLE-CONNECTIONS", N_("AVAILABLE-CONNECTIONS"), 80}, /* 2 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_SHOW_CONNECTIONS_ALL "AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS" +#define NMC_FIELDS_DEV_SHOW_CONNECTIONS_COMMON "AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS" + +/* Available fields for 'device show' - CAPABILITIES part */ +static NmcOutputField nmc_fields_dev_show_cap[] = { + {"NAME", N_("NAME"), 13}, /* 0 */ + {"CARRIER-DETECT", N_("CARRIER-DETECT"), 16}, /* 1 */ + {"SPEED", N_("SPEED"), 10}, /* 2 */ + {"IS-SOFTWARE", N_("IS-SOFTWARE"), 12}, /* 3 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_SHOW_CAP_ALL "NAME,CARRIER-DETECT,SPEED,IS-SOFTWARE" +#define NMC_FIELDS_DEV_SHOW_CAP_COMMON "NAME,CARRIER-DETECT,SPEED,IS-SOFTWARE" + +/* Available fields for 'device show' - wired properties part */ +static NmcOutputField nmc_fields_dev_show_wired_prop[] = { + {"NAME", N_("NAME"), 18}, /* 0 */ + {"CARRIER", N_("CARRIER"), 10}, /* 1 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_SHOW_WIRED_PROP_ALL "NAME,CARRIER" +#define NMC_FIELDS_DEV_SHOW_WIRED_PROP_COMMON "NAME,CARRIER" + +/* Available fields for 'device show' - wireless properties part */ +static NmcOutputField nmc_fields_dev_show_wifi_prop[] = { + {"NAME", N_("NAME"), 18}, /* 0 */ + {"WEP", N_("WEP"), 5}, /* 1 */ + {"WPA", N_("WPA"), 5}, /* 2 */ + {"WPA2", N_("WPA2"), 6}, /* 3 */ + {"TKIP", N_("TKIP"), 6}, /* 4 */ + {"CCMP", N_("CCMP"), 6}, /* 5 */ + {"AP", N_("AP"), 6}, /* 6 */ + {"ADHOC", N_("ADHOC"), 6}, /* 7 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_SHOW_WIFI_PROP_ALL "NAME,WEP,WPA,WPA2,TKIP,CCMP,AP,ADHOC" +#define NMC_FIELDS_DEV_SHOW_WIFI_PROP_COMMON "NAME,WEP,WPA,WPA2,TKIP,CCMP,AP,ADHOC" + +/* Available fields for 'device show' - wimax properties part */ +static NmcOutputField nmc_fields_dev_show_wimax_prop[] = { + {"NAME", N_("NAME"), 18}, /* 0 */ + {"CTR-FREQ", N_("CTR-FREQ"), 7}, /* 1 */ + {"RSSI", N_("RSSI"), 5}, /* 2 */ + {"CINR", N_("CINR"), 5}, /* 3 */ + {"TX-POW", N_("TX-POW"), 5}, /* 4 */ + {"BSID", N_("BSID"), 18}, /* 5 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_SHOW_WIMAX_PROP_ALL "NAME,CTR-FREQ,RSSI,CINR,TX-POW,BSID" +#define NMC_FIELDS_DEV_SHOW_WIMAX_PROP_COMMON "NAME,CTR-FREQ,RSSI,CINR,TX-POW,BSID" + +/* Available fields for 'device wifi list' */ +static NmcOutputField nmc_fields_dev_wifi_list[] = { + {"NAME", N_("NAME"), 15}, /* 0 */ + {"SSID", N_("SSID"), 33}, /* 1 */ + {"SSID-HEX", N_("SSID-HEX"), 66}, /* 2 */ + {"BSSID", N_("BSSID"), 19}, /* 3 */ + {"MODE", N_("MODE"), 16}, /* 4 */ + {"CHAN", N_("CHAN"), 6}, /* 5 */ + {"FREQ", N_("FREQ"), 10}, /* 6 */ + {"RATE", N_("RATE"), 10}, /* 7 */ + {"SIGNAL", N_("SIGNAL"), 8}, /* 8 */ + {"BARS", N_("BARS"), 6}, /* 9 */ + {"SECURITY", N_("SECURITY"), 10}, /* 10 */ + {"WPA-FLAGS", N_("WPA-FLAGS"), 25}, /* 11 */ + {"RSN-FLAGS", N_("RSN-FLAGS"), 25}, /* 12 */ + {"DEVICE", N_("DEVICE"), 10}, /* 13 */ + {"ACTIVE", N_("ACTIVE"), 8}, /* 14 */ + {"IN-USE", N_("*"), 1}, /* 15 */ + {"DBUS-PATH", N_("DBUS-PATH"), 46}, /* 16 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_WIFI_LIST_ALL "SSID,SSID-HEX,BSSID,MODE,CHAN,FREQ,RATE,SIGNAL,BARS,SECURITY,"\ + "WPA-FLAGS,RSN-FLAGS,DEVICE,ACTIVE,IN-USE,DBUS-PATH" +#define NMC_FIELDS_DEV_WIFI_LIST_COMMON "IN-USE,SSID,MODE,CHAN,RATE,SIGNAL,BARS,SECURITY" +#define NMC_FIELDS_DEV_WIFI_LIST_FOR_DEV_LIST "NAME,"NMC_FIELDS_DEV_WIFI_LIST_COMMON + +/* Available fields for 'device wimax list' */ +static NmcOutputField nmc_fields_dev_wimax_list[] = { + {"NAME", N_("NAME"), 15}, /* 0 */ + {"NSP", N_("NSP"), 33}, /* 1 */ + {"SIGNAL", N_("SIGNAL"), 8}, /* 2 */ + {"TYPE", N_("TYPE"), 16}, /* 3 */ + {"DEVICE", N_("DEVICE"), 10}, /* 4 */ + {"ACTIVE", N_("ACTIVE"), 8}, /* 5 */ + {"DBUS-PATH", N_("DBUS-PATH"), 46}, /* 6 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_WIMAX_LIST_ALL "NSP,SIGNAL,TYPE,DEVICE,ACTIVE,DBUS-PATH" +#define NMC_FIELDS_DEV_WIMAX_LIST_COMMON "NSP,SIGNAL,TYPE,DEVICE,ACTIVE" +#define NMC_FIELDS_DEV_WIMAX_LIST_FOR_DEV_LIST "NAME,"NMC_FIELDS_DEV_WIMAX_LIST_COMMON + +/* Available fields for 'device show' - BOND, TEAM, BRIDGE part */ +static NmcOutputField nmc_fields_dev_show_master_prop[] = { + {"NAME", N_("NAME"), 18}, /* 0 */ + {"SLAVES", N_("SLAVES"), 20}, /* 1 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_SHOW_MASTER_PROP_ALL "NAME,SLAVES" +#define NMC_FIELDS_DEV_SHOW_MASTER_PROP_COMMON "NAME,SLAVES" + +/* Available fields for 'device show' - VLAN part */ +static NmcOutputField nmc_fields_dev_show_vlan_prop[] = { + {"NAME", N_("NAME"), 18}, /* 0 */ + {"PARENT", N_("PARENT"), 10}, /* 1 */ + {"ID", N_("ID"), 5}, /* 2 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_SHOW_VLAN_PROP_ALL "NAME,PARENT,ID" +#define NMC_FIELDS_DEV_SHOW_VLAN_PROP_COMMON "NAME,PARENT,ID" + +/* Available fields for 'device show' - BLUETOOTH part */ +static NmcOutputField nmc_fields_dev_show_bluetooth[] = { + {"NAME", N_("NAME"), 0}, /* 0 */ + {"CAPABILITIES", N_("CAPABILITIES"), 0}, /* 1 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_SHOW_BLUETOOTH_ALL "NAME,CAPABILITIES" +#define NMC_FIELDS_DEV_SHOW_BLUETOOTH_COMMON "NAME,CAPABILITIES" + +/* defined in common.c */ +extern NmcOutputField nmc_fields_ip4_config[]; +extern NmcOutputField nmc_fields_ip6_config[]; +extern NmcOutputField nmc_fields_dhcp4_config[]; +extern NmcOutputField nmc_fields_dhcp6_config[]; + +/* Available sections for 'device show' */ +static NmcOutputField nmc_fields_dev_show_sections[] = { + {"GENERAL", N_("GENERAL"), 0, nmc_fields_dev_show_general + 1 }, /* 0 */ + {"CAPABILITIES", N_("CAPABILITIES"), 0, nmc_fields_dev_show_cap + 1 }, /* 1 */ + {"WIFI-PROPERTIES", N_("WIFI-PROPERTIES"), 0, nmc_fields_dev_show_wifi_prop + 1 }, /* 2 */ + {"AP", N_("AP"), 0, nmc_fields_dev_wifi_list + 1 }, /* 3 */ + {"WIRED-PROPERTIES", N_("WIRED-PROPERTIES"), 0, nmc_fields_dev_show_wired_prop + 1 }, /* 4 */ + {"WIMAX-PROPERTIES", N_("WIMAX-PROPERTIES"), 0, nmc_fields_dev_show_wimax_prop + 1 }, /* 5 */ + {"NSP", N_("NSP"), 0, nmc_fields_dev_wimax_list + 1 }, /* 6 */ + {"IP4", N_("IP4"), 0, nmc_fields_ip4_config + 1 }, /* 7 */ + {"DHCP4", N_("DHCP4"), 0, nmc_fields_dhcp4_config + 1 }, /* 8 */ + {"IP6", N_("IP6"), 0, nmc_fields_ip6_config + 1 }, /* 9 */ + {"DHCP6", N_("DHCP6"), 0, nmc_fields_dhcp6_config + 1 }, /* 10 */ + {"BOND", N_("BOND"), 0, nmc_fields_dev_show_master_prop + 1 }, /* 11 */ + {"TEAM", N_("TEAM"), 0, nmc_fields_dev_show_master_prop + 1 }, /* 12 */ + {"BRIDGE", N_("BRIDGE"), 0, nmc_fields_dev_show_master_prop + 1 }, /* 13 */ + {"VLAN", N_("VLAN"), 0, nmc_fields_dev_show_vlan_prop + 1 }, /* 14 */ + {"BLUETOOTH", N_("BLUETOOTH"), 0, nmc_fields_dev_show_bluetooth + 1 }, /* 15 */ + {"CONNECTIONS", N_("CONNECTIONS"), 0, nmc_fields_dev_show_connections + 1 }, /* 16 */ + {NULL, NULL, 0, NULL } +}; +#if WITH_WIMAX +#define NMC_FIELDS_DEV_SHOW_SECTIONS_ALL "GENERAL,CAPABILITIES,BOND,TEAM,BRIDGE,VLAN,WIFI-PROPERTIES,AP,WIRED-PROPERTIES,"\ + "WIMAX-PROPERTIES,NSP,BLUETOOTH,CONNECTIONS,IP4,DHCP4,IP6,DHCP6" +#define NMC_FIELDS_DEV_SHOW_SECTIONS_COMMON "GENERAL.DEVICE,GENERAL.TYPE,GENERAL.HWADDR,GENERAL.MTU,GENERAL.STATE,"\ + "GENERAL.CONNECTION,GENERAL.CON-PATH,WIRED-PROPERTIES,IP4,IP6" +#else +#define NMC_FIELDS_DEV_SHOW_SECTIONS_ALL "GENERAL,CAPABILITIES,BOND,TEAM,BRIDGE,VLAN,WIFI-PROPERTIES,AP,WIRED-PROPERTIES,"\ + "BLUETOOTH,CONNECTIONS,IP4,DHCP4,IP6,DHCP6" +#define NMC_FIELDS_DEV_SHOW_SECTIONS_COMMON "GENERAL.DEVICE,GENERAL.TYPE,GENERAL.HWADDR,GENERAL.MTU,GENERAL.STATE,"\ + "GENERAL.CONNECTION,GENERAL.CON-PATH,WIRED-PROPERTIES,IP4,IP6" +#endif + + +/* glib main loop variable - defined in nmcli.c */ +extern GMainLoop *loop; + +static guint progress_id = 0; /* ID of event source for displaying progress */ + +static void +usage (void) +{ + g_printerr (_("Usage: nmcli device { COMMAND | help }\n\n" +#if WITH_WIMAX + "COMMAND := { status | show | connect | disconnect | delete | wifi | wimax }\n\n" +#else + "COMMAND := { status | show | connect | disconnect | delete | wifi }\n\n" +#endif + " status\n\n" + " show [<ifname>]\n\n" + " connect <ifname>\n\n" + " disconnect <ifname>\n\n" + " delete <ifname>\n\n" + " wifi [list [ifname <ifname>] [bssid <BSSID>]]\n\n" + " wifi connect <(B)SSID> [password <password>] [wep-key-type key|phrase] [ifname <ifname>]\n" + " [bssid <BSSID>] [name <name>] [private yes|no]\n\n" + " wifi rescan [[ifname] <ifname>]\n\n" +#if WITH_WIMAX + " wimax [list [ifname <ifname>] [nsp <name>]]\n\n" +#endif + )); +} + +static void +usage_device_status (void) +{ + g_printerr (_("Usage: nmcli device status { help }\n" + "\n" + "Show status for all devices.\n" + "By default, the following columns are shown:\n" + " DEVICE - interface name\n" + " TYPE - device type\n" + " STATE - device state\n" + " CONNECTION - connection activated on device (if any)\n" + "Displayed columns can be changed using '--fields' global option. 'status' is\n" + "the default command, which means 'nmcli device' calls 'nmcli device status'.\n\n")); +} + +static void +usage_device_show (void) +{ + g_printerr (_("Usage: nmcli device show { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [<ifname>]\n" + "\n" + "Show details of device(s).\n" + "The command lists details for all devices, or for a given device.\n\n")); +} + +static void +usage_device_connect (void) +{ + g_printerr (_("Usage: nmcli device connect { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := <ifname>\n" + "\n" + "Connect the device.\n" + "NetworkManager will try to find a suitable connection that will be activated.\n" + "It will also consider connections that are not set to auto-connect.\n\n")); +} + +static void +usage_device_disconnect (void) +{ + g_printerr (_("Usage: nmcli device disconnect { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := <ifname>\n" + "\n" + "Disconnect the device.\n" + "The command disconnects the device and prevents it from auto-activating\n" + "further connections without user/manual intervention.\n\n")); +} + +static void +usage_device_delete (void) +{ + g_printerr (_("Usage: nmcli device delete { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := <ifname>\n" + "\n" + "Deletes the software device.\n" + "The command removes the interface. It only works for software devices\n" + "(like bonds, bridges, etc.). Hardware devices cannot be deleted by the\n" + "command.\n\n")); +} + +static void +usage_device_wifi (void) +{ + g_printerr (_("Usage: nmcli device wifi { ARGUMENTS | help }\n" + "\n" + "Perform operation on Wi-Fi devices.\n" + "\n" + "ARGUMENTS := [list [ifname <ifname>] [bssid <BSSID>]]\n" + "\n" + "List available Wi-Fi access points. The 'ifname' and 'bssid' options can be\n" + "used to list APs for a particular interface, or with a specific BSSID.\n" + "\n" + "ARGUMENTS := connect <(B)SSID> [password <password>] [wep-key-type key|phrase] [ifname <ifname>]\n" + " [bssid <BSSID>] [name <name>] [private yes|no]\n" + "\n" + "Connect to a Wi-Fi network specified by SSID or BSSID. The command creates\n" + "a new connection and then activates it on a device. This is a command-line\n" + "counterpart of clicking an SSID in a GUI client. The command always creates\n" + "a new connection and thus it is mainly useful for connecting to new Wi-Fi\n" + "networks. If a connection for the network already exists, it is better to\n" + "bring up the existing profile as follows: nmcli con up id <name>. Note that\n" + "only open, WEP and WPA-PSK networks are supported at the moment. It is also\n" + "assumed that IP configuration is obtained via DHCP.\n" + "\n" + "ARGUMENTS := rescan [[ifname] <ifname>]\n" + "\n" + "Request that NetworkManager immediately re-scan for available access points.\n" + "NetworkManager scans Wi-Fi networks periodically, but in some cases it might\n" + "be useful to start scanning manually. Note that this command does not show\n" + "the APs, use 'nmcli device wifi list' for that.\n\n")); +} + +#if WITH_WIMAX +static void +usage_device_wimax (void) +{ + g_printerr (_("Usage: nmcli device wimax { ARGUMENTS | help }\n" + "\n" + "Perform operation on WiMAX devices.\n" + "\n" + "ARGUMENTS := [list [ifname <ifname>] [nsp <name>]]\n" + "\n" + "List available WiMAX NSPs. The 'ifname' and 'nsp' options can be used to\n" + "list networks for a particular interface, or with a specific NSP.\n\n")); +} +#endif + +/* quit main loop */ +static void +quit (void) +{ + if (progress_id) { + g_source_remove (progress_id); + nmc_terminal_erase_line (); + } + + g_main_loop_quit (loop); /* quit main loop */ +} + +static int +compare_devices (const void *a, const void *b) +{ + NMDevice *da = *(NMDevice **)a; + NMDevice *db = *(NMDevice **)b; + int cmp; + + /* Sort by later device states first */ + cmp = nm_device_get_state (db) - nm_device_get_state (da); + if (cmp != 0) + return cmp; + + cmp = g_strcmp0 (nm_device_get_type_description (da), + nm_device_get_type_description (db)); + if (cmp != 0) + return cmp; + + return g_strcmp0 (nm_device_get_iface (da), + nm_device_get_iface (db)); +} + +static NMDevice ** +get_devices_sorted (NMClient *client) +{ + const GPtrArray *devs; + NMDevice **sorted; + + devs = nm_client_get_devices (client); + + sorted = g_new (NMDevice *, devs->len + 1); + memcpy (sorted, devs->pdata, devs->len * sizeof (NMDevice *)); + sorted[devs->len] = NULL; + + qsort (sorted, devs->len, sizeof (NMDevice *), compare_devices); + return sorted; +} + +static char * +ap_wpa_rsn_flags_to_string (NM80211ApSecurityFlags flags) +{ + char *flags_str[16]; /* Enough space for flags and terminating NULL */ + char *ret_str; + int i = 0; + + if (flags & NM_802_11_AP_SEC_PAIR_WEP40) + flags_str[i++] = g_strdup ("pair_wpe40"); + if (flags & NM_802_11_AP_SEC_PAIR_WEP104) + flags_str[i++] = g_strdup ("pair_wpe104"); + if (flags & NM_802_11_AP_SEC_PAIR_TKIP) + flags_str[i++] = g_strdup ("pair_tkip"); + if (flags & NM_802_11_AP_SEC_PAIR_CCMP) + flags_str[i++] = g_strdup ("pair_ccmp"); + if (flags & NM_802_11_AP_SEC_GROUP_WEP40) + flags_str[i++] = g_strdup ("group_wpe40"); + if (flags & NM_802_11_AP_SEC_GROUP_WEP104) + flags_str[i++] = g_strdup ("group_wpe104"); + if (flags & NM_802_11_AP_SEC_GROUP_TKIP) + flags_str[i++] = g_strdup ("group_tkip"); + if (flags & NM_802_11_AP_SEC_GROUP_CCMP) + flags_str[i++] = g_strdup ("group_ccmp"); + if (flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + flags_str[i++] = g_strdup ("psk"); + if (flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + flags_str[i++] = g_strdup ("802.1X"); + + if (i == 0) + flags_str[i++] = g_strdup (_("(none)")); + + flags_str[i] = NULL; + + ret_str = g_strjoinv (" ", flags_str); + + i = 0; + while (flags_str[i]) + g_free (flags_str[i++]); + + return ret_str; +} + +typedef struct { + NmCli *nmc; + int index; + guint32 output_flags; + const char* active_bssid; + const char* device; +} APInfo; + +static void +fill_output_access_point (gpointer data, gpointer user_data) +{ + NMAccessPoint *ap = NM_ACCESS_POINT (data); + APInfo *info = (APInfo *) user_data; + NmcOutputField *arr; + gboolean active = FALSE; + NM80211ApFlags flags; + NM80211ApSecurityFlags wpa_flags, rsn_flags; + guint32 freq, bitrate; + guint8 strength; + GBytes *ssid; + const char *bssid; + NM80211Mode mode; + char *channel_str, *freq_str, *ssid_str = NULL, *ssid_hex_str = NULL, + *bitrate_str, *strength_str, *wpa_flags_str, *rsn_flags_str; + GString *security_str; + char *ap_name; + const char *sig_bars; + + if (info->active_bssid) { + const char *current_bssid = nm_access_point_get_bssid (ap); + if (current_bssid && !strcmp (current_bssid, info->active_bssid)) + active = TRUE; + } + + /* Get AP properties */ + flags = nm_access_point_get_flags (ap); + wpa_flags = nm_access_point_get_wpa_flags (ap); + rsn_flags = nm_access_point_get_rsn_flags (ap); + ssid = nm_access_point_get_ssid (ap); + bssid = nm_access_point_get_bssid (ap); + freq = nm_access_point_get_frequency (ap); + mode = nm_access_point_get_mode (ap); + bitrate = nm_access_point_get_max_bitrate (ap); + strength = MIN (nm_access_point_get_strength (ap), 100); + + /* Convert to strings */ + if (ssid) { + const guint8 *ssid_data; + gsize ssid_len; + + ssid_data = g_bytes_get_data (ssid, &ssid_len); + ssid_str = nm_utils_ssid_to_utf8 (ssid_data, ssid_len); + ssid_hex_str = ssid_to_hex ((const char *) ssid_data, ssid_len); + } + channel_str = g_strdup_printf ("%u", nm_utils_wifi_freq_to_channel (freq)); + freq_str = g_strdup_printf (_("%u MHz"), freq); + bitrate_str = g_strdup_printf (_("%u Mbit/s"), bitrate/1000); + strength_str = g_strdup_printf ("%u", strength); + wpa_flags_str = ap_wpa_rsn_flags_to_string (wpa_flags); + rsn_flags_str = ap_wpa_rsn_flags_to_string (rsn_flags); + sig_bars = nm_utils_wifi_strength_bars (strength); + + security_str = g_string_new (NULL); + + if ( (flags & NM_802_11_AP_FLAGS_PRIVACY) + && (wpa_flags == NM_802_11_AP_SEC_NONE) + && (rsn_flags == NM_802_11_AP_SEC_NONE)) { + g_string_append (security_str, _("WEP")); + g_string_append_c (security_str, ' '); + } + if (wpa_flags != NM_802_11_AP_SEC_NONE) { + g_string_append (security_str, _("WPA1")); + g_string_append_c (security_str, ' '); + } + if (rsn_flags != NM_802_11_AP_SEC_NONE) { + g_string_append (security_str, _("WPA2")); + g_string_append_c (security_str, ' '); + } + if ( (wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + g_string_append (security_str, _("802.1X")); + g_string_append_c (security_str, ' '); + } + + if (security_str->len > 0) + g_string_truncate (security_str, security_str->len-1); /* Chop off last space */ + + arr = nmc_dup_fields_array (nmc_fields_dev_wifi_list, + sizeof (nmc_fields_dev_wifi_list), + info->output_flags); + + ap_name = g_strdup_printf ("AP[%d]", info->index++); /* AP */ + set_val_str (arr, 0, ap_name); + set_val_str (arr, 1, ssid_str); + set_val_str (arr, 2, ssid_hex_str); + set_val_strc (arr, 3, bssid); + set_val_strc (arr, 4, mode == NM_802_11_MODE_ADHOC ? _("Ad-Hoc") + : mode == NM_802_11_MODE_INFRA ? _("Infra") + : _("N/A")); + set_val_str (arr, 5, channel_str); + set_val_str (arr, 6, freq_str); + set_val_str (arr, 7, bitrate_str); + set_val_str (arr, 8, strength_str); + set_val_strc (arr, 9, sig_bars); + set_val_str (arr, 10, security_str->str); + set_val_str (arr, 11, wpa_flags_str); + set_val_str (arr, 12, rsn_flags_str); + set_val_strc (arr, 13, info->device); + set_val_strc (arr, 14, active ? _("yes") : _("no")); + set_val_strc (arr, 15, active ? "*" : " "); + set_val_strc (arr, 16, nm_object_get_path (NM_OBJECT (ap))); + + g_ptr_array_add (info->nmc->output_data, arr); + + g_string_free (security_str, FALSE); +} + +#if WITH_WIMAX +static void +fill_output_wimax_nsp (NMWimaxNsp *nsp, NmCli *nmc, NMDevice *dev, int idx, guint32 o_flags) +{ + NMDeviceWimax *wimax = NM_DEVICE_WIMAX (dev); + char *nsp_name, *quality_str; + const char *ntype; + gboolean active = FALSE; + NmcOutputField *arr; + + switch (nm_wimax_nsp_get_network_type (nsp)) { + case NM_WIMAX_NSP_NETWORK_TYPE_HOME: + ntype = _("Home"); + break; + case NM_WIMAX_NSP_NETWORK_TYPE_PARTNER: + ntype = _("Partner"); + break; + case NM_WIMAX_NSP_NETWORK_TYPE_ROAMING_PARTNER: + ntype = _("Roaming"); + break; + default: + ntype = _("Unknown"); + break; + } + + if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) { + if (nsp == nm_device_wimax_get_active_nsp (wimax)) + active = TRUE; + } + + quality_str = g_strdup_printf ("%u", nm_wimax_nsp_get_signal_quality (nsp)); + nsp_name = g_strdup_printf ("NSP[%d]", idx); /* NSP */ + + arr = nmc_dup_fields_array (nmc_fields_dev_wimax_list, + sizeof (nmc_fields_dev_wimax_list), + o_flags); + set_val_str (arr, 0, nsp_name); + set_val_strc (arr, 1, nm_wimax_nsp_get_name (nsp)); + set_val_str (arr, 2, quality_str); + set_val_strc (arr, 3, ntype); + set_val_strc (arr, 4, nm_device_get_iface (dev)); + set_val_strc (arr, 5, active ? _("yes") : _("no")); + set_val_strc (arr, 6, nm_object_get_path (NM_OBJECT (nsp))); + + g_ptr_array_add (nmc->output_data, arr); +} +#endif + +static char * +bluetooth_caps_to_string (NMBluetoothCapabilities caps) +{ + char *caps_str[8]; /* Enough space for caps and terminating NULL */ + char *ret_str; + int i = 0; + + if (caps & NM_BT_CAPABILITY_DUN) + caps_str[i++] = g_strdup ("DUN"); + if (caps & NM_BT_CAPABILITY_NAP) + caps_str[i++] = g_strdup ("NAP"); + + if (i == 0) + caps_str[i++] = g_strdup (_("(none)")); + + caps_str[i] = NULL; + + ret_str = g_strjoinv (" ", caps_str); + + i = 0; + while (caps_str[i]) + g_free (caps_str[i++]); + + return ret_str; +} + +static const char * +construct_header_name (const char *base, const char *spec) +{ + static char header_name[128]; + + if (spec == NULL) + return base; + + g_strlcpy (header_name, base, sizeof (header_name)); + g_strlcat (header_name, " (", sizeof (header_name)); + g_strlcat (header_name, spec, sizeof (header_name)); + g_strlcat (header_name, ")", sizeof (header_name)); + + return header_name; +} + +static const char * +get_active_connection_id (NMDevice *device) +{ + NMActiveConnection *ac; + + ac = nm_device_get_active_connection (device); + if (!ac) + return NULL; + + return nm_active_connection_get_id (ac); +} + +static gboolean +print_bond_team_bridge_info (NMDevice *device, + NmCli *nmc, + const char *group_prefix, + const char *one_field) +{ + const GPtrArray *slaves = NULL; + GString *slaves_str; + int idx; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + if (NM_IS_DEVICE_BOND (device)) + slaves = nm_device_bond_get_slaves (NM_DEVICE_BOND (device)); + else if (NM_IS_DEVICE_TEAM (device)) + slaves = nm_device_team_get_slaves (NM_DEVICE_TEAM (device)); + else if (NM_IS_DEVICE_BRIDGE (device)) + slaves = nm_device_bridge_get_slaves (NM_DEVICE_BRIDGE (device)); + + slaves_str = g_string_new (NULL); + for (idx = 0; slaves && idx < slaves->len; idx++) { + NMDevice *slave = g_ptr_array_index (slaves, idx); + const char *iface = nm_device_get_iface (slave); + + if (iface) { + g_string_append (slaves_str, iface); + g_string_append_c (slaves_str, ' '); + } + } + if (slaves_str->len > 0) + g_string_truncate (slaves_str, slaves_str->len-1); /* Chop off last space */ + + tmpl = nmc_fields_dev_show_master_prop; + tmpl_len = sizeof (nmc_fields_dev_show_master_prop); + nmc->print_fields.indices = parse_output_fields (one_field ? one_field : NMC_FIELDS_DEV_SHOW_MASTER_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, group_prefix); /* i.e. BOND, TEAM, BRIDGE */ + set_val_str (arr, 1, slaves_str->str); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + g_string_free (slaves_str, FALSE); + nmc_empty_output_fields (nmc); + + return TRUE; +} + +static gboolean +show_device_info (NMDevice *device, NmCli *nmc) +{ + GError *error = NULL; + APInfo *info; + const char *hwaddr = NULL; + NMDeviceState state = NM_DEVICE_STATE_UNKNOWN; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + NMDeviceCapabilities caps; + NMActiveConnection *acon; + guint32 speed; + char *speed_str, *state_str, *reason_str, *mtu_str; + GArray *sections_array; + int k; + char *fields_str; + char *fields_all = NMC_FIELDS_DEV_SHOW_SECTIONS_ALL; + char *fields_common = NMC_FIELDS_DEV_SHOW_SECTIONS_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + gboolean was_output = FALSE; + NMIPConfig *cfg4, *cfg6; + NMDhcpConfig *dhcp4, *dhcp6; + const char *base_hdr = _("Device details"); + GPtrArray *fields_in_section = NULL; + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + sections_array = parse_output_fields (fields_str, nmc_fields_dev_show_sections, TRUE, &fields_in_section, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: 'device show': %s"), error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + + /* Main header */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_device_get_iface (device)); + nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_SHOW_GENERAL_ALL, + nmc_fields_dev_show_general, FALSE, NULL, NULL); + + nmc_fields_dev_show_general[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY; + print_required_fields (nmc, nmc_fields_dev_show_general); + + /* Loop through the required sections and print them. */ + for (k = 0; k < sections_array->len; k++) { + int section_idx = g_array_index (sections_array, int, k); + char *section_fld = (char *) g_ptr_array_index (fields_in_section, k); + + if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) + g_print ("\n"); /* Print empty line between groups in tabular mode */ + + was_output = FALSE; + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + state = nm_device_get_state (device); + reason = nm_device_get_state_reason (device); + + /* section GENERAL */ + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[0].name)) { + tmpl = nmc_fields_dev_show_general; + tmpl_len = sizeof (nmc_fields_dev_show_general); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_GENERAL_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + state_str = g_strdup_printf ("%d (%s)", state, nmc_device_state_to_string (state)); + reason_str = g_strdup_printf ("%d (%s)", reason, nmc_device_reason_to_string (reason)); + hwaddr = nm_device_get_hw_address (device); + mtu_str = g_strdup_printf ("%u", nm_device_get_mtu (device)); + acon = nm_device_get_active_connection (device); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[0].name); /* "GENERAL"*/ + set_val_strc (arr, 1, nm_device_get_iface (device)); + set_val_strc (arr, 2, nm_device_get_type_description (device)); + set_val_strc (arr, 3, nm_device_get_vendor (device)); + set_val_strc (arr, 4, nm_device_get_product (device)); + set_val_strc (arr, 5, nm_device_get_driver (device) ? nm_device_get_driver (device) : _("(unknown)")); + set_val_strc (arr, 6, nm_device_get_driver_version (device)); + set_val_strc (arr, 7, nm_device_get_firmware_version (device)); + set_val_strc (arr, 8, hwaddr ? hwaddr : _("(unknown)")); + set_val_str (arr, 9, mtu_str); + set_val_str (arr, 10, state_str); + set_val_str (arr, 11, reason_str); + set_val_strc (arr, 12, nm_device_get_udi (device)); + set_val_strc (arr, 13, nm_device_get_ip_iface (device)); + set_val_strc (arr, 14, nm_device_is_software (device) ? _("yes") : _("no")); + set_val_strc (arr, 15, nm_device_get_managed (device) ? _("yes") : _("no")); + set_val_strc (arr, 16, nm_device_get_autoconnect (device) ? _("yes") : _("no")); + set_val_strc (arr, 17, nm_device_get_firmware_missing (device) ? _("yes") : _("no")); + set_val_strc (arr, 18, get_active_connection_id (device)); + set_val_strc (arr, 19, acon ? nm_active_connection_get_uuid (acon) : NULL); + set_val_strc (arr, 20, acon ? nm_object_get_path (NM_OBJECT (acon)) : NULL); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + was_output = TRUE; + } + + /* section CAPABILITIES */ + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[1].name)) { + tmpl = nmc_fields_dev_show_cap; + tmpl_len = sizeof (nmc_fields_dev_show_cap); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_CAP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + caps = nm_device_get_capabilities (device); + speed = 0; + if (NM_IS_DEVICE_ETHERNET (device)) { + /* Speed in Mb/s */ + speed = nm_device_ethernet_get_speed (NM_DEVICE_ETHERNET (device)); + } else if (NM_IS_DEVICE_WIFI (device)) { + /* Speed in b/s */ + speed = nm_device_wifi_get_bitrate (NM_DEVICE_WIFI (device)); + speed /= 1000; + } + speed_str = speed ? g_strdup_printf (_("%u Mb/s"), speed) : g_strdup (_("unknown")); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[1].name); /* "CAPABILITIES" */ + set_val_strc (arr, 1, (caps & NM_DEVICE_CAP_CARRIER_DETECT) ? _("yes") : _("no")); + set_val_str (arr, 2, speed_str); + set_val_strc (arr, 3, (caps & NM_DEVICE_CAP_IS_SOFTWARE) ? _("yes") : _("no")); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + was_output = TRUE; + } + + /* Wireless specific information */ + if ((NM_IS_DEVICE_WIFI (device))) { + NMDeviceWifiCapabilities wcaps; + NMAccessPoint *active_ap = NULL; + const char *active_bssid = NULL; + const GPtrArray *aps; + + /* section WIFI-PROPERTIES */ + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[2].name)) { + wcaps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (device)); + + tmpl = nmc_fields_dev_show_wifi_prop; + tmpl_len = sizeof (nmc_fields_dev_show_wifi_prop); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_WIFI_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[2].name); /* "WIFI-PROPERTIES" */ + set_val_strc (arr, 1, (wcaps & (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104)) ? + _("yes") : _("no")); + set_val_strc (arr, 2, (wcaps & NM_WIFI_DEVICE_CAP_WPA) ? _("yes") : _("no")); + set_val_strc (arr, 3, (wcaps & NM_WIFI_DEVICE_CAP_RSN) ? _("yes") : _("no")); + set_val_strc (arr, 4, (wcaps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP) ? _("yes") : _("no")); + set_val_strc (arr, 5, (wcaps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP) ? _("yes") : _("no")); + set_val_strc (arr, 6, (wcaps & NM_WIFI_DEVICE_CAP_AP) ? _("yes") : _("no")); + set_val_strc (arr, 7, (wcaps & NM_WIFI_DEVICE_CAP_ADHOC) ? _("yes") : _("no")); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + was_output = TRUE; + } + + /* section AP */ + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[3].name)) { + if (state == NM_DEVICE_STATE_ACTIVATED) { + active_ap = nm_device_wifi_get_active_access_point (NM_DEVICE_WIFI (device)); + active_bssid = active_ap ? nm_access_point_get_bssid (active_ap) : NULL; + } + + tmpl = nmc_fields_dev_wifi_list; + tmpl_len = sizeof (nmc_fields_dev_wifi_list); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_WIFI_LIST_FOR_DEV_LIST, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + info = g_malloc0 (sizeof (APInfo)); + info->nmc = nmc; + info->index = 1; + info->output_flags = NMC_OF_FLAG_SECTION_PREFIX; + info->active_bssid = active_bssid; + info->device = nm_device_get_iface (device); + aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); + g_ptr_array_foreach ((GPtrArray *) aps, fill_output_access_point, (gpointer) info); + g_free (info); + print_data (nmc); /* Print all data */ + was_output = TRUE; + } + } else if (NM_IS_DEVICE_ETHERNET (device)) { + /* WIRED-PROPERTIES */ + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[4].name)) { + tmpl = nmc_fields_dev_show_wired_prop; + tmpl_len = sizeof (nmc_fields_dev_show_wired_prop); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_WIRED_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[4].name); /* "WIRED-PROPERTIES" */ + set_val_strc (arr, 1, (nm_device_ethernet_get_carrier (NM_DEVICE_ETHERNET (device))) ? + _("on") : _("off")); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + was_output = TRUE; + } + } +#if WITH_WIMAX + else if (NM_IS_DEVICE_WIMAX (device)) { + /* WIMAX-PROPERTIES */ + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[5].name)) { + char *cfreq = NULL, *rssi = NULL, *cinr = NULL, *txpow = NULL; + guint tmp_uint; + gint tmp_int; + + /* Field names */ + tmpl = nmc_fields_dev_show_wimax_prop; + tmpl_len = sizeof (nmc_fields_dev_show_wimax_prop); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_WIMAX_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + /* Center frequency */ + tmp_uint = nm_device_wimax_get_center_frequency (NM_DEVICE_WIMAX (device)); + if (tmp_uint) + cfreq = g_strdup_printf ("%'.1f MHz", (double) tmp_uint / 1000.0); + + /* RSSI */ + tmp_int = nm_device_wimax_get_rssi (NM_DEVICE_WIMAX (device)); + if (tmp_int) + rssi = g_strdup_printf ("%d dBm", tmp_int); + + /* CINR */ + tmp_int = nm_device_wimax_get_cinr (NM_DEVICE_WIMAX (device)); + if (tmp_int) + cinr = g_strdup_printf ("%d dB", tmp_int); + + /* TX Power */ + tmp_int = nm_device_wimax_get_tx_power (NM_DEVICE_WIMAX (device)); + if (tmp_int) + txpow = g_strdup_printf ("%'.2f dBm", (float) tmp_int / 2.0); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[5].name); /* "WIMAX-PROPERTIES" */ + set_val_str (arr, 1, cfreq); + set_val_str (arr, 2, rssi); + set_val_str (arr, 3, cinr); + set_val_str (arr, 4, txpow); + set_val_strc (arr, 5, nm_device_wimax_get_bsid (NM_DEVICE_WIMAX (device))); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + was_output = TRUE; + } + + /* section NSP */ + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[6].name)) { + const GPtrArray *nsps; + int g, idx = 1; + + tmpl = nmc_fields_dev_wimax_list; + tmpl_len = sizeof (nmc_fields_dev_wimax_list); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_WIMAX_LIST_FOR_DEV_LIST, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (device)); + for (g = 0; g < nsps->len; g++) { + NMWimaxNsp *nsp = g_ptr_array_index (nsps, g); + + fill_output_wimax_nsp (nsp, nmc, device, idx++, NMC_OF_FLAG_SECTION_PREFIX); + } + print_data (nmc); /* Print all data */ + was_output = TRUE; + } + } +#endif + + /* IP configuration info */ + cfg4 = nm_device_get_ip4_config (device); + cfg6 = nm_device_get_ip6_config (device); + dhcp4 = nm_device_get_dhcp4_config (device); + dhcp6 = nm_device_get_dhcp6_config (device); + + /* IP4 */ + if (cfg4 && !strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[7].name)) + was_output = print_ip4_config (cfg4, nmc, nmc_fields_dev_show_sections[7].name, section_fld); + + /* DHCP4 */ + if (dhcp4 && !strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[8].name)) + was_output = print_dhcp4_config (dhcp4, nmc, nmc_fields_dev_show_sections[8].name, section_fld); + + /* IP6 */ + if (cfg6 && !strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[9].name)) + was_output = print_ip6_config (cfg6, nmc, nmc_fields_dev_show_sections[9].name, section_fld); + + /* DHCP6 */ + if (dhcp6 && !strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[10].name)) + was_output = print_dhcp6_config (dhcp6, nmc, nmc_fields_dev_show_sections[10].name, section_fld); + + /* Bond specific information */ + if (NM_IS_DEVICE_BOND (device)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[11].name)) + was_output = print_bond_team_bridge_info (device, nmc, nmc_fields_dev_show_sections[11].name, section_fld); + } + + /* Team specific information */ + if (NM_IS_DEVICE_TEAM (device)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[12].name)) + was_output = print_bond_team_bridge_info (device, nmc, nmc_fields_dev_show_sections[12].name, section_fld); + } + + /* Bridge specific information */ + if (NM_IS_DEVICE_BRIDGE (device)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[13].name)) + was_output = print_bond_team_bridge_info (device, nmc, nmc_fields_dev_show_sections[13].name, section_fld); + } + + /* VLAN-specific information */ + if ((NM_IS_DEVICE_VLAN (device))) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[14].name)) { + char * vlan_id_str = g_strdup_printf ("%u", nm_device_vlan_get_vlan_id (NM_DEVICE_VLAN (device))); + NMDevice *parent = nm_device_vlan_get_parent (NM_DEVICE_VLAN (device)); + + tmpl = nmc_fields_dev_show_vlan_prop; + tmpl_len = sizeof (nmc_fields_dev_show_vlan_prop); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_VLAN_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[14].name); /* "VLAN" */ + set_val_strc (arr, 1, parent ? nm_device_get_iface (parent) : NULL); + set_val_str (arr, 2, vlan_id_str); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + was_output = TRUE; + } + } + + if (NM_IS_DEVICE_BT (device)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[15].name)) { + tmpl = nmc_fields_dev_show_bluetooth; + tmpl_len = sizeof (nmc_fields_dev_show_bluetooth); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_BLUETOOTH_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[15].name); /* "BLUETOOTH" */ + set_val_str (arr, 1, bluetooth_caps_to_string (nm_device_bt_get_capabilities (NM_DEVICE_BT (device)))); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + was_output = TRUE; + } + } + + /* section CONNECTIONS */ + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[16].name)) { + const GPtrArray *avail_cons; + GString *ac_paths_str; + char **ac_arr = NULL; + int i; + + tmpl = nmc_fields_dev_show_connections; + tmpl_len = sizeof (nmc_fields_dev_show_connections); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_CONNECTIONS_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + /* available-connections */ + avail_cons = nm_device_get_available_connections (device); + ac_paths_str = g_string_new (NULL); + if (avail_cons->len) { + ac_arr = g_new (char *, avail_cons->len + 1); + ac_arr[avail_cons->len] = NULL; + } + for (i = 0; i < avail_cons->len; i++) { + NMRemoteConnection *avail_con = g_ptr_array_index (avail_cons, i); + const char *ac_path = nm_connection_get_path (NM_CONNECTION (avail_con)); + const char *ac_id = nm_connection_get_id (NM_CONNECTION (avail_con)); + const char *ac_uuid = nm_connection_get_uuid (NM_CONNECTION (avail_con)); + + ac_arr[i] = g_strdup_printf ("%s | %s", ac_uuid, ac_id); + + if (i == 0) + g_string_printf (ac_paths_str, "%s/{", NM_DBUS_PATH_SETTINGS); + else + g_string_append_c (ac_paths_str, ','); + g_string_append (ac_paths_str, strrchr (ac_path, '/') + 1); + } + if (ac_paths_str->len > 0) + g_string_append_c (ac_paths_str, '}'); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[16].name); /* "CONNECTIONS" */ + set_val_str (arr, 1, ac_paths_str->str); + set_val_arr (arr, 2, (ac_arr)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + g_string_free (ac_paths_str, FALSE); + was_output = TRUE; + } + } + + if (sections_array) + g_array_free (sections_array, TRUE); + if (fields_in_section) + g_ptr_array_free (fields_in_section, TRUE); + + return TRUE; +} + +static void +fill_output_device_status (NMDevice *device, NmCli *nmc) +{ + NMActiveConnection *ac; + NmcOutputField *arr = nmc_dup_fields_array (nmc_fields_dev_status, + sizeof (nmc_fields_dev_status), + 0); + + ac = nm_device_get_active_connection (device); + + set_val_strc (arr, 0, nm_device_get_iface (device)); + set_val_strc (arr, 1, nm_device_get_type_description (device)); + set_val_strc (arr, 2, nmc_device_state_to_string (nm_device_get_state (device))); + set_val_strc (arr, 3, nm_object_get_path (NM_OBJECT (device))); + set_val_strc (arr, 4, get_active_connection_id (device)); + set_val_strc (arr, 5, ac ? nm_active_connection_get_uuid (ac) : NULL); + set_val_strc (arr, 6, ac ? nm_object_get_path (NM_OBJECT (ac)) : NULL); + + g_ptr_array_add (nmc->output_data, arr); +} + +static NMCResultCode +do_devices_status (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + NMDevice **devices; + int i; + char *fields_str; + char *fields_all = NMC_FIELDS_DEV_STATUS_ALL; + char *fields_common = NMC_FIELDS_DEV_STATUS_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + while (argc > 0) { + g_printerr (_("Unknown parameter: %s\n"), *argv); + argc--; + argv++; + } + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + tmpl = nmc_fields_dev_status; + tmpl_len = sizeof (nmc_fields_dev_status); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); + + if (error) { + g_string_printf (nmc->return_text, _("Error: 'device status': %s"), error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + /* Add headers */ + nmc->print_fields.header_name = _("Status of devices"); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + devices = get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) + fill_output_device_status (devices[i], nmc); + + /* Now print all data */ + print_data (nmc); + + g_free (devices); + + return NMC_RESULT_SUCCESS; + +error: + return nmc->return_value; +} + +static NMCResultCode +do_devices_show (NmCli *nmc, int argc, char **argv) +{ + NMDevice **devices = NULL; + NMDevice *device = NULL; + const char *ifname = NULL; + int i; + gboolean ret; + + if (argc == 1) + ifname = *argv; + else if (argc > 1) { + g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *(argv+1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + devices = get_devices_sorted (nmc->client); + + if (ifname) { + /* Interface specified; show details only for the device */ + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; + const char *dev_iface = nm_device_get_iface (candidate); + + if (!g_strcmp0 (dev_iface, ifname)) + device = candidate; + } + if (!device) { + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + show_device_info (device, nmc); + } else { + /* Show details for all devices */ + for (i = 0; devices[i]; i++) { + nmc_empty_output_fields (nmc); + ret = show_device_info (devices[i], nmc); + if (!ret) + break; + if (devices[i + 1]) + g_print ("\n"); /* Empty line */ + } + } + +error: + g_free (devices); + return nmc->return_value; +} + +static gboolean +timeout_cb (gpointer user_data) +{ + /* Time expired -> exit nmcli */ + + NmCli *nmc = (NmCli *) user_data; + + g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout); + nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED; + quit (); + return FALSE; +} + +static gboolean +progress_cb (gpointer user_data) +{ + NMDevice *device = (NMDevice *) user_data; + + nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : ""); + + return TRUE; +} + +static void connected_state_cb (NMDevice *device, NMActiveConnection *active); + +static void +device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) +{ + NMActiveConnection *active = (NMActiveConnection *) user_data; + + connected_state_cb (device, active); +} + +static void +active_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data) +{ + NMDevice *device = (NMDevice *) user_data; + + connected_state_cb (device, active); +} + +static void +connected_state_cb (NMDevice *device, NMActiveConnection *active) +{ + NMDeviceState state; + NMDeviceStateReason reason; + NMActiveConnectionState ac_state; + + state = nm_device_get_state (device); + ac_state = nm_active_connection_get_state (active); + + if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) + return; + + if (state == NM_DEVICE_STATE_ACTIVATED) { + nmc_terminal_erase_line (); + g_print (_("Device '%s' successfully activated with '%s'.\n"), + nm_device_get_iface (device), + nm_active_connection_get_uuid (active)); + } else if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state >= NM_DEVICE_STATE_DEACTIVATING) { + reason = nm_device_get_state_reason (device); + g_print (_("Error: Connection activation failed: (%d) %s.\n"), + reason, nmc_device_reason_to_string (reason)); + } else + return; + + g_signal_handlers_disconnect_by_func (active, G_CALLBACK (active_state_cb), device); + g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_state_cb), active); + + g_object_unref (active); + g_object_unref (device); + + quit (); +} + +typedef struct { + NmCli *nmc; + NMDevice *device; +} AddAndActivateInfo; + +static void +add_and_activate_cb (GObject *client, + GAsyncResult *result, + gpointer user_data) +{ + AddAndActivateInfo *info = (AddAndActivateInfo *) user_data; + NmCli *nmc = info->nmc; + NMDevice *device = info->device; + NMActiveConnectionState state; + NMActiveConnection *active; + GError *error = NULL; + + active = nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error); + + if (error) { + g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: %s"), + error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + quit (); + } else { + state = nm_active_connection_get_state (active); + + if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) { + g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: Unknown error")); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + g_object_unref (active); + quit (); + } + + if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + /* User doesn't want to wait or already activated */ + if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("Connection with UUID '%s' created and activated on device '%s'\n"), + nm_active_connection_get_uuid (active), nm_device_get_iface (device)); + } + g_object_unref (active); + quit (); + } else { + g_object_ref (device); + g_signal_connect (device, "notify::state", G_CALLBACK (device_state_cb), active); + g_signal_connect (active, "notify::state", G_CALLBACK (active_state_cb), device); + + g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); /* Exit if timeout expires */ + + if (nmc->print_output == NMC_PRINT_PRETTY) + progress_id = g_timeout_add (120, progress_cb, device); + } + } + + g_free (info); +} + +static void +create_connect_connection_for_device (AddAndActivateInfo *info) +{ + NMConnection *connection; + NMSettingConnection *s_con; + + /* Create new connection and tie it to the device */ + connection = nm_simple_connection_new (); + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, nm_device_get_iface (info->device), + NM_SETTING_CONNECTION_INTERFACE_NAME, nm_device_get_iface (info->device), + NULL); + + nm_client_add_and_activate_connection_async (info->nmc->client, + connection, + info->device, + NULL, + NULL, + add_and_activate_cb, + info); +} + +static void +connect_device_cb (GObject *client, GAsyncResult *result, gpointer user_data) +{ + AddAndActivateInfo *info = (AddAndActivateInfo *) user_data; + NmCli *nmc = info->nmc; + NMActiveConnection *active; + GError *error = NULL; + const GPtrArray *devices; + NMDevice *device; + NMDeviceState state; + + active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error); + + if (error) { + /* If no connection existed for the device, create one and activate it */ + if (g_error_matches (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION)) { + create_connect_connection_for_device (info); + return; + } + + g_string_printf (nmc->return_text, _("Error: Device activation failed: %s"), + error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + quit (); + } else { + g_assert (active); + devices = nm_active_connection_get_devices (active); + if (devices->len == 0) { + g_string_printf (nmc->return_text, _("Error: Device activation failed: device was disconnected")); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + g_object_unref (active); + quit (); + g_free (info); + return; + } + + device = g_ptr_array_index (devices, 0); + state = nm_device_get_state (device); + + if (nmc->nowait_flag || state == NM_DEVICE_STATE_ACTIVATED) { + /* Don't want to wait or device already activated */ + if (state == NM_DEVICE_STATE_ACTIVATED && nmc->print_output == NMC_PRINT_PRETTY) { + nmc_terminal_erase_line (); + g_print (_("Device '%s' has been connected.\n"), nm_device_get_iface (device)); + } + g_object_unref (active); + quit (); + } else { + if (nmc->secret_agent) { + NMRemoteConnection *connection = nm_active_connection_get_connection (active); + + nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent), + nm_connection_get_path (NM_CONNECTION (connection))); + } + + g_object_ref (device); + g_signal_connect (device, "notify::state", G_CALLBACK (device_state_cb), active); + g_signal_connect (active, "notify::state", G_CALLBACK (active_state_cb), device); + /* Start timer not to loop forever if "notify::state" signal is not issued */ + g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); + } + } + g_free (info); +} + +static NMCResultCode +do_device_connect (NmCli *nmc, int argc, char **argv) +{ + NMDevice **devices; + NMDevice *device = NULL; + const char *ifname = NULL; + char *ifname_ask = NULL; + int i; + AddAndActivateInfo *info; + + /* Set default timeout for connect operation. */ + if (nmc->timeout == -1) + nmc->timeout = 90; + + if (argc == 0) { + if (nmc->ask) + ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE); + + if (!ifname_ask) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else { + ifname = *argv; + } + + if (!ifname) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + if (next_arg (&argc, &argv) == 0) { + g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + devices = get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; + const char *dev_iface = nm_device_get_iface (candidate); + + if (!g_strcmp0 (dev_iface, ifname)) + device = candidate; + } + g_free (devices); + + if (!device) { + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + /* + * Use nowait_flag instead of should_wait, because exiting has to be postponed + * till connect_device_cb() is called, giving NM time to check our permissions. + */ + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + + /* Create secret agent */ + nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-connect"); + if (nmc->secret_agent) + g_signal_connect (nmc->secret_agent, "request-secrets", G_CALLBACK (nmc_secrets_requested), nmc); + + info = g_malloc0 (sizeof (AddAndActivateInfo)); + info->nmc = nmc; + info->device = device; + + nm_client_activate_connection_async (nmc->client, + NULL, /* let NM find a connection automatically */ + device, + NULL, + NULL, + connect_device_cb, + info); + + /* Start progress indication */ + if (nmc->print_output == NMC_PRINT_PRETTY) + progress_id = g_timeout_add (120, progress_cb, device); + +error: + g_free (ifname_ask); + + return nmc->return_value; +} + +static void +disconnect_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) +{ + NMDeviceState state; + + state = nm_device_get_state (device); + + if (state == NM_DEVICE_STATE_DISCONNECTED) { + g_signal_handlers_disconnect_by_data (device, user_data); + g_print (_("Device '%s' successfully disconnected.\n"), + nm_device_get_iface (device)); + quit (); + } +} + +static void +device_removed_cb (NMClient *client, NMDevice *device, gpointer user_data) +{ + /* Success: device has been removed. It happens when disconnecting a software device. */ + g_signal_handlers_disconnect_by_data (client, user_data); + g_print (_("Device '%s' successfully disconnected.\n"), + nm_device_get_iface (device)); + quit (); +} + +static void +disconnect_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (object); + NmCli *nmc = (NmCli *) user_data; + NMDeviceState state; + GError *error = NULL; + + if (!nm_device_disconnect_finish (device, result, &error)) { + g_string_printf (nmc->return_text, _("Error: Device '%s' (%s) disconnecting failed: %s"), + nm_device_get_iface (device), + nm_object_get_path (NM_OBJECT (device)), + error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_DEV_DISCONNECT; + quit (); + } else { + state = nm_device_get_state (device); + + if (nmc->nowait_flag || state == NM_DEVICE_STATE_DISCONNECTED) { + /* Don't want to wait or device already disconnected */ + if (state == NM_DEVICE_STATE_DISCONNECTED) { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("Device '%s' has been disconnected.\n"), nm_device_get_iface (device)); + } + quit (); + } else { + g_signal_connect (device, "notify::state", G_CALLBACK (disconnect_state_cb), nmc); + g_signal_connect (nmc->client, NM_CLIENT_DEVICE_REMOVED, G_CALLBACK (device_removed_cb), nmc); + /* Start timer not to loop forever if "notify::state" signal is not issued */ + g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); + } + + } +} + +static NMCResultCode +do_device_disconnect (NmCli *nmc, int argc, char **argv) +{ + NMDevice **devices; + NMDevice *device = NULL; + const char *ifname = NULL; + char *ifname_ask = NULL; + int i; + + /* Set default timeout for disconnect operation. */ + if (nmc->timeout == -1) + nmc->timeout = 10; + + if (argc == 0) { + if (nmc->ask) + ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE); + + if (!ifname_ask) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else { + ifname = *argv; + } + + if (!ifname) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + if (next_arg (&argc, &argv) == 0) { + g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + devices = get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; + const char *dev_iface = nm_device_get_iface (candidate); + + if (!g_strcmp0 (dev_iface, ifname)) + device = candidate; + } + g_free (devices); + + if (!device) { + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + /* + * Use nowait_flag instead of should_wait, because exiting has to be postponed + * till disconnect_device_cb() is called, giving NM time to check our permissions. + */ + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + nm_device_disconnect_async (device, NULL, disconnect_device_cb, nmc); + + /* Start progress indication */ + if (nmc->print_output == NMC_PRINT_PRETTY) + progress_id = g_timeout_add (120, progress_cb, device); + +error: + return nmc->return_value; +} + +static void +delete_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (object); + NmCli *nmc = (NmCli *) user_data; + GError *error = NULL; + + if (!nm_device_delete_finish (device, result, &error)) { + g_string_printf (nmc->return_text, _("Error: Device '%s' (%s) deletion failed: %s"), + nm_device_get_iface (device), + nm_object_get_path (NM_OBJECT (device)), + error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + } + quit (); +} + +static NMCResultCode +do_device_delete (NmCli *nmc, int argc, char **argv) +{ + NMDevice **devices; + NMDevice *device = NULL; + const char *ifname = NULL; + char *ifname_ask = NULL; + int i; + + /* Set default timeout for delete operation. */ + if (nmc->timeout == -1) + nmc->timeout = 10; + + if (argc == 0) { + if (nmc->ask) + ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE); + + if (!ifname_ask) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else + ifname = *argv; + + if (!ifname) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + if (next_arg (&argc, &argv) == 0) { + g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + devices = get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; + const char *dev_iface = nm_device_get_iface (candidate); + + if (!g_strcmp0 (dev_iface, ifname)) + device = candidate; + } + g_free (devices); + + if (!device) { + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + if (!nm_device_is_software (device)) { + g_string_printf (nmc->return_text, _("Error: Device '%s' is a hardware device. It can't be deleted."), ifname); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + /* + * Use nowait_flag instead of should_wait, because exiting has to be postponed + * till delete_device_cb() is called, giving NM time to check our permissions. + */ + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + nm_device_delete_async (device, NULL, delete_device_cb, nmc); + + /* Start progress indication */ + if (nmc->print_output == NMC_PRINT_PRETTY) + progress_id = g_timeout_add (120, progress_cb, device); + +error: + return nmc->return_value; +} + +static void +show_access_point_info (NMDevice *device, NmCli *nmc) +{ + NMAccessPoint *active_ap = NULL; + const char *active_bssid = NULL; + const GPtrArray *aps; + APInfo *info; + NmcOutputField *arr; + + if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { + active_ap = nm_device_wifi_get_active_access_point (NM_DEVICE_WIFI (device)); + active_bssid = active_ap ? nm_access_point_get_bssid (active_ap) : NULL; + } + + arr = nmc_dup_fields_array (nmc_fields_dev_wifi_list, sizeof (nmc_fields_dev_wifi_list), + NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + info = g_malloc0 (sizeof (APInfo)); + info->nmc = nmc; + info->index = 1; + info->output_flags = 0; + info->active_bssid = active_bssid; + info->device = nm_device_get_iface (device); + aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); + g_ptr_array_foreach ((GPtrArray *) aps, fill_output_access_point, (gpointer) info); + + print_data (nmc); /* Print all data */ + nmc_empty_output_fields (nmc); + g_free (info); +} + +static NMCResultCode +do_device_wifi_list (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + NMDevice *device = NULL; + NMAccessPoint *ap = NULL; + const char *ifname = NULL; + const char *bssid_user = NULL; + NMDevice **devices = NULL; + const GPtrArray *aps; + APInfo *info; + int i, j; + char *fields_str; + char *fields_all = NMC_FIELDS_DEV_WIFI_LIST_ALL; + char *fields_common = NMC_FIELDS_DEV_WIFI_LIST_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + const char *base_hdr = _("Wi-Fi scan list"); + + while (argc > 0) { + if (strcmp (*argv, "ifname") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + ifname = *argv; + } else if (strcmp (*argv, "bssid") == 0 || strcmp (*argv, "hwaddr") == 0) { + /* hwaddr is deprecated and will be removed later */ + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + bssid_user = *argv; + } else { + g_printerr (_("Unknown parameter: %s\n"), *argv); + } + + argc--; + argv++; + } + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + tmpl = nmc_fields_dev_wifi_list; + tmpl_len = sizeof (nmc_fields_dev_wifi_list); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); + + if (error) { + g_string_printf (nmc->return_text, _("Error: 'device wifi': %s"), error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + devices = get_devices_sorted (nmc->client); + if (ifname) { + /* Device specified - list only APs of this interface */ + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; + const char *dev_iface = nm_device_get_iface (candidate); + + if (!g_strcmp0 (dev_iface, ifname)) { + device = candidate; + break; + } + } + if (!device) { + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, ifname); + + if (NM_IS_DEVICE_WIFI (device)) { + if (bssid_user) { + /* Specific AP requested - list only that */ + aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); + for (j = 0; j < aps->len; j++) { + char *bssid_up; + NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j); + const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); + + bssid_up = g_ascii_strup (bssid_user, -1); + if (!strcmp (bssid_up, candidate_bssid)) + ap = candidate_ap; + g_free (bssid_up); + } + if (!ap) { + g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), + bssid_user); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + /* Add headers (field names) */ + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + info = g_malloc0 (sizeof (APInfo)); + info->nmc = nmc; + info->index = 1; + info->output_flags = 0; + info->active_bssid = NULL; + info->device = nm_device_get_iface (device); + + fill_output_access_point (ap, info); + + print_data (nmc); /* Print all data */ + g_free (info); + } else { + show_access_point_info (device, nmc); + } + } else { + g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto error; + } + } else { + gboolean empty_line = FALSE; + + /* List APs for all devices */ + if (bssid_user) { + /* Specific AP requested - list only that */ + for (i = 0; devices[i]; i++) { + NMDevice *dev = devices[i]; + + if (!NM_IS_DEVICE_WIFI (dev)) + continue; + + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_device_get_iface (dev)); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, NULL); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev)); + for (j = 0; j < aps->len; j++) { + char *bssid_up; + NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j); + const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); + + bssid_up = g_ascii_strup (bssid_user, -1); + if (!strcmp (bssid_up, candidate_bssid)) { + ap = candidate_ap; + + info = g_malloc0 (sizeof (APInfo)); + info->nmc = nmc; + info->index = 1; + info->output_flags = 0; + info->active_bssid = NULL; + info->device = nm_device_get_iface (dev); + fill_output_access_point (ap, info); + g_free (info); + } + g_free (bssid_up); + } + if (empty_line) + g_print ("\n"); /* Empty line between devices' APs */ + print_data (nmc); /* Print all data */ + nmc_empty_output_fields (nmc); + empty_line = TRUE; + } + if (!ap) { + g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), + bssid_user); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + } else { + for (i = 0; devices[i]; i++) { + NMDevice *dev = devices[i]; + + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, + nm_device_get_iface (dev)); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, NULL); + + if (NM_IS_DEVICE_WIFI (dev)) { + if (empty_line) + g_print ("\n"); /* Empty line between devices' APs */ + show_access_point_info (dev, nmc); + empty_line = TRUE; + } + } + } + } + +error: + g_free (devices); + return nmc->return_value; +} + +/* + * Find a Wi-Fi device with 'iface' in 'devices' array. If 'iface' is NULL, + * the first Wi-Fi device is returned. 'idx' parameter is updated to the point + * where the function finished so that the function can be called repeatedly + * to get next matching device. + * Returns: found device or NULL + */ +static NMDevice * +find_wifi_device_by_iface (const GPtrArray *devices, const char *iface, int *idx) +{ + NMDevice *device = NULL; + int i; + + for (i = *idx; i < devices->len; i++) { + NMDevice *candidate = g_ptr_array_index (devices, i); + const char *dev_iface = nm_device_get_iface (candidate); + + if (!NM_IS_DEVICE_WIFI (candidate)) + continue; + + if (iface) { + /* If a iface was specified then use it. */ + if (g_strcmp0 (dev_iface, iface) == 0) { + device = candidate; + break; + } + } else { + /* Else return the first Wi-Fi device. */ + device = candidate; + break; + } + } + + *idx = i + 1; + return device; +} + +/* + * Find AP on 'device' according to 'bssid' or 'ssid' parameter. + * Returns: found AP or NULL + */ +static NMAccessPoint * +find_ap_on_device (NMDevice *device, GByteArray *bssid, const char *ssid) +{ + const GPtrArray *aps; + NMAccessPoint *ap = NULL; + int i; + + g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL); + g_return_val_if_fail ((bssid && !ssid) || (!bssid && ssid), NULL); + + aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); + for (i = 0; i < aps->len; i++) { + NMAccessPoint *candidate_ap = g_ptr_array_index (aps, i); + + if (ssid) { + /* Parameter is SSID */ + GBytes *candidate_ssid; + + candidate_ssid = nm_access_point_get_ssid (candidate_ap); + if (candidate_ssid) { + char *ssid_tmp = nm_utils_ssid_to_utf8 (g_bytes_get_data (candidate_ssid, NULL), + g_bytes_get_size (candidate_ssid)); + + /* Compare SSIDs */ + if (strcmp (ssid, ssid_tmp) == 0) { + ap = candidate_ap; + g_free (ssid_tmp); + break; + } + g_free (ssid_tmp); + } + } else if (bssid) { + /* Parameter is BSSID */ + const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); + char *bssid_up = nm_utils_hwaddr_ntoa (bssid->data, bssid->len); + + /* Compare BSSIDs */ + if (strcmp (bssid_up, candidate_bssid) == 0) { + ap = candidate_ap; + g_free (bssid_up); + break; + } + g_free (bssid_up); + } + } + + return ap; +} + +static NMCResultCode +do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) +{ + NMDevice *device = NULL; + NMAccessPoint *ap = NULL; + NM80211ApFlags ap_flags; + NM80211ApSecurityFlags ap_wpa_flags; + NM80211ApSecurityFlags ap_rsn_flags; + NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + AddAndActivateInfo *info; + const char *param_user = NULL; + const char *ifname = NULL; + const char *bssid = NULL; + const char *password = NULL; + const char *con_name = NULL; + gboolean private = FALSE; + gboolean wep_passphrase = FALSE; + GByteArray *bssid1_arr = NULL; + GByteArray *bssid2_arr = NULL; + const GPtrArray *devices; + int devices_idx; + char *ssid_ask = NULL; + char *passwd_ask = NULL; + + /* Set default timeout waiting for operation completion. */ + if (nmc->timeout == -1) + nmc->timeout = 90; + + /* Get the first compulsory argument (SSID or BSSID) */ + if (argc > 0) { + param_user = *argv; + bssid1_arr = nm_utils_hwaddr_atoba (param_user, ETH_ALEN); + + argc--; + argv++; + } else { + if (nmc->ask) { + ssid_ask = nmc_readline (_("SSID or BSSID: ")); + param_user = ssid_ask ? ssid_ask : ""; + bssid1_arr = nm_utils_hwaddr_atoba (param_user, ETH_ALEN); + } + if (!ssid_ask) { + g_string_printf (nmc->return_text, _("Error: SSID or BSSID are missing.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + + /* Get the rest of the parameters */ + while (argc > 0) { + if (strcmp (*argv, "ifname") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + ifname = *argv; + } else if (strcmp (*argv, "bssid") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + bssid = *argv; + bssid2_arr = nm_utils_hwaddr_atoba (bssid, ETH_ALEN); + if (!bssid2_arr) { + g_string_printf (nmc->return_text, _("Error: bssid argument value '%s' is not a valid BSSID."), + bssid); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else if (strcmp (*argv, "password") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + password = *argv; + } else if (strcmp (*argv, "wep-key-type") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + if (strcmp (*argv, "key") == 0) + wep_passphrase = FALSE; + else if (strcmp (*argv, "phrase") == 0) + wep_passphrase = TRUE; + else { + g_string_printf (nmc->return_text, + _("Error: wep-key-type argument value '%s' is invalid, use 'key' or 'phrase'."), + *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else if (strcmp (*argv, "name") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + con_name = *argv; + } else if (strcmp (*argv, "private") == 0) { + GError *err_tmp = NULL; + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + if (!nmc_string_to_bool (*argv, &private, &err_tmp)) { + g_string_printf (nmc->return_text, _("Error: %s: %s."), *(argv-1), err_tmp->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_clear_error (&err_tmp); + goto error; + } + } else { + g_printerr (_("Unknown parameter: %s\n"), *argv); + } + + argc--; + argv++; + } + + /* Verify SSID/BSSID parameters */ + if (bssid1_arr && bssid2_arr && memcmp (bssid1_arr->data, bssid2_arr->data, ETH_ALEN)) { + g_string_printf (nmc->return_text, _("Error: BSSID to connect to (%s) differs from bssid argument (%s)."), + param_user, bssid); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + if (!bssid1_arr && strlen (param_user) > 32) { + g_string_printf (nmc->return_text, _("Error: Parameter '%s' is neither SSID nor BSSID."), param_user); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + devices = nm_client_get_devices (nmc->client); + + /* Find a device to activate the connection on */ + devices_idx = 0; + device = find_wifi_device_by_iface (devices, ifname, &devices_idx); + + if (!device) { + if (ifname) + g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname); + else + g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto error; + } + + /* Find an AP to connect to */ + ap = find_ap_on_device (device, bssid1_arr, bssid1_arr ? NULL : param_user); + if (!ap && !ifname) { + /* AP not found. ifname was not specified, so try finding the AP on another device. */ + while ((device = find_wifi_device_by_iface (devices, NULL, &devices_idx)) != NULL) { + ap = find_ap_on_device (device, bssid1_arr, bssid1_arr ? NULL : param_user); + if (ap) + break; + } + } + + if (!ap) { + if (!bssid1_arr) + g_string_printf (nmc->return_text, _("Error: No network with SSID '%s' found."), param_user); + else + g_string_printf (nmc->return_text, _("Error: No access point with BSSID '%s' found."), param_user); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + /* If there are some connection data from user, create a connection and + * fill them into proper settings. */ + if (con_name || private || bssid2_arr || password) + connection = nm_simple_connection_new (); + + if (con_name || private) { + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + /* Set user provided connection name */ + if (con_name) + g_object_set (s_con, NM_SETTING_CONNECTION_ID, con_name, NULL); + + /* Connection will only be visible to this user when '--private' is specified */ + if (private) + nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL); + } + if (bssid2_arr) { + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + /* 'bssid' parameter is used to restrict the conenction only to the BSSID */ + g_object_set (s_wifi, NM_SETTING_WIRELESS_BSSID, bssid2_arr, NULL); + } + + /* handle password */ + ap_flags = nm_access_point_get_flags (ap); + ap_wpa_flags = nm_access_point_get_wpa_flags (ap); + ap_rsn_flags = nm_access_point_get_rsn_flags (ap); + + /* Set password for WEP or WPA-PSK. */ + if (ap_flags & NM_802_11_AP_FLAGS_PRIVACY) { + /* Ask for missing password when one is expected and '--ask' is used */ + if (!password && nmc->ask) + password = passwd_ask = nmc_readline (_("Password: ")); + + if (password) { + if (!connection) + connection = nm_simple_connection_new (); + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + + if (ap_wpa_flags == NM_802_11_AP_SEC_NONE && ap_rsn_flags == NM_802_11_AP_SEC_NONE) { + /* WEP */ + nm_setting_wireless_security_set_wep_key (s_wsec, 0, password); + g_object_set (G_OBJECT (s_wsec), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, + wep_passphrase ? NM_WEP_KEY_TYPE_PASSPHRASE: NM_WEP_KEY_TYPE_KEY, + NULL); + } else if ( !(ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + && !(ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + /* WPA PSK */ + g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_PSK, password, NULL); + } + } + } + // FIXME: WPA-Enterprise is not supported yet. + // We are not able to determine and fill all the parameters for + // 802.1X authentication automatically without user providing + // the data. Adding nmcli options for the 8021x setting would + // clutter the command. However, that could be solved later by + // implementing add/edit connections support for nmcli. + + /* nowait_flag indicates user input. should_wait says whether quit in start(). + * We have to delay exit after add_and_activate_cb() is called, even if + * the user doesn't want to wait, in order to give NM time to check our + * permissions. */ + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + + info = g_malloc0 (sizeof (AddAndActivateInfo)); + info->nmc = nmc; + info->device = device; + + nm_client_add_and_activate_connection_async (nmc->client, + connection, + device, + nm_object_get_path (NM_OBJECT (ap)), + NULL, + add_and_activate_cb, + info); + +error: + if (bssid1_arr) + g_byte_array_free (bssid1_arr, TRUE); + if (bssid2_arr) + g_byte_array_free (bssid2_arr, TRUE); + g_free (ssid_ask); + g_free (passwd_ask); + + return nmc->return_value; +} + +static void +request_rescan_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + GError *error = NULL; + + nm_device_wifi_request_scan_finish (NM_DEVICE_WIFI (object), result, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_error_free (error); + } + quit (); +} + +static NMCResultCode +do_device_wifi_rescan (NmCli *nmc, int argc, char **argv) +{ + NMDevice *device; + const char *ifname = NULL; + const GPtrArray *devices; + int devices_idx; + + nmc->should_wait = TRUE; + + /* Get the parameters */ + if (argc > 0) { + if (strcmp (*argv, "ifname") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + ifname = *argv; + } + + /* Find Wi-Fi device to scan on. When no ifname is provided, the first Wi-Fi is used. */ + devices = nm_client_get_devices (nmc->client); + devices_idx = 0; + device = find_wifi_device_by_iface (devices, ifname, &devices_idx); + + if (!device) { + if (ifname) + g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname); + else + g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto error; + } + + nm_device_wifi_request_scan_async (NM_DEVICE_WIFI (device), NULL, + request_rescan_cb, nmc); + + return nmc->return_value; +error: + nmc->should_wait = FALSE; + return nmc->return_value; +} + +static NMCResultCode +do_device_wifi (NmCli *nmc, int argc, char **argv) +{ + if (argc == 0) + nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1); + else if (argc > 0) { + if (matches (*argv, "list") == 0) { + nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1); + } else if (matches (*argv, "connect") == 0) { + nmc->return_value = do_device_wifi_connect_network (nmc, argc-1, argv+1); + } else if (matches (*argv, "rescan") == 0) { + nmc->return_value = do_device_wifi_rescan (nmc, argc-1, argv+1); + } else { + g_string_printf (nmc->return_text, _("Error: 'device wifi' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + + return nmc->return_value; +} + +#if WITH_WIMAX +static void +show_nsp_info (NMDevice *device, NmCli *nmc) +{ + const GPtrArray *nsps; + int i, idx = 1; + NmcOutputField *arr; + + /* Add headers (field names) */ + arr = nmc_dup_fields_array (nmc_fields_dev_wimax_list, sizeof (nmc_fields_dev_wimax_list), + NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (device)); + for (i = 0; i < nsps->len; i++) { + NMWimaxNsp *nsp = g_ptr_array_index (nsps, i); + + fill_output_wimax_nsp (nsp, nmc, device, idx++, 0); + } + print_data (nmc); /* Print all data */ + nmc_empty_output_fields (nmc); +} + +static NMCResultCode +do_device_wimax_list (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + NMDevice *device = NULL; + NMWimaxNsp *nsp = NULL; + const char *ifname = NULL; + const char *nsp_user = NULL; + const GPtrArray *devices; + const GPtrArray *nsps; + int i, j; + char *fields_str; + char *fields_all = NMC_FIELDS_DEV_WIMAX_LIST_ALL; + char *fields_common = NMC_FIELDS_DEV_WIMAX_LIST_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + const char *base_hdr = _("WiMAX NSP list"); + + while (argc > 0) { + if (strcmp (*argv, "ifname") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + ifname = *argv; + } else if (strcmp (*argv, "nsp") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + nsp_user = *argv; + } else { + g_printerr (_("Unknown parameter: %s\n"), *argv); + } + + argc--; + argv++; + } + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + tmpl = nmc_fields_dev_wimax_list; + tmpl_len = sizeof (nmc_fields_dev_wimax_list); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); + + if (error) { + g_string_printf (nmc->return_text, _("Error: 'device wimax': %s"), error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + devices = nm_client_get_devices (nmc->client); + if (ifname) { + /* Device specified - list only NSPs of this interface */ + for (i = 0; i < devices->len; i++) { + NMDevice *candidate = g_ptr_array_index (devices, i); + const char *dev_iface = nm_device_get_iface (candidate); + + if (!g_strcmp0 (dev_iface, ifname)) { + device = candidate; + break; + } + } + + if (!device) { + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, ifname); + + if (NM_IS_DEVICE_WIMAX (device)) { + if (nsp_user) { + /* Specific NSP requested - list only that */ + nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (device)); + for (j = 0, nsp = NULL; j < nsps->len; j++) { + NMWimaxNsp *candidate_nsp = g_ptr_array_index (nsps, j); + const char *candidate_name = nm_wimax_nsp_get_name (candidate_nsp); + char *nsp_up; + + nsp_up = g_ascii_strup (nsp_user, -1); + if (!strcmp (nsp_up, candidate_name)) + nsp = candidate_nsp; + g_free (nsp_up); + } + if (!nsp) { + g_string_printf (nmc->return_text, _("Error: NSP with name '%s' not found."), nsp_user); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + /* Add headers (field names) */ + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + fill_output_wimax_nsp (nsp, nmc, device, 1, 0); + print_data (nmc); /* Print all data */ + } else { + show_nsp_info (device, nmc); + } + } else { + g_string_printf (nmc->return_text, _("Error: Device '%s' is not a WiMAX device."), ifname); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto error; + } + } else { + gboolean empty_line = FALSE; + + /* List NSPs for all devices */ + if (nsp_user) { + /* Specific NSP requested - list only that */ + for (i = 0; i < devices->len; i++) { + NMDevice *dev = g_ptr_array_index (devices, i); + int idx = 1; + + if (!NM_IS_DEVICE_WIMAX (dev)) + continue; + + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_device_get_iface (dev)); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, NULL); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (dev)); + for (j = 0, nsp = NULL; j < nsps->len; j++) { + NMWimaxNsp *candidate_nsp = g_ptr_array_index (nsps, j); + const char *candidate_name = nm_wimax_nsp_get_name (candidate_nsp); + char *nsp_up; + + nsp_up = g_ascii_strup (nsp_user, -1); + if (!strcmp (nsp_up, candidate_name)) { + nsp = candidate_nsp; + fill_output_wimax_nsp (nsp, nmc, dev, idx, 0); + } + g_free (nsp_up); + } + if (empty_line) + g_print ("\n"); /* Empty line between devices' NSPs */ + print_data (nmc); /* Print all data */ + nmc_empty_output_fields (nmc); + empty_line = TRUE; + } + if (!nsp) { + g_string_printf (nmc->return_text, _("Error: Access point with nsp '%s' not found."), nsp_user); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + } else { + for (i = 0; i < devices->len; i++) { + NMDevice *dev = g_ptr_array_index (devices, i); + + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, + nm_device_get_iface (dev)); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, NULL); + + if (NM_IS_DEVICE_WIMAX (dev)) { + if (empty_line) + g_print ("\n"); /* Empty line between devices' NSPs */ + show_nsp_info (dev, nmc); + empty_line = TRUE; + } + } + } + } + +error: + return nmc->return_value; +} + +static NMCResultCode +do_device_wimax (NmCli *nmc, int argc, char **argv) +{ + if (argc == 0) + nmc->return_value = do_device_wimax_list (nmc, argc-1, argv+1); + else if (argc > 0) { + if (matches (*argv, "list") == 0) { + nmc->return_value = do_device_wimax_list (nmc, argc-1, argv+1); + } + else { + g_string_printf (nmc->return_text, _("Error: 'device wimax' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + + return nmc->return_value; +} +#endif + +static gboolean +is_single_word (const char* line) +{ + size_t n1, n2, n3; + + n1 = strspn (line, " \t"); + n2 = strcspn (line+n1, " \t\0") + n1; + n3 = strspn (line+n2, " \t"); + + if (n3 == 0) + return TRUE; + else + return FALSE; +} + +/* Global variable defined in nmcli.c */ +extern NmCli nm_cli; + +static char * +gen_func_ifnames (const char *text, int state) +{ + int i, j = 0; + const GPtrArray *devices; + const char **ifnames; + char *ret; + + nm_cli.get_client (&nm_cli); + devices = nm_client_get_devices (nm_cli.client); + if (devices->len == 0) + return NULL; + + ifnames = g_new (const char *, devices->len + 1); + for (i = 0; i < devices->len; i++) { + NMDevice *dev = g_ptr_array_index (devices, i); + const char *ifname = nm_device_get_iface (dev); + ifnames[j++] = ifname; + } + ifnames[j] = NULL; + + ret = nmc_rl_gen_func_basic (text, state, ifnames); + + g_free (ifnames); + return ret; +} + +static char ** +nmcli_device_tab_completion (const char *text, int start, int end) +{ + char **match_array = NULL; + rl_compentry_func_t *generator_func = NULL; + + /* Disable readline's default filename completion */ + rl_attempted_completion_over = 1; + + /* Disable appending space after completion */ + rl_completion_append_character = '\0'; + + if (!is_single_word (rl_line_buffer)) + return NULL; + + if (g_strcmp0 (rl_prompt, PROMPT_INTERFACE) == 0) + generator_func = gen_func_ifnames; + + if (generator_func) + match_array = rl_completion_matches (text, generator_func); + + return match_array; +} + +NMCResultCode +do_devices (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + + rl_attempted_completion_function = (rl_completion_func_t *) nmcli_device_tab_completion; + + /* Get NMClient object early */ + nmc->get_client (nmc); + + /* Check whether NetworkManager is running */ + if (!nm_client_get_nm_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return nmc->return_value; + } + /* Compare NM and nmcli versions */ + if (!nmc_versions_match (nmc)) + return nmc->return_value; + + if (argc == 0) { + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) + goto opt_error; + nmc->return_value = do_devices_status (nmc, 0, NULL); + } + + if (argc > 0) { + if (nmc_arg_is_help (*argv)) { + usage (); + goto usage_exit; + } + else if (matches (*argv, "status") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_status (); + goto usage_exit; + } + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) + goto opt_error; + nmc->return_value = do_devices_status (nmc, argc-1, argv+1); + } + else if (matches (*argv, "show") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_show (); + goto usage_exit; + } + if (!nmc->mode_specified) + nmc->multiline_output = TRUE; /* multiline mode is default for 'device show' */ + nmc->return_value = do_devices_show (nmc, argc-1, argv+1); + } + else if (matches (*argv, "connect") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_connect (); + goto usage_exit; + } + nmc->return_value = do_device_connect (nmc, argc-1, argv+1); + } + else if (matches (*argv, "disconnect") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_disconnect (); + goto usage_exit; + } + nmc->return_value = do_device_disconnect (nmc, argc-1, argv+1); + } + else if (matches (*argv, "delete") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_delete (); + goto usage_exit; + } + nmc->return_value = do_device_delete (nmc, argc-1, argv+1); + } + else if (matches (*argv, "wifi") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_wifi (); + goto usage_exit; + } + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) + goto opt_error; + nmc->return_value = do_device_wifi (nmc, argc-1, argv+1); + } +#if WITH_WIMAX + else if (matches (*argv, "wimax") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_wimax (); + goto usage_exit; + } + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) + goto opt_error; + nmc->return_value = do_device_wimax (nmc, argc-1, argv+1); + } +#endif + else { + usage (); + g_string_printf (nmc->return_text, _("Error: 'dev' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + +usage_exit: + return nmc->return_value; + +opt_error: + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_error_free (error); + return nmc->return_value; +} diff --git a/clients/cli/devices.h b/clients/cli/devices.h new file mode 100644 index 00000000..152dd207 --- /dev/null +++ b/clients/cli/devices.h @@ -0,0 +1,27 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2010 Red Hat, Inc. + */ + +#ifndef NMC_DEVICES_H +#define NMC_DEVICES_H + +#include "nmcli.h" + +NMCResultCode do_devices (NmCli *nmc, int argc, char **argv); + +#endif /* NMC_DEVICES_H */ diff --git a/clients/cli/general.c b/clients/cli/general.c new file mode 100644 index 00000000..44c63c35 --- /dev/null +++ b/clients/cli/general.c @@ -0,0 +1,899 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + */ + +#include "config.h" + +#include <string.h> +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "polkit-agent.h" +#include "utils.h" +#include "general.h" + + +/* Available fields for 'general status' */ +static NmcOutputField nmc_fields_nm_status[] = { + {"RUNNING", N_("RUNNING"), 15}, /* 0 */ + {"VERSION", N_("VERSION"), 10}, /* 1 */ + {"STATE", N_("STATE"), 15}, /* 2 */ + {"STARTUP", N_("STARTUP"), 10}, /* 3 */ + {"CONNECTIVITY", N_("CONNECTIVITY"), 15}, /* 4 */ + {"NETWORKING", N_("NETWORKING"), 13}, /* 5 */ + {"WIFI-HW", N_("WIFI-HW"), 15}, /* 6 */ + {"WIFI", N_("WIFI"), 10}, /* 7 */ + {"WWAN-HW", N_("WWAN-HW"), 15}, /* 8 */ + {"WWAN", N_("WWAN"), 10}, /* 9 */ + {"WIMAX-HW", N_("WIMAX-HW"), 15}, /* 10 */ + {"WIMAX", N_("WIMAX"), 10}, /* 11 */ + {NULL, NULL, 0} +}; +#if WITH_WIMAX +#define NMC_FIELDS_NM_STATUS_ALL "RUNNING,VERSION,STATE,STARTUP,CONNECTIVITY,NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN,WIMAX-HW,WIMAX" +#define NMC_FIELDS_NM_STATUS_SWITCH "NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN,WIMAX-HW,WIMAX" +#define NMC_FIELDS_NM_STATUS_RADIO "WIFI-HW,WIFI,WWAN-HW,WWAN,WIMAX-HW,WIMAX" +#else +#define NMC_FIELDS_NM_STATUS_ALL "RUNNING,VERSION,STATE,STARTUP,CONNECTIVITY,NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN" +#define NMC_FIELDS_NM_STATUS_SWITCH "NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN" +#define NMC_FIELDS_NM_STATUS_RADIO "WIFI-HW,WIFI,WWAN-HW,WWAN" +#endif +#define NMC_FIELDS_NM_STATUS_COMMON "STATE,CONNECTIVITY,WIFI-HW,WIFI,WWAN-HW,WWAN" +#define NMC_FIELDS_NM_NETWORKING "NETWORKING" +#define NMC_FIELDS_NM_WIFI "WIFI" +#define NMC_FIELDS_NM_WWAN "WWAN" +#define NMC_FIELDS_NM_WIMAX "WIMAX" +#define NMC_FIELDS_NM_CONNECTIVITY "CONNECTIVITY" + + +/* Available fields for 'general permissions' */ +static NmcOutputField nmc_fields_nm_permissions[] = { + {"PERMISSION", N_("PERMISSION"), 57}, /* 0 */ + {"VALUE", N_("VALUE"), 10}, /* 1 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_NM_PERMISSIONS_ALL "PERMISSION,VALUE" +#define NMC_FIELDS_NM_PERMISSIONS_COMMON "PERMISSION,VALUE" + +/* Available fields for 'general logging' */ +static NmcOutputField nmc_fields_nm_logging[] = { + {"LEVEL", N_("LEVEL"), 10}, /* 0 */ + {"DOMAINS", N_("DOMAINS"), 70}, /* 1 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_NM_LOGGING_ALL "LEVEL,DOMAINS" +#define NMC_FIELDS_NM_LOGGING_COMMON "LEVEL,DOMAINS" + + +/* glib main loop variable - defined in nmcli.c */ +extern GMainLoop *loop; + + +static void +usage_general (void) +{ + g_printerr (_("Usage: nmcli general { COMMAND | help }\n\n" + "COMMAND := { status | hostname | permissions | logging }\n\n" + " status\n\n" + " hostname [<hostname>]\n\n" + " permissions\n\n" + " logging [level <log level>] [domains <log domains>]\n\n")); +} + +static void +usage_general_status (void) +{ + g_printerr (_("Usage: nmcli general status { help }\n" + "\n" + "Show overall status of NetworkManager.\n" + "'status' is the default action, which means 'nmcli gen' calls 'nmcli gen status'\n\n")); +} + +static void +usage_general_hostname (void) +{ + g_printerr (_("Usage: nmcli general hostname { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [<hostname>]\n" + "\n" + "Get or change persistent system hostname.\n" + "With no arguments, this prints currently configured hostname. When you pass\n" + "a hostname, NetworkManager will set it as the new persistent system hostname.\n\n")); +} + +static void +usage_general_permissions (void) +{ + g_printerr (_("Usage: nmcli general permissions { help }\n" + "\n" + "Show caller permissions for authenticated operations.\n\n")); +} + +static void +usage_general_logging (void) +{ + g_printerr (_("Usage: nmcli general logging { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [level <log level>] [domains <log domains>]\n" + "\n" + "Get or change NetworkManager logging level and domains.\n" + "Without any argument current logging level and domains are shown. In order to\n" + "change logging state, provide level and/or domain. Please refer to the man page\n" + "for the list of possible logging domains.\n\n")); +} + +static void +usage_networking (void) +{ + g_printerr (_("Usage: nmcli networking { COMMAND | help }\n\n" + "COMMAND := { [ on | off | connectivity ] }\n\n" + " on\n\n" + " off\n\n" + " connectivity [check]\n\n")); +} + +static void +usage_networking_on (void) +{ + g_printerr (_("Usage: nmcli networking on { help }\n" + "\n" + "Switch networking on.\n\n")); +} + +static void +usage_networking_off (void) +{ + g_printerr (_("Usage: nmcli networking off { help }\n" + "\n" + "Switch networking off.\n\n")); +} + +static void +usage_networking_connectivity (void) +{ + g_printerr (_("Usage: nmcli networking connectivity { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [check]\n" + "\n" + "Get network connectivity state.\n" + "The optional 'check' argument makes NetworkManager re-check the connectivity.\n\n")); + +} + +static void +usage_radio (void) +{ + g_printerr (_("Usage: nmcli radio { COMMAND | help }\n\n" +#if WITH_WIMAX + "COMMAND := { all | wifi | wwan | wimax }\n\n" + " all | wifi | wwan | wimax [ on | off ]\n\n" +#else + "COMMAND := { all | wifi | wwan }\n\n" + " all | wifi | wwan [ on | off ]\n\n" +#endif + )); +} + +static void +usage_radio_all (void) +{ + g_printerr (_("Usage: nmcli radio all { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [on | off]\n" + "\n" + "Get status of all radio switches, or turn them on/off.\n\n")); +} + +static void +usage_radio_wifi (void) +{ + g_printerr (_("Usage: nmcli radio wifi { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [on | off]\n" + "\n" + "Get status of Wi-Fi radio switch, or turn it on/off.\n\n")); +} + +static void +usage_radio_wwan (void) +{ + g_printerr (_("Usage: nmcli radio wwan { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [on | off]\n" + "\n" + "Get status of mobile broadband radio switch, or turn it on/off.\n\n")); +} + +#if WITH_WIMAX +static void +usage_radio_wimax (void) +{ + g_printerr (_("Usage: nmcli radio wimax { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [on | off]\n" + "\n" + "Get status of WiMAX radio switch, or turn it on/off.\n\n")); +} +#endif + +/* quit main loop */ +static void +quit (void) +{ + g_main_loop_quit (loop); /* quit main loop */ +} + +static const char * +nm_state_to_string (NMState state) +{ + switch (state) { + case NM_STATE_ASLEEP: + return _("asleep"); + case NM_STATE_CONNECTING: + return _("connecting"); + case NM_STATE_CONNECTED_LOCAL: + return _("connected (local only)"); + case NM_STATE_CONNECTED_SITE: + return _("connected (site only)"); + case NM_STATE_CONNECTED_GLOBAL: + return _("connected"); + case NM_STATE_DISCONNECTING: + return _("disconnecting"); + case NM_STATE_DISCONNECTED: + return _("disconnected"); + case NM_STATE_UNKNOWN: + default: + return _("unknown"); + } +} + +static const char * +nm_connectivity_to_string (NMConnectivityState connectivity) +{ + switch (connectivity) { + case NM_CONNECTIVITY_NONE: + return _("none"); + case NM_CONNECTIVITY_PORTAL: + return _("portal"); + case NM_CONNECTIVITY_LIMITED: + return _("limited"); + case NM_CONNECTIVITY_FULL: + return _("full"); + case NM_CONNECTIVITY_UNKNOWN: + default: + return _("unknown"); + } +} + +static gboolean +show_nm_status (NmCli *nmc, const char *pretty_header_name, const char *print_flds) +{ + gboolean startup = FALSE; + NMState state = NM_STATE_UNKNOWN; + NMConnectivityState connectivity = NM_CONNECTIVITY_UNKNOWN; + const char *net_enabled_str; + const char *wireless_hw_enabled_str, *wireless_enabled_str; + const char *wwan_hw_enabled_str, *wwan_enabled_str; +#if WITH_WIMAX + const char *wimax_hw_enabled_str, *wimax_enabled_str; +#endif + GError *error = NULL; + const char *fields_str; + const char *fields_all = print_flds ? print_flds : NMC_FIELDS_NM_STATUS_ALL; + const char *fields_common = print_flds ? print_flds : NMC_FIELDS_NM_STATUS_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + tmpl = nmc_fields_nm_status; + tmpl_len = sizeof (nmc_fields_nm_status); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); + + if (error) { + g_string_printf (nmc->return_text, _("Error: only these fields are allowed: %s"), fields_all); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + + nmc->get_client (nmc); /* create NMClient */ + + if (!nm_client_get_nm_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return FALSE; + } + + if (!nmc_versions_match (nmc)) + return FALSE; + + state = nm_client_get_state (nmc->client); + startup = nm_client_get_startup (nmc->client); + connectivity = nm_client_get_connectivity (nmc->client); + net_enabled_str = nm_client_networking_get_enabled (nmc->client) ? _("enabled") : _("disabled"); + wireless_hw_enabled_str = nm_client_wireless_hardware_get_enabled (nmc->client) ? _("enabled") : _("disabled"); + wireless_enabled_str = nm_client_wireless_get_enabled (nmc->client) ? _("enabled") : _("disabled"); + wwan_hw_enabled_str = nm_client_wwan_hardware_get_enabled (nmc->client) ? _("enabled") : _("disabled"); + wwan_enabled_str = nm_client_wwan_get_enabled (nmc->client) ? _("enabled") : _("disabled"); +#if WITH_WIMAX + wimax_hw_enabled_str = nm_client_wimax_hardware_get_enabled (nmc->client) ? _("enabled") : _("disabled"); + wimax_enabled_str = nm_client_wimax_get_enabled (nmc->client) ? _("enabled") : _("disabled"); +#endif + + nmc->print_fields.header_name = pretty_header_name ? (char *) pretty_header_name : _("NetworkManager status"); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, 0); + set_val_strc (arr, 0, _("running")); + set_val_strc (arr, 1, nm_client_get_version (nmc->client)); + set_val_strc (arr, 2, nm_state_to_string (state)); + set_val_strc (arr, 3, startup ? _("starting") : _("started")); + set_val_strc (arr, 4, nm_connectivity_to_string (connectivity)); + set_val_strc (arr, 5, net_enabled_str); + set_val_strc (arr, 6, wireless_hw_enabled_str); + set_val_strc (arr, 7, wireless_enabled_str); + set_val_strc (arr, 8, wwan_hw_enabled_str); + set_val_strc (arr, 9, wwan_enabled_str); +#if WITH_WIMAX + set_val_strc (arr, 10, wimax_hw_enabled_str); + set_val_strc (arr, 11, wimax_enabled_str); +#endif + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI "org.freedesktop.NetworkManager.enable-disable-wifi" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN "org.freedesktop.NetworkManager.enable-disable-wwan" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX "org.freedesktop.NetworkManager.enable-disable-wimax" +#define NM_AUTH_PERMISSION_SLEEP_WAKE "org.freedesktop.NetworkManager.sleep-wake" +#define NM_AUTH_PERMISSION_NETWORK_CONTROL "org.freedesktop.NetworkManager.network-control" +#define NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED "org.freedesktop.NetworkManager.wifi.share.protected" +#define NM_AUTH_PERMISSION_WIFI_SHARE_OPEN "org.freedesktop.NetworkManager.wifi.share.open" +#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM "org.freedesktop.NetworkManager.settings.modify.system" +#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN "org.freedesktop.NetworkManager.settings.modify.own" +#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME "org.freedesktop.NetworkManager.settings.modify.hostname" + +static const char * +permission_to_string (NMClientPermission perm) +{ + switch (perm) { + case NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK: + return NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK; + case NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI: + return NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI; + case NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN: + return NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN; + case NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX: + return NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX; + case NM_CLIENT_PERMISSION_SLEEP_WAKE: + return NM_AUTH_PERMISSION_SLEEP_WAKE; + case NM_CLIENT_PERMISSION_NETWORK_CONTROL: + return NM_AUTH_PERMISSION_NETWORK_CONTROL; + case NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED: + return NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED; + case NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN: + return NM_AUTH_PERMISSION_WIFI_SHARE_OPEN; + case NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM: + return NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM; + case NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN: + return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN; + case NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME: + return NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME; + default: + return _("unknown"); + } +} + +static const char * +permission_result_to_string (NMClientPermissionResult perm_result) +{ + + switch (perm_result) { + case NM_CLIENT_PERMISSION_RESULT_YES: + return _("yes"); + case NM_CLIENT_PERMISSION_RESULT_NO: + return _("no"); + case NM_CLIENT_PERMISSION_RESULT_AUTH: + return _("auth"); + default: + return _("unknown"); + } +} + +static gboolean +show_nm_permissions (NmCli *nmc) +{ + NMClientPermission perm; + GError *error = NULL; + const char *fields_str; + const char *fields_all = NMC_FIELDS_NM_PERMISSIONS_ALL; + const char *fields_common = NMC_FIELDS_NM_PERMISSIONS_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + tmpl = nmc_fields_nm_permissions; + tmpl_len = sizeof (nmc_fields_nm_permissions); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); + + if (error) { + g_string_printf (nmc->return_text, _("Error: 'general permissions': %s"), error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + + nmc->get_client (nmc); /* create NMClient */ + + if (!nm_client_get_nm_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return FALSE; + } + + nmc->print_fields.header_name = _("NetworkManager permissions"); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + for (perm = NM_CLIENT_PERMISSION_NONE + 1; perm <= NM_CLIENT_PERMISSION_LAST; perm++) { + NMClientPermissionResult perm_result = nm_client_get_permission_result (nmc->client, perm); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, 0); + set_val_strc (arr, 0, permission_to_string (perm)); + set_val_strc (arr, 1, permission_result_to_string (perm_result)); + g_ptr_array_add (nmc->output_data, arr); + } + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +show_general_logging (NmCli *nmc) +{ + char *level = NULL; + char *domains = NULL; + GError *error = NULL; + const char *fields_str; + const char *fields_all = NMC_FIELDS_NM_LOGGING_ALL; + const char *fields_common = NMC_FIELDS_NM_LOGGING_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + tmpl = nmc_fields_nm_logging; + tmpl_len = sizeof (nmc_fields_nm_logging); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); + + if (error) { + g_string_printf (nmc->return_text, _("Error: 'general logging': %s"), error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + + nmc->get_client (nmc); /* create NMClient */ + nm_client_get_logging (nmc->client, &level, &domains, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_error_free (error); + return FALSE; + } + + nmc->print_fields.header_name = _("NetworkManager logging"); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, 0); + set_val_str (arr, 0, level); + set_val_str (arr, 1, domains); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static void +save_hostname_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + GError *error = NULL; + + nm_client_save_hostname_finish (NM_CLIENT (object), result, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: failed to set hostname: %s"), + error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_error_free (error); + } + quit (); +} + +/* + * Entry point function for general operations 'nmcli general' + */ +NMCResultCode +do_general (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + + if (argc == 0) { + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + show_nm_status (nmc, NULL, NULL); + } + + if (argc > 0) { + if (nmc_arg_is_help (*argv)) { + usage_general (); + } + else if (matches (*argv, "status") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_general_status (); + goto finish; + } + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + show_nm_status (nmc, NULL, NULL); + } + else if (matches (*argv, "hostname") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_general_hostname (); + goto finish; + } + + if (next_arg (&argc, &argv) != 0) { + /* no arguments -> get hostname */ + char *hostname = NULL; + + nmc->get_client (nmc); /* create NMClient */ + g_object_get (nmc->client, NM_CLIENT_HOSTNAME, &hostname, NULL); + if (hostname) + g_print ("%s\n", hostname); + g_free (hostname); + } else { + /* hostname provided -> set it */ + const char *hostname = *argv; + + if (next_arg (&argc, &argv) == 0) + g_print ("Warning: ignoring extra garbage after '%s' hostname\n", hostname); + + nmc->should_wait = TRUE; + nmc->get_client (nmc); /* create NMClient */ + nm_client_save_hostname_async (nmc->client, hostname, NULL, save_hostname_cb, nmc); + } + } + else if (matches (*argv, "permissions") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_general_permissions (); + goto finish; + } + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + show_nm_permissions (nmc); + } + else if (matches (*argv, "logging") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_general_logging (); + goto finish; + } + if (next_arg (&argc, &argv) != 0) { + /* no arguments -> get logging level and domains */ + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + show_general_logging (nmc); + } else { + /* arguments provided -> set logging level and domains */ + const char *level = NULL; + const char *domains = NULL; + nmc_arg_t exp_args[] = { {"level", TRUE, &level, TRUE}, + {"domains", TRUE, &domains, TRUE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { + g_string_assign (nmc->return_text, error->message); + nmc->return_value = error->code; + goto finish; + } + + nmc->get_client (nmc); /* create NMClient */ + nm_client_set_logging (nmc->client, level, domains, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: failed to set logging: %s"), + error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto finish; + } + } + } + else { + usage_general (); + g_string_printf (nmc->return_text, _("Error: 'general' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + +finish: + if (error) + g_error_free (error); + return nmc->return_value; +} + +static gboolean +nmc_switch_show (NmCli *nmc, const char *switch_name, const char *header) +{ + g_return_val_if_fail (nmc != NULL, FALSE); + g_return_val_if_fail (switch_name != NULL, FALSE); + + if (nmc->required_fields && strcasecmp (nmc->required_fields, switch_name) != 0) { + g_string_printf (nmc->return_text, _("Error: '--fields' value '%s' is not valid here (allowed field: %s)"), + nmc->required_fields, switch_name); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + if (nmc->print_output == NMC_PRINT_NORMAL) + nmc->print_output = NMC_PRINT_TERSE; + + if (!nmc->required_fields) + nmc->required_fields = g_strdup (switch_name); + return show_nm_status (nmc, header, NULL); +} + +static gboolean +nmc_switch_parse_on_off (NmCli *nmc, const char *arg1, const char *arg2, gboolean *res) +{ + g_return_val_if_fail (nmc != NULL, FALSE); + g_return_val_if_fail (arg1 && arg2, FALSE); + g_return_val_if_fail (res != NULL, FALSE); + + if (!strcmp (arg2, "on")) + *res = TRUE; + else if (!strcmp (arg2, "off")) + *res = FALSE; + else { + g_string_printf (nmc->return_text, _("Error: invalid '%s' argument: '%s' (use on/off)."), arg1, arg2); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + + return TRUE; +} + +static gboolean +show_networking_connectivity (NmCli *nmc) +{ + return nmc_switch_show (nmc, NMC_FIELDS_NM_CONNECTIVITY, _("Connectivity")); +} + +/* + * Entry point function for 'nmcli networking' + */ +NMCResultCode +do_networking (NmCli *nmc, int argc, char **argv) +{ + gboolean enable_flag; + + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + + if (argc == 0) + nmc_switch_show (nmc, NMC_FIELDS_NM_NETWORKING, _("Networking")); + else if (argc > 0) { + if (nmc_arg_is_help (*argv)) { + usage_networking (); + } else if (matches (*argv, "connectivity") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_networking_connectivity (); + goto finish; + } + if (next_arg (&argc, &argv) != 0) { + /* no arguments -> get current state */ + show_networking_connectivity (nmc); + } else if (matches (*argv, "check") == 0) { + GError *error = NULL; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_check_connectivity (nmc->client, NULL, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_clear_error (&error); + } else + show_networking_connectivity (nmc); + } else { + usage_networking (); + g_string_printf (nmc->return_text, _("Error: 'networking connectivity' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } else if (nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) { + if (nmc_arg_is_help (*(argv+1))) { + if (enable_flag) + usage_networking_on (); + else + usage_networking_off (); + goto finish; + } + + nmc->get_client (nmc); /* create NMClient */ + nm_client_networking_set_enabled (nmc->client, enable_flag, NULL); + } else { + usage_networking (); + g_string_printf (nmc->return_text, _("Error: 'networking' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + +finish: + quit (); + return nmc->return_value; +} + +/* + * Entry point function for radio switch commands 'nmcli radio' + */ +NMCResultCode +do_radio (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + gboolean enable_flag; + + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + + if (argc == 0) { + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_error_free (error); + goto finish; + } + show_nm_status (nmc, _("Radio switches"), NMC_FIELDS_NM_STATUS_RADIO); + } + + if (argc > 0) { + if (nmc_arg_is_help (*argv)) { + usage_radio (); + } + else if (matches (*argv, "all") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_radio_all (); + goto finish; + } + if (next_arg (&argc, &argv) != 0) { + /* no argument, show all radio switches */ + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_error_free (error); + goto finish; + } + show_nm_status (nmc, _("Radio switches"), NMC_FIELDS_NM_STATUS_RADIO); + } else { + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + goto finish; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wireless_set_enabled (nmc->client, enable_flag); + nm_client_wimax_set_enabled (nmc->client, enable_flag); + nm_client_wwan_set_enabled (nmc->client, enable_flag); + } + } + else if (matches (*argv, "wifi") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_radio_wifi (); + goto finish; + } + if (next_arg (&argc, &argv) != 0) { + /* no argument, show current WiFi state */ + nmc_switch_show (nmc, NMC_FIELDS_NM_WIFI, _("Wi-Fi radio switch")); + } else { + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + goto finish; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wireless_set_enabled (nmc->client, enable_flag); + } + } + else if (matches (*argv, "wwan") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_radio_wwan (); + goto finish; + } + if (next_arg (&argc, &argv) != 0) { + /* no argument, show current WWAN (mobile broadband) state */ + nmc_switch_show (nmc, NMC_FIELDS_NM_WWAN, _("WWAN radio switch")); + } else { + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + goto finish; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wwan_set_enabled (nmc->client, enable_flag); + } + } +#if WITH_WIMAX + else if (matches (*argv, "wimax") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_radio_wimax (); + goto finish; + } + if (next_arg (&argc, &argv) != 0) { + /* no argument, show current WiMAX state */ + nmc_switch_show (nmc, NMC_FIELDS_NM_WIMAX, _("WiMAX radio switch")); + } else { + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + goto finish; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wimax_set_enabled (nmc->client, enable_flag); + } + } +#endif + else { + usage_radio (); + g_string_printf (nmc->return_text, _("Error: 'radio' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + +finish: + quit (); + return nmc->return_value; +} + diff --git a/clients/cli/general.h b/clients/cli/general.h new file mode 100644 index 00000000..7e8c21f9 --- /dev/null +++ b/clients/cli/general.h @@ -0,0 +1,29 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + */ + +#ifndef NMC_GENERAL_H +#define NMC_GENERAL_H + +#include "nmcli.h" + +NMCResultCode do_general (NmCli *nmc, int argc, char **argv); +NMCResultCode do_networking (NmCli *nmc, int argc, char **argv); +NMCResultCode do_radio (NmCli *nmc, int argc, char **argv); + +#endif /* NMC_GENERAL_H */ diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion new file mode 100644 index 00000000..73028bdf --- /dev/null +++ b/clients/cli/nmcli-completion @@ -0,0 +1,1287 @@ +# nmcli(1) completion -*- shell-script -*- +# Originally based on +# https://github.com/GArik/bash-completion/blob/master/completions/nmcli + +_nmcli_list() +{ + COMPREPLY=( $( compgen -W '$1' -- $cur ) ) +} + +_nmcli_list_nl() +{ + local IFS=$'\n' + COMPREPLY=( $( compgen -W '$1' -- $cur ) ) + + # Now escape special characters (spaces, single and double quotes), + # so that the argument is really regarded a single argument by bash. + # See http://stackoverflow.com/questions/1146098/properly-handling-spaces-and-quotes-in-bash-completion + local escaped_single_quote="'\''" + local i=0 + local entry + for entry in ${COMPREPLY[*]} + do + if [[ "${cur:0:1}" == "'" ]]; then + # started with single quote, escaping only other single quotes + # [']bla'bla"bla\bla bla --> [']bla'\''bla"bla\bla bla + COMPREPLY[$i]="${entry//\'/${escaped_single_quote}}" + elif [[ "${cur:0:1}" == '"' ]]; then + # started with double quote, escaping all double quotes and all backslashes + # ["]bla'bla"bla\bla bla --> ["]bla'bla\"bla\\bla bla + entry="${entry//\\/\\\\}" + entry="${entry//\"/\\\"}" + COMPREPLY[$i]="$entry" + else + # no quotes in front, escaping _everything_ + # [ ]bla'bla"bla\bla bla --> [ ]bla\'bla\"bla\\bla\ bla + entry="${entry//\\/\\\\}" + entry="${entry//\'/\'}" + entry="${entry//\"/\\\"}" + entry="${entry// /\\ }" + COMPREPLY[$i]="$entry" + fi + (( i++ )) + done +} + +_nmcli_con_show() +{ + nmcli -t -f "$1" connection show $2 2> /dev/null +} + +_nmcli_wifi_list() +{ + nmcli -t -f "$1" device wifi list 2>/dev/null +} + +_nmcli_dev_status() +{ + nmcli -t -f "$1" device status 2>/dev/null +} + +_nmcli_array_has_value() { + # expects the name of an array as first parameter and + # returns true if if one of the remaining arguments is + # contained in the array ${$1[@]} + eval "local ARRAY=(\"\${$1[@]}\")" + local arg a + shift + for arg; do + for a in "${ARRAY[@]}"; do + if [[ "$a" = "$arg" ]]; then + return 0 + fi + done + done + return 1 +} + +_nmcli_array_delete_at() +{ + eval "local ARRAY=(\"\${$1[@]}\")" + local i + local tmp=() + local lower=$2 + local upper=${3:-$lower} + + # for some reason the following fails. So this clumsy workaround... + # A=(a "") + # echo " >> ${#A[@]}" + # >> 2 + # A=("${A[@]:1}") + # echo " >> ${#A[@]}" + # >> 0 + # ... seriously??? + + for i in "${!ARRAY[@]}"; do + if [[ "$i" -lt "$2" || "$i" -gt "${3-$2}" ]]; then + tmp=("${tmp[@]}" "${ARRAY[$i]}") + fi + done + eval "$1=(\"\${tmp[@]}\")" +} + +_nmcli_compl_match_option() +{ + local S="$1" + local V + shift + if [[ "${S:0:2}" == "--" ]]; then + S="${S:2}" + elif [[ "${S:0:1}" == "-" ]]; then + S="${S:1}" + fi + for V; do + case "$V" in + "$S"*) + printf "%s" "$V" + return 0 + ;; + esac + done + return 1 +} + +# OPTIONS appear first at the command line (before the OBJECT). +# This iterates over the argument list and tries to complete +# the options. If there are options that are to be completed, +# zero is returned and completion will be performed. +# Otherwise it will remove all the option parameters from the ${words[@]} +# array and return with zero (so that completion of OBJECT can continue). +_nmcli_compl_OPTIONS() +{ + local i W + + for (( ; ; )); do + if [[ "${#words[@]}" -le 1 ]]; then + return 1 + fi + W="$(_nmcli_compl_match_option "${words[0]}" "${LONG_OPTIONS[@]}")" + if [[ $? != 0 ]]; then + return 2 + fi + + # remove the options already seen. + for i in ${!LONG_OPTIONS[@]}; do + if [[ "${LONG_OPTIONS[$i]}" == "$W" ]]; then + _nmcli_array_delete_at LONG_OPTIONS $i + break + fi + done + + if [[ "$HELP_ONLY_AS_FIRST" == '1' ]]; then + for i in ${!LONG_OPTIONS[@]}; do + if [[ "${LONG_OPTIONS[$i]}" == "help" ]]; then + _nmcli_array_delete_at LONG_OPTIONS $i + break + fi + done + fi + + case "$W" in + terse) + _nmcli_array_delete_at words 0 + ;; + pretty) + _nmcli_array_delete_at words 0 + ;; + nocheck) + _nmcli_array_delete_at words 0 + ;; + ask) + _nmcli_array_delete_at words 0 + ;; + show-secrets) + _nmcli_array_delete_at words 0 + ;; + active) + _nmcli_array_delete_at words 0 + ;; + version) + _nmcli_array_delete_at words 0 + ;; + help) + _nmcli_array_delete_at words 0 + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + HELP_ONLY_AS_FIRST=0 + return 0 + fi + HELP_ONLY_AS_FIRST=0 + ;; + temporary) + _nmcli_array_delete_at words 0 + ;; + mode) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "tabular multiline" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + fields) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "all common + NAME UUID TYPE TIMESTAMP TIMESTAMP-REAL AUTOCONNECT READONLY DBUS-PATH ACTIVE DEVICE STATE ACTIVE-PATH + connection 802-3-ethernet 802-1x 802-11-wireless 802-11-wireless-security ipv4 ipv6 serial ppp pppoe gsm cdma bluetooth 802-11-olpc-mesh vpn wimax infiniband bond vlan adsl bridge bridge-port team team-port dcb + GENERAL IP4 DHCP4 IP6 DHCP6 VPN + profile active" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + escape) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "no yes" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + wait) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + *) + # something unexpected. We are finished with parsing the OPTIONS. + return 2 + ;; + esac + done +} + +# after the OPTIONS, the OBJECT, the COMMAND and possible the COMMAND_CONNECTION, the syntax for nmcli +# expects several options with parameters. This function can parse them and remove them from the words array. +_nmcli_compl_ARGS() +{ + local OPTIONS_ALL N_REMOVE_WORDS REMOVE_OPTIONS OPTIONS_HAS_MANDATORY i + OPTIONS_ALL=("${OPTIONS[@]}") + OPTIONS_UNKNOWN_OPTION= + + OPTIONS_HAS_MANDATORY=0 + if [[ "${#OPTIONS_MANDATORY[@]}" -ge 1 ]]; then + OPTIONS_HAS_MANDATORY=1 + fi + + for (( ; ; )); do + if [[ "${#words[@]}" -le 1 ]]; then + # we have no more words left... + if [[ ${#OPTIONS[@]} -eq 0 ]]; then + return 1; + fi + if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then + _nmcli_list "$(echo "${OPTIONS[@]}")" + return 0 + fi + COMMAND_ARGS_WAIT_OPTIONS=0 + return 1 + fi + if ! _nmcli_array_has_value OPTIONS_ALL "${words[0]}"; then + # This is an entirely unknown option. + OPTIONS_UNKNOWN_OPTION="?${words[0]}" + return 1 + fi + if [[ "$OPTIONS_HAS_MANDATORY" -eq 1 && "${#OPTIONS_MANDATORY[@]}" -eq 0 ]]; then + # we had some mandatory options, but they are all satisfied... stop here... + # This means, that we can continue with more additional options from the NEXT_GROUP. + return 1 + fi + + N_REMOVE_WORDS=2 + REMOVE_OPTIONS=("${words[0]}") + case "${words[0]}" in + level) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "ERR WARN INFO DEBUG TRACE" + return 0 + fi + ;; + domains) + if [[ "${#words[@]}" -eq 2 ]]; then + local OPTIONS_DOM=(ALL DEFAULT PLATFORM RFKILL ETHER WIFI BT MB DHCP4 DHCP6 PPP WIFI_SCAN IP4 IP6 AUTOIP4 DNS VPN SHARING SUPPLICANT AGENTS SETTINGS SUSPEND CORE DEVICE OLPC WIMAX INFINIBAND FIREWALL ADSL BOND VLAN BRIDGE DBUS_PROPS TEAM CONCHECK DCB DISPATCH) + if [[ "${words[1]}" != "" ]]; then + + # split the comma separaeted domain string into its parts LOGD + local oIFS="$IFS" + IFS="," + local LOGD=($(printf '%s' "${words[1]}" | sed 's/\(^\|,\)/,#/g')) + IFS="$oIFS" + unset oIFS + + local LOGDLAST LOGDLAST_IS_OPTION LOGDI i + # first we iterate over all present domains and remove them from OPTIONS_DOM + for LOGDI in ${LOGD[@]}; do + LOGDI="${LOGDI:1}" + LOGDLAST="$LOGDI" + LOGDLAST_IS_OPTION=0 + for i in ${!OPTIONS_DOM[*]}; do + if [[ "${OPTIONS_DOM[$i]}" = "$LOGDI" ]]; then + LOGDLAST_IS_OPTION=1 + unset OPTIONS_DOM[$i] + fi + done + done + + local OPTIONS_DOM2=() + if [[ "$LOGDLAST" = "" ]]; then + # we have a word that ends with ','. Just append all remaining options. + for i in ${!OPTIONS_DOM[*]}; do + OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]}${OPTIONS_DOM[$i]}" + done + else + # if the last option is not "" we keep only those option with the same prefix + # as the last domain (LOGDLAST) + for i in ${!OPTIONS_DOM[*]}; do + if [[ "${OPTIONS_DOM[$i]:0:${#LOGDLAST}}" == "$LOGDLAST" ]]; then + # modify the option with the present prefix + OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]}${OPTIONS_DOM[$i]:${#LOGDLAST}}" + fi + done + + if [[ $LOGDLAST_IS_OPTION -eq 1 ]]; then + # if the last logd itself was a valid iption, ${words[1]} is itself a valid match + OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]}" + + # also, add all remaining options by comma separated to the word. + for i in ${!OPTIONS_DOM[*]}; do + OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]},${OPTIONS_DOM[$i]}" + done + fi + if [[ ${#OPTIONS_DOM2[@]} -eq 1 ]]; then + for i in ${!OPTIONS_DOM[*]}; do + if [[ "$LOGDLAST" != "${OPTIONS_DOM[$i]:0:${#LOGDLAST}}" ]]; then + OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${OPTIONS_DOM2[0]},${OPTIONS_DOM[$i]}" + fi + done + fi + + fi + OPTIONS_DOM=(${OPTIONS_DOM2[@]}) + fi + + _nmcli_list "$(echo "${OPTIONS_DOM[@]}")" + return 0 + fi + ;; + type) + if [[ "$OPTIONS_TYPE" != "" ]]; then + return 1 + fi + if [[ "${#words[@]}" -eq 2 ]]; then + if [[ "${words[1]:0:1}" = "8" ]]; then + # usually we don't want to show the 802-x types (because the shorter aliases are more + # user friendly. Only complete them, if the current word already starts with an "8". + _nmcli_list "802-3-ethernet 802-11-wireless 802-11-olpc-mesh" + else + _nmcli_list "ethernet wifi wimax gsm cdma infiniband bluetooth vpn olpc-mesh vlan bond bond-slave bridge bridge-slave team team-slave pppoe" + fi + return 0 + fi + OPTIONS_TYPE="${words[1]}" + + if [[ "x$OPTIONS_MANDATORY_IFNAME" != x ]]; then + # the ifname is not a mandatory option for a few connection types... + # Check, if we have such a 'type' and remove the 'ifname' from the list + # of mandatory options. + case "$OPTIONS_TYPE" in + vl|vla|vlan| \ + bond| \ + team| \ + bridge) + for i in ${!OPTIONS_MANDATORY[*]}; do + if [[ "${OPTIONS_MANDATORY[$i]}" = "ifname" ]]; then + unset OPTIONS_MANDATORY[$i] + fi + done + ;; + *) + ;; + + esac + OPTIONS_MANDATORY_IFNAME= + fi + ;; + master) + if [[ "${#words[@]}" -eq 2 ]]; then + if [[ "${words[1]}" = "" ]]; then + _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" + else + _nmcli_list_nl "$(printf "%s\n%s\n%s" "$(_nmcli_dev_status DEVICE)" "$(_nmcli_con_show UUID)")" + fi + return 0 + fi + ;; + dev) + if [[ "${#words[@]}" -eq 2 ]]; then + if [[ "${words[1]}" = "" ]]; then + _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" + else + _nmcli_list_nl "$(printf "%s\n%s\n%s" "$(_nmcli_dev_status DEVICE)" "$(_nmcli_wifi_list BSSID)" "$(_nmcli_con_show UUID)")" + fi + return 0 + fi + ;; + primary| \ + ifname) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" + return 0 + fi + ;; + mode) + if [[ "${#words[@]}" -eq 2 ]]; then + case "$OPTIONS_TYPE" in + "wifi") + _nmcli_list "infrastructure ap adhoc" + ;; + "bond"| \ + *) + _nmcli_list "balance-rr active-backup balance-xor broadcast 802.3ad balance-tlb balance-alb" + esac + return 0 + fi + ;; + transport-mode) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "datagram connected" + return 0 + fi + ;; + vpn-type) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "vpnc openvpn pptp openconnect openswan libreswan ssh l2tp iodine" + return 0 + fi + ;; + lacp-rate) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "slow fast" + return 0 + fi + ;; + bt-type) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "panu dun-gsm dun-cdma" + return 0 + fi + ;; + wep-key-type) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "key phrase" + return 0 + fi + ;; + autoconnect| \ + stp| \ + hairpin| \ + save| \ + private) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "yes no" + return 0 + fi + ;; + config) + if [[ "${#words[@]}" -eq 2 ]]; then + compopt -o default + COMPREPLY=() + return 0 + fi + ;; + ip4| \ + ip6| \ + gw4| \ + gw6| \ + priority| \ + forward-delay| \ + hello-time| \ + max-age| \ + ageing-time| \ + nsp| \ + path-cost| \ + name| \ + mtu| \ + cloned-mac| \ + addr| \ + parent| \ + miimon| \ + arp-interval| \ + arp-ip-target| \ + downdelay| \ + updelay| \ + p-key| \ + mac| \ + id| \ + flags| \ + ingress| \ + dhcp-anycast| \ + channel| \ + egress| \ + apn| \ + con-name| \ + user| \ + username| \ + service| \ + password| \ + passwd-file) + if [[ "${#words[@]}" -eq 2 ]]; then + return 0 + fi + ;; + ssid) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list_nl "$(_nmcli_wifi_list SSID)" + return 0 + fi + ;; + ap| \ + bssid) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list_nl "$(_nmcli_wifi_list BSSID)" + return 0 + fi + ;; + *) + return 1 + ;; + esac + + + if [[ "${#OPTIONS_NEXT_GROUP[@]}" -gt 0 ]]; then + if _nmcli_array_has_value OPTIONS_NEXT_GROUP "${words[0]}"; then + # the current value is from the next group... + # We back off, because the current group is complete. + return 1 + fi + fi + + _nmcli_array_delete_at words 0 $((N_REMOVE_WORDS-1)) + # remove the options already seen. + for i in ${!OPTIONS[*]}; do + if [[ "${OPTIONS[$i]}" = "${REMOVE_OPTIONS[0]}" || "${OPTIONS[$i]}" = "${REMOVE_OPTIONS[1]}" ]]; then + unset OPTIONS[$i] + fi + done + for i in ${!OPTIONS_MANDATORY[*]}; do + if [[ "${OPTIONS_MANDATORY[$i]}" = "${REMOVE_OPTIONS[0]}" || "${OPTIONS_MANDATORY[$i]}" = "${REMOVE_OPTIONS[1]}" ]]; then + unset OPTIONS_MANDATORY[$i] + fi + done + done +} + +# some commands expect a connection as parameter. This connection can either be given +# as id|uuid|path|apath. Parse that connection parameter. +_nmcli_compl_ARGS_CONNECTION() +{ + if ! _nmcli_array_has_value OPTIONS "${words[0]}"; then + COMMAND_CONNECTION_TYPE= + COMMAND_CONNECTION_ID="${words[0]}" + _nmcli_array_delete_at words 0 + return 1 + fi + COMMAND_CONNECTION_TYPE="${words[0]}" + COMMAND_CONNECTION_ID="${words[1]}" + local CON_TYPE= + if [[ "x$COMMAND_CONNECTION_ACTIVE" != x ]]; then + CON_TYPE=--active + fi + case "${words[0]}" in + id) + if [[ ${#words[@]} -le 2 ]]; then + _nmcli_list_nl "$(_nmcli_con_show NAME $CON_TYPE)" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + uuid) + if [[ ${#words[@]} -le 2 ]]; then + _nmcli_list_nl "$(_nmcli_con_show UUID $CON_TYPE)" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + path) + if [[ ${#words[@]} -le 2 ]]; then + _nmcli_list_nl "$(_nmcli_con_show DBUS-PATH $CON_TYPE)" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + apath) + if [[ ${#words[@]} -le 2 ]]; then + _nmcli_list_nl "$(_nmcli_con_show ACTIVE-PATH --active)" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + ifname) + if [[ ${#words[@]} -le 2 ]]; then + _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + *) + COMMAND_CONNECTION_TYPE= + COMMAND_CONNECTION_ID="${words[0]}" + _nmcli_array_delete_at words 0 + ;; + esac + return 1 +} + +_nmcli_compl_COMMAND() { + local command="$1" + shift + local V=("$@") + local H= + if [[ "${command[0]:0:1}" != '-' ]]; then + H=help + elif [[ "${command[0]:1:1}" == '-' || "${command[0]}" == "-" ]]; then + H=--help + else + H=-help + fi + if [[ "x$COMPL_COMMAND_NO_HELP" == x ]]; then + V=("${V[@]}" "$H") + fi + _nmcli_list "${V[*]}" +} + +_nmcli_compl_COMMAND_nl() { + local command="$1" + local a="$2" + shift + shift + local V=("$@") + local H= + if [[ "${command[0]:0:1}" != '-' ]]; then + V=("${V[@]/#/--}") + H=help + elif [[ "${command[0]:1:1}" == '-' || "${command[0]}" == "-" ]]; then + V=("${V[@]/#/--}") + H=--help + else + V=("${V[@]/#/-}") + H=-help + fi + if [[ "x$COMPL_COMMAND_NO_HELP" == x ]]; then + V=("${V[@]}" "$H") + fi + local IFS=$'\n' + V="${V[*]}" + _nmcli_list_nl "$(printf "%s%s\n%s" "" "$V" "$a")" +} + +_nmcli() +{ + local cur prev words cword i + _init_completion || return + + # we don't care about any arguments after the current cursor position + # because we only parse from left to right. So, if there are some arguments + # right of the cursor, just ignore them. Also don't care about ${words[0]}. + _nmcli_array_delete_at words $((cword+1)) ${#words[@]} + _nmcli_array_delete_at words 0 + + # _init_completion returns the words with all the quotes and escaping + # characters. We don't care about them, drop them at first. + for i in ${!words[@]}; do + words[i]="$(printf '%s' "${words[i]}" | xargs printf '%s\n' 2>/dev/null || true)" + done + + # In case the cursor is not at the end of the line, + # $cur consists of spaces that we want do remove. + # For example: `nmcli connection modify id <TAB> lo` + if [[ "$cur" =~ ^[[:space:]]+ ]]; then + cur='' + fi + + local OPTIONS_UNKNOWN_OPTION OPTIONS_TYPE OPTIONS_TYPED OPTIONS OPTIONS_MANDATORY COMMAND_ARGS_WAIT_OPTIONS OPTIONS_IP OPTIONS_MANDATORY OPTIONS_NEXT_GROUP + local COMMAND_CONNECTION_TYPE COMMAND_CONNECTION_ID OPTIONS_MANDATORY_IFNAME HELP_ONLY_AS_FIRST + local COMMAND_CONNECTION_ACTIVE="" + + HELP_ONLY_AS_FIRST= + local LONG_OPTIONS=(terse pretty mode fields escape nocheck ask wait version help) + _nmcli_compl_OPTIONS + i=$? + + if [[ "$HELP_ONLY_AS_FIRST" == '0' ]]; then + # got a --help. No more completion. + return 0 + fi + + case $i in + 0) + return 0 + ;; + 1) + # we show for completion either the (remaining) OPTIONS + # (if the current word starts with a dash) or the OBJECT list + # otherwise. + if [[ "${words[0]:0:1}" != '-' ]]; then + OPTIONS=(help general networking radio connection device agent) + elif [[ "${words[0]:1:1}" == '-' || "${words[0]}" == "-" ]]; then + OPTIONS=("${LONG_OPTIONS[@]/#/--}") + else + OPTIONS=("${LONG_OPTIONS[@]/#/-}") + fi + _nmcli_list "${OPTIONS[*]}" + return 0 + ;; + esac + + local command="${words[1]}" + case "${words[0]}" in + h|he|hel|help) + ;; + g|ge|gen|gene|gener|genera|general) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" status permissions logging hostname + elif [[ ${#words[@]} -gt 2 ]]; then + case "$command" in + ho|hos|host|hostn|hostna|hostnam|hostname) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" \ + "$(printf '%s\n%s\n%s\n' \ + "$(nmcli general hostname 2>/dev/null)" \ + "$(cat /etc/hostname 2>/dev/null)" \ + "$(hostnamectl status 2>/dev/null | sed -n '1s/^.\+hostname: \(.\+\)$/\1/p')" \ + "$HOSTNAME")" + fi + ;; + l|lo|log|logg|loggi|loggin|logging) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" level domains + else + _nmcli_array_delete_at words 0 1 + OPTIONS=(level domains) + _nmcli_compl_ARGS + fi + ;; + s|st|sta|stat|statu|status| \ + p|pe|per|perm|permi|permis|permiss|permissi|permissio|permission|permissions) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" + fi + ;; + esac + fi + ;; + n|ne|net|netw|netwo|networ|network|networki|networkin|networking) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" on off connectivity + elif [[ ${#words[@]} -eq 3 ]]; then + case "$command" in + c|co|con|conn|conne|connec|connect|connecti|connectiv|connectivi|connectivit|connectivity) + _nmcli_compl_COMMAND "${words[2]}" "check" + ;; + esac + fi + ;; + r|ra|rad|radi|radio) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" all wifi wwan wimax + elif [[ ${#words[@]} -eq 3 ]]; then + case "$command" in + a|al|all | w|wi|wif|wifi | ww|wwa|wwan | wim|wima|wimax) + _nmcli_compl_COMMAND "${words[2]}" "on off" + ;; + esac + fi + ;; + c|co|con|conn|conne|connec|connect|connecti|connectio|connection) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" show up down add modify edit delete reload load + elif [[ ${#words[@]} -gt 2 ]]; then + case "$command" in + s|sh|sho|show) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME)")" active show-secrets + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help active show-secrets) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + i=$? + + if ! _nmcli_array_has_value LONG_OPTIONS active; then + COMMAND_CONNECTION_ACTIVE=1 + fi + + case $i in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + if [[ "x$COMMAND_CONNECTION_ACTIVE" = x ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" + + else + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" "${LONG_OPTIONS[@]}" + fi + fi + return 0 + ;; + esac + + OPTIONS=(id uuid path apath) + while [[ ${#words[@]} -gt 0 ]]; do + _nmcli_compl_ARGS_CONNECTION && return 0 + done + if [[ "x$COMMAND_CONNECTION_ACTIVE" = x ]]; then + _nmcli_list_nl "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME)")" + else + _nmcli_list_nl "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" + fi + fi + ;; + u|up) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "ifname\nid\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + + case $? in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "ifname\nid\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" + fi + return 0 + ;; + esac + + local COMMAND_CONNECTION_TYPE='' + OPTIONS=(ifname id uuid path) + _nmcli_compl_ARGS_CONNECTION && return 0 + + if [[ "$COMMAND_CONNECTION_TYPE" = "ifname" ]]; then + OPTIONS=(ap nsp passwd-file) + else + OPTIONS=(ifname ap nsp passwd-file) + fi + _nmcli_compl_ARGS + fi + ;; + d|do|dow|down) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + case $? in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" "${LONG_OPTIONS[@]}" + fi + return 0 + ;; + esac + + OPTIONS=(id uuid path apath) + COMMAND_CONNECTION_ACTIVE=1 + _nmcli_compl_ARGS_CONNECTION && return 0 + fi + ;; + a|ad|add) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" type ifname con-name autoconnect + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + case $? in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + _nmcli_compl_COMMAND "${words[2]}" type ifname con-name autoconnect + fi + return 0 + ;; + esac + + OPTIONS_TYPE= + OPTIONS=(type ifname con-name autoconnect save) + OPTIONS_MANDATORY=(type ifname) + COMMAND_ARGS_WAIT_OPTIONS=1 + OPTIONS_MANDATORY_IFNAME=1 + _nmcli_compl_ARGS && return 0 + + OPTIONS_MANDATORY_IFNAME= + if _nmcli_array_has_value OPTIONS "${OPTIONS_MANDATORY[@]}"; then + # we still have some missing mandatory options... + if [[ "$OPTIONS_UNKNOWN_OPTION" != '' ]]; then + if ! _nmcli_array_has_value OPTIONS "${OPTIONS_UNKNOWN_OPTION:1}"; then + # if we encountered an unknown option while having mandatory + # options, just return. + return 0 + fi + fi + _nmcli_list "$(echo "${OPTIONS[@]}")" + return 0 + fi + + OPTIONS_IP=(ip4 ip6 gw4 gw6) + OPTIONS_MANDATORY=() + case "$OPTIONS_TYPE" in + 802-3|802-3-|802-3-e|802-3-et|802-3-eth|802-3-ethe|802-3-ether|802-3-ethern|802-3-etherne|802-3-ethernet| \ + e|et|eth|ethe|ether|ethern|etherne|ethernet) + OPTIONS_TYPE=ethernet + OPTIONS_TYPED=(mac cloned-mac mtu) + ;; + 802-11-w|802-11-wi|802-11-wir|802-11-wire|802-11-wirel|802-11-wirele|802-11-wireles|802-11-wireless| \ + wif|wifi) + OPTIONS_TYPE=wifi + OPTIONS_TYPED=(ssid mac cloned-mac mtu mode) + OPTIONS_MANDATORY=(ssid) + ;; + wim|wima|wimax) + OPTIONS_TYPE=wimax + OPTIONS_TYPED=(mac nsp) + ;; + g|gs|gsm) + OPTIONS_TYPE=gsm + OPTIONS_TYPED=(apn user password) + OPTIONS_MANDATORY=(apn) + ;; + c|cd|cdm|cdma) + OPTIONS_TYPE=cdma + OPTIONS_TYPED=(user password) + ;; + i|in|inf|infi|infin|infini|infinib|infiniba|infiniban|infiniband) + OPTIONS_TYPE=infiniband + OPTIONS_TYPED=(mac mtu transport-mode parent p-key) + ;; + bl|blu|blue|bluet|blueto|bluetoo|bluetoot|bluetooth) + OPTIONS_TYPE=bluetooth + OPTIONS_TYPED=(addr bt-type) + ;; + vl|vla|vlan) + OPTIONS_TYPE=vlan + OPTIONS_TYPED=(dev id flags ingress egress mtu) + OPTIONS_MANDATORY=(dev) + ;; + bond) + OPTIONS_TYPE=bond + OPTIONS_TYPED=(mode miimon downdelay updelay arp-interval arp-ip-target primary lacp-rate) + ;; + bond-|bond-s|bond-sl|bond-sla|bond-slav|bond-slave) + OPTIONS_TYPE=bond-slave + OPTIONS_TYPED=(master) + OPTIONS_MANDATORY=(master) + OPTIONS_IP=() + ;; + team) + OPTIONS_TYPE=team + OPTIONS_TYPED=(config) + ;; + team-|team-s|team-sl|team-sla|team-slav|team-slave) + OPTIONS_TYPE=team-slave + OPTIONS_TYPED=(master config) + OPTIONS_MANDATORY=(master) + OPTIONS_IP=() + ;; + bridge) + OPTIONS_TYPE=bridge + OPTIONS_TYPED=(stp priority forward-delay hello-time max-age ageing-time mac) + ;; + bridge-|bridge-s|bridge-sl|bridge-sla|bridge-slav|bridge-slave) + OPTIONS_TYPE=bridge-slave + OPTIONS_TYPED=(master priority path-cost hairpin) + OPTIONS_MANDATORY=(master) + OPTIONS_IP=() + ;; + vp|vpn) + OPTIONS_TYPE=vpn + OPTIONS_TYPED=(vpn-type user) + OPTIONS_MANDATORY=(vpn-type) + ;; + 802-11-o|802-11-ol|802-11-olp|802-11-olpc|802-11-olpc-|802-11-olpc-m|802-11-olpc-me|802-11-olpc-mes|802-11-olpc-mesh| \ + o|ol|olp|olpc|olpc-|olpc-m|olpc-me|olpc-mes|olpc-mesh) + OPTIONS_TYPE=olpc-mesh + OPTIONS_TYPED=(ssid channel dhcp-anycast) + OPTIONS_MANDATORY=(ssid) + ;; + p|pp|ppp|pppo|pppoe) + OPTIONS_TYPE=pppoe + OPTIONS_TYPED=(username password service mtu mac) + OPTIONS_MANDATORY=(username) + ;; + *) + # for an unknown connection type, we stop completion here + return 0 + ;; + esac + if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then + # means, we are at the end of options. Nothing more to parse, just show + # what are the options now. + if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then + _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_TYPED[@]}")" + else + _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_TYPED[@]}") $(echo "${OPTIONS_IP[@]}")" + fi + return 0 + fi + if [[ "${#OPTIONS[@]}" -gt 0 ]]; then + # we still have some options from before, but no mandatory ones. Mix them with OPTIONS_TYPED + # and continue parsing the options... + OPTIONS=("${OPTIONS[@]}" "${OPTIONS_TYPED[@]}") + OPTIONS_NEXT_GROUP=("${OPTIONS_TYPED[@]}") + _nmcli_compl_ARGS && return 0 + + if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then + # means, we are at the end of options. Nothing more to parse, just show + # what are the options now. + if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then + _nmcli_list "$(echo "${OPTIONS[@]}")" + else + _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_IP[@]}")" + fi + return 0 + fi + + if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then + # there was an unknown option specified. Maybe we have to stop with the completion. + if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then + # we have an unknown option, but still mandatory ones that must be fullfiled first. + return 0 + fi + if ! _nmcli_array_has_value OPTIONS_IP "${OPTIONS_UNKNOWN_OPTION:1}"; then + # the unknown option is NOT an IP option. + return 0 + fi + # The unknown option is an IP option, which is fine... continue... + fi + + fi + OPTIONS=("${OPTIONS_TYPED[@]}") + OPTIONS_NEXT_GROUP=() + + if [[ "${#OPTIONS_MANDATORY[@]}" -ge 1 ]]; then + # we have some mandatory options... don't check for IP options yet... + _nmcli_compl_ARGS && return 0 + + if _nmcli_array_has_value OPTIONS "${OPTIONS_MANDATORY[@]}"; then + _nmcli_list "$(echo "${OPTIONS[@]}")" + return 0 + fi + + if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then + if ! _nmcli_array_has_value OPTIONS_IP "${OPTIONS_UNKNOWN_OPTION:1}"; then + # the unknown option is NOT an IP option. + return 0 + fi + fi + fi + + + # no mandatory options... do final completion including IP options + OPTIONS=("${OPTIONS[@]}" "${OPTIONS_IP[@]}") + OPTIONS_NEXT_GROUP=("${OPTIONS_IP[@]}") + _nmcli_compl_ARGS && return 0 + + if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then + return 0 + fi + + if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then + # means, we are at the end of options. Nothing more to parse, just show + # what are the options now. + _nmcli_list "$(echo "${OPTIONS[@]}")" + return 0 + fi + + # process the last group of options, as the OPTIONS_TYPED are already handled... + OPTIONS=("${OPTIONS_IP[@]}") + OPTIONS_NEXT_GROUP=() + COMMAND_ARGS_WAIT_OPTIONS=0 + _nmcli_compl_ARGS && return 0 + fi + ;; + e|ed|edi|edit) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\ntype\ncon-name\n%s" "$(_nmcli_con_show NAME)")" + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + + case $? in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\ntype\ncon-name\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" + fi + return 0 + ;; + esac + + if [[ "${words[0]}" = 'type' || "${words[0]}" = 'con-name' ]]; then + OPTIONS=(type con-name) + _nmcli_compl_ARGS + else + OPTIONS=(id uuid path apath) + _nmcli_compl_ARGS_CONNECTION + fi + fi + ;; + m|mo|mod|modi|modif|modify) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" temporary + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help temporary) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + case $? in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" + fi + return 0 + ;; + esac + + OPTIONS=(id uuid path apath) + _nmcli_compl_ARGS_CONNECTION && return 0 + while [[ "${#words[@]}" -gt 0 ]]; do + if [[ ${#words[@]} -le 1 ]]; then + local PREFIX="" + + if [[ "${words[0]:0:1}" == [+-] ]]; then + PREFIX="${words[0]:0:1}" + fi + _nmcli_list_nl "$(nmcli --fields profile connection show "${COMMAND_CONNECTION_TYPE:-id}" "$COMMAND_CONNECTION_ID" 2>/dev/null | sed -n 's/^\([^:]\+\):.*/'$PREFIX'\1/p')" + return 0 + elif [[ ${#words[@]} -le 2 ]]; then + return 0 + fi + _nmcli_array_delete_at words 0 1 + done + _nmcli_list_nl "$(nmcli --fields profile connection show "${COMMAND_CONNECTION_TYPE:-id}" "$COMMAND_CONNECTION_ID" 2>/dev/null | sed -n 's/^\([^:]\+\):.*/\1/p')" + return 0 + fi + ;; + de|del|dele|delet|delete) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help) + _nmcli_compl_OPTIONS + case $? in + 0) + return 0 + ;; + 1) + if ! _nmcli_array_has_value LONG_OPTIONS "help"; then + return 0 + fi + ;; + esac + + OPTIONS=(id uuid path apath) + while [[ ${#words[@]} -gt 0 ]]; do + _nmcli_compl_ARGS_CONNECTION && return 0 + done + _nmcli_list_nl "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" + fi + ;; + l|lo|loa|load) + if [[ ${#words[@]} -gt 2 ]]; then + # we should also complete for help/--help, but who to mix that + # with file name completion? + compopt -o default + COMPREPLY=() + fi + ;; + esac + fi + ;; + d|de|dev|devi|devic|device) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" status show connect disconnect delete wifi wimax + elif [[ ${#words[@]} -gt 2 ]]; then + case "$command" in + s|st|sta|stat|statu|status) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" + fi + ;; + sh|sho|show| \ + c|co|con|conn|conne|connec|connect| \ + d|di|dis|disc|disco|discon|disconn|disconne|disconnec|disconnect| \ + de|del|dele|delet|delete) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)" + fi + ;; + w|wi|wif|wifi) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" list connect rescan + else + case "${words[2]}" in + l|li|lis|list) + _nmcli_array_delete_at words 0 2 + OPTIONS=(ifname bssid) + _nmcli_compl_ARGS + ;; + c|co|con|conn|conne|connec|connect) + if [[ ${#words[@]} -eq 4 ]]; then + if [[ "${words[3]}" = "" ]]; then + _nmcli_list_nl "$(_nmcli_wifi_list SSID)" + else + _nmcli_list_nl "$(printf "%s\n%s" "$(_nmcli_wifi_list SSID)" "$(_nmcli_wifi_list BSSID)")" + fi + else + _nmcli_array_delete_at words 0 3 + local OPTIONS=(password wep-key-type ifname bssid name private) + _nmcli_compl_ARGS + fi + ;; + r|re|res|resc|resca|rescan) + _nmcli_array_delete_at words 0 2 + OPTIONS=(ifname) + _nmcli_compl_ARGS + ;; + esac + fi + ;; + wim|wima|wimax) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" list + else + case "${words[2]}" in + l|li|lis|list) + _nmcli_array_delete_at words 0 2 + OPTIONS=(ifname nsp) + _nmcli_compl_ARGS + ;; + esac + fi + ;; + + esac + fi + ;; + a|ag|age|agen|agent) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" secret polkit all + fi + ;; + esac + + return 0 +} && +complete -F _nmcli nmcli + +# ex: ts=4 sw=4 et filetype=sh diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c new file mode 100644 index 00000000..8d9f7fef --- /dev/null +++ b/clients/cli/nmcli.c @@ -0,0 +1,621 @@ +/* nmcli - command-line tool to control NetworkManager + * + * Jiri Klimes <jklimes@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + */ + +/* Generated configuration file */ +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <pthread.h> +#include <termios.h> +#include <unistd.h> +#include <locale.h> +#include <readline/readline.h> +#include <readline/history.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "polkit-agent.h" +#include "nmcli.h" +#include "utils.h" +#include "common.h" +#include "connections.h" +#include "devices.h" +#include "general.h" +#include "agent.h" + +#if defined(NM_DIST_VERSION) +# define NMCLI_VERSION NM_DIST_VERSION +#else +# define NMCLI_VERSION VERSION +#endif + +/* Global NmCli object */ +// FIXME: Currently, we pass NmCli over in most APIs, but we might refactor +// that and use the global variable directly instead. +NmCli nm_cli; + +typedef struct { + NmCli *nmc; + int argc; + char **argv; +} ArgsInfo; + +/* --- Global variables --- */ +GMainLoop *loop = NULL; +static sigset_t signal_set; +struct termios termios_orig; + + +/* Get an error quark for use with GError */ +GQuark +nmcli_error_quark (void) +{ + static GQuark error_quark = 0; + + if (G_UNLIKELY (error_quark == 0)) + error_quark = g_quark_from_static_string ("nmcli-error-quark"); + + return error_quark; +} + +static void +usage (const char *prog_name) +{ + g_printerr (_("Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n" + "\n" + "OPTIONS\n" + " -t[erse] terse output\n" + " -p[retty] pretty output\n" + " -m[ode] tabular|multiline output mode\n" + " -f[ields] <field1,field2,...>|all|common specify fields to output\n" + " -e[scape] yes|no escape columns separators in values\n" + " -n[ocheck] don't check nmcli and NetworkManager versions\n" + " -a[sk] ask for missing parameters\n" + " -w[ait] <seconds> set timeout waiting for finishing operations\n" + " -v[ersion] show program version\n" + " -h[elp] print this help\n" + "\n" + "OBJECT\n" + " g[eneral] NetworkManager's general status and operations\n" + " n[etworking] overall networking control\n" + " r[adio] NetworkManager radio switches\n" + " c[onnection] NetworkManager's connections\n" + " d[evice] devices managed by NetworkManager\n" + " a[gent] NetworkManager secret agent or polkit agent\n" + "\n"), + prog_name); +} + +static NMCResultCode +do_help (NmCli *nmc, int argc, char **argv) +{ + usage ("nmcli"); + return NMC_RESULT_SUCCESS; +} + +static const struct cmd { + const char *cmd; + NMCResultCode (*func) (NmCli *nmc, int argc, char **argv); +} nmcli_cmds[] = { + { "general", do_general }, + { "networking", do_networking }, + { "radio", do_radio }, + { "connection", do_connections }, + { "device", do_devices }, + { "agent", do_agent }, + { "help", do_help }, + { 0 } +}; + +static NMCResultCode +do_cmd (NmCli *nmc, const char *argv0, int argc, char **argv) +{ + const struct cmd *c; + + for (c = nmcli_cmds; c->cmd; ++c) { + if (matches (argv0, c->cmd) == 0) + return c->func (nmc, argc-1, argv+1); + } + + g_string_printf (nmc->return_text, _("Error: Object '%s' is unknown, try 'nmcli help'."), argv0); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; +} + +static NMCResultCode +parse_command_line (NmCli *nmc, int argc, char **argv) +{ + char *base; + + base = strrchr (argv[0], '/'); + if (base == NULL) + base = argv[0]; + else + base++; + + /* parse options */ + while (argc > 1) { + char *opt = argv[1]; + /* '--' ends options */ + if (strcmp (opt, "--") == 0) { + argc--; argv++; + break; + } + if (opt[0] != '-') + break; + if (opt[1] == '-') + opt++; + if (matches (opt, "-terse") == 0) { + if (nmc->print_output == NMC_PRINT_TERSE) { + g_string_printf (nmc->return_text, _("Error: Option '--terse' is specified the second time.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + else if (nmc->print_output == NMC_PRINT_PRETTY) { + g_string_printf (nmc->return_text, _("Error: Option '--terse' is mutually exclusive with '--pretty'.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + else + nmc->print_output = NMC_PRINT_TERSE; + } else if (matches (opt, "-pretty") == 0) { + if (nmc->print_output == NMC_PRINT_PRETTY) { + g_string_printf (nmc->return_text, _("Error: Option '--pretty' is specified the second time.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + else if (nmc->print_output == NMC_PRINT_TERSE) { + g_string_printf (nmc->return_text, _("Error: Option '--pretty' is mutually exclusive with '--terse'.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + else + nmc->print_output = NMC_PRINT_PRETTY; + } else if (matches (opt, "-mode") == 0) { + nmc->mode_specified = TRUE; + next_arg (&argc, &argv); + if (argc <= 1) { + g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + if (matches (argv[1], "tabular") == 0) + nmc->multiline_output = FALSE; + else if (matches (argv[1], "multiline") == 0) + nmc->multiline_output = TRUE; + else { + g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[1], opt); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + } else if (matches (opt, "-escape") == 0) { + next_arg (&argc, &argv); + if (argc <= 1) { + g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + if (matches (argv[1], "yes") == 0) + nmc->escape_values = TRUE; + else if (matches (argv[1], "no") == 0) + nmc->escape_values = FALSE; + else { + g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[1], opt); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + } else if (matches (opt, "-fields") == 0) { + next_arg (&argc, &argv); + if (argc <= 1) { + g_string_printf (nmc->return_text, _("Error: fields for '%s' options are missing."), opt); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + nmc->required_fields = g_strdup (argv[1]); + } else if (matches (opt, "-nocheck") == 0) { + nmc->nocheck_ver = TRUE; + } else if (matches (opt, "-ask") == 0) { + nmc->ask = TRUE; + } else if (matches (opt, "-wait") == 0) { + unsigned long timeout; + next_arg (&argc, &argv); + if (argc <= 1) { + g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + if (!nmc_string_to_uint (argv[1], TRUE, 0, G_MAXINT, &timeout)) { + g_string_printf (nmc->return_text, _("Error: '%s' is not a valid timeout for '%s' option."), + argv[1], opt); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + nmc->timeout = (int) timeout; + } else if (matches (opt, "-version") == 0) { + g_print (_("nmcli tool, version %s\n"), NMCLI_VERSION); + return NMC_RESULT_SUCCESS; + } else if (matches (opt, "-help") == 0) { + usage (base); + return NMC_RESULT_SUCCESS; + } else { + g_string_printf (nmc->return_text, _("Error: Option '%s' is unknown, try 'nmcli -help'."), opt); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + argc--; + argv++; + } + + if (argc > 1) { + /* Now run the requested command */ + return do_cmd (nmc, argv[1], argc-1, argv+1); + } + + usage (base); + return nmc->return_value; +} + +static gboolean nmcli_sigint = FALSE; +static pthread_mutex_t sigint_mutex = PTHREAD_MUTEX_INITIALIZER; +static gboolean nmcli_sigquit_internal = FALSE; + +gboolean +nmc_seen_sigint (void) +{ + gboolean sigint; + + pthread_mutex_lock (&sigint_mutex); + sigint = nmcli_sigint; + pthread_mutex_unlock (&sigint_mutex); + return sigint; +} + +void +nmc_clear_sigint (void) +{ + pthread_mutex_lock (&sigint_mutex); + nmcli_sigint = FALSE; + pthread_mutex_unlock (&sigint_mutex); +} + +void +nmc_set_sigquit_internal (void) +{ + nmcli_sigquit_internal = TRUE; +} + +static int +event_hook_for_readline (void) +{ + /* Make readline() exit on SIGINT */ + if (nmc_seen_sigint ()) { + rl_echo_signal_char (SIGINT); + rl_stuff_char ('\n'); + } + return 0; +} + +void *signal_handling_thread (void *arg); +/* + * Thread function waiting for signals and processing them. + * Wait for signals in signal set. The semantics of sigwait() require that all + * threads (including the thread calling sigwait()) have the signal masked, for + * reliable operation. Otherwise, a signal that arrives while this thread is + * not blocked in sigwait() might be delivered to another thread. + */ +void * +signal_handling_thread (void *arg) { + int signo; + + while (1) { + sigwait (&signal_set, &signo); + + switch (signo) { + case SIGINT: + if (nmc_get_in_readline ()) { + /* Don't quit when in readline, only signal we received SIGINT */ + pthread_mutex_lock (&sigint_mutex); + nmcli_sigint = TRUE; + pthread_mutex_unlock (&sigint_mutex); + } else { + /* We can quit nmcli */ + tcsetattr (STDIN_FILENO, TCSADRAIN, &termios_orig); + nmc_cleanup_readline (); + g_print (_("\nError: nmcli terminated by signal %s (%d)\n"), + strsignal (signo), signo); + exit (1); + } + break; + case SIGQUIT: + case SIGTERM: + tcsetattr (STDIN_FILENO, TCSADRAIN, &termios_orig); + nmc_cleanup_readline (); + if (!nmcli_sigquit_internal) + g_print (_("\nError: nmcli terminated by signal %s (%d)\n"), + strsignal (signo), signo); + exit (1); + break; + default: + break; + } + } + return NULL; +} + +/* + * Mask the signals we are interested in and create a signal handling thread. + * Because all threads inherit the signal mask from their creator, all threads + * in the process will have the signals masked. That's why setup_signals() has + * to be called before creating other threads. + */ +static gboolean +setup_signals (void) +{ + pthread_t signal_thread_id; + int status; + + sigemptyset (&signal_set); + sigaddset (&signal_set, SIGINT); + sigaddset (&signal_set, SIGQUIT); + sigaddset (&signal_set, SIGTERM); + + /* Block all signals of interest. */ + status = pthread_sigmask (SIG_BLOCK, &signal_set, NULL); + if (status != 0) { + g_printerr (_("Failed to set signal mask: %d\n"), status); + return FALSE; + } + + /* Create the signal handling thread. */ + status = pthread_create (&signal_thread_id, NULL, signal_handling_thread, NULL); + if (status != 0) { + g_printerr (_("Failed to create signal handling thread: %d\n"), status); + return FALSE; + } + + return TRUE; +} + +static void +nmc_convert_strv_to_string (const GValue *src_value, GValue *dest_value) +{ + char **strings; + + strings = g_value_get_boxed (src_value); + if (strings) + g_value_take_string (dest_value, g_strjoinv (",", strings)); + else + g_value_set_string (dest_value, ""); +} + +static void +nmc_convert_string_hash_to_string (const GValue *src_value, GValue *dest_value) +{ + GHashTable *hash; + GHashTableIter iter; + const char *key, *value; + GString *string; + + hash = (GHashTable *) g_value_get_boxed (src_value); + + string = g_string_new (NULL); + if (hash) { + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) { + if (string->len) + g_string_append_c (string, ','); + g_string_append_printf (string, "%s=%s", key, value); + } + } + + g_value_take_string (dest_value, g_string_free (string, FALSE)); +} + +static void +nmc_convert_bytes_to_string (const GValue *src_value, GValue *dest_value) +{ + GBytes *bytes; + const guint8 *array; + gsize length; + GString *printable; + guint i = 0; + + bytes = g_value_get_boxed (src_value); + + printable = g_string_new ("["); + + if (bytes) { + array = g_bytes_get_data (bytes, &length); + while (i < MIN (length, 35)) { + if (i > 0) + g_string_append_c (printable, ' '); + g_string_append_printf (printable, "0x%02X", array[i++]); + } + if (i < length) + g_string_append (printable, " ... "); + } + g_string_append_c (printable, ']'); + + g_value_take_string (dest_value, g_string_free (printable, FALSE)); +} + +static void +nmc_value_transforms_register (void) +{ + g_value_register_transform_func (G_TYPE_STRV, + G_TYPE_STRING, + nmc_convert_strv_to_string); + + /* This depends on the fact that all of the hash-table-valued properties + * in libnm-core are string->string. + */ + g_value_register_transform_func (G_TYPE_HASH_TABLE, + G_TYPE_STRING, + nmc_convert_string_hash_to_string); + + g_value_register_transform_func (G_TYPE_BYTES, + G_TYPE_STRING, + nmc_convert_bytes_to_string); +} + +static NMClient * +nmc_get_client (NmCli *nmc) +{ + GError *error = NULL; + + if (!nmc->client) { + nmc->client = nm_client_new (NULL, &error); + if (!nmc->client) { + g_critical (_("Error: Could not create NMClient object: %s."), error->message); + g_clear_error (&error); + exit (NMC_RESULT_ERROR_UNKNOWN); + } + } + + return nmc->client; +} + +/* Initialize NmCli structure - set default values */ +static void +nmc_init (NmCli *nmc) +{ + nmc->client = NULL; + nmc->get_client = &nmc_get_client; + + nmc->return_value = NMC_RESULT_SUCCESS; + nmc->return_text = g_string_new (_("Success")); + + nmc->timeout = -1; + + nmc->connections = NULL; + + nmc->secret_agent = NULL; + nmc->pwds_hash = NULL; + nmc->pk_listener = NULL; + + nmc->should_wait = FALSE; + nmc->nowait_flag = TRUE; + nmc->print_output = NMC_PRINT_NORMAL; + nmc->multiline_output = FALSE; + nmc->mode_specified = FALSE; + nmc->escape_values = TRUE; + nmc->required_fields = NULL; + nmc->output_data = g_ptr_array_new_full (20, g_free); + memset (&nmc->print_fields, '\0', sizeof (NmcPrintFields)); + nmc->nocheck_ver = FALSE; + nmc->ask = FALSE; + nmc->in_editor = FALSE; + nmc->editor_status_line = FALSE; + nmc->editor_save_confirmation = TRUE; + nmc->editor_show_secrets = FALSE; + nmc->editor_prompt_color = NMC_TERM_COLOR_NORMAL; +} + +static void +nmc_cleanup (NmCli *nmc) +{ + if (nmc->client) g_object_unref (nmc->client); + + g_string_free (nmc->return_text, TRUE); + + if (nmc->secret_agent) { + /* Destroy secret agent if we have one. */ + nm_secret_agent_old_unregister (nmc->secret_agent, NULL, NULL); + g_object_unref (nmc->secret_agent); + } + if (nmc->pwds_hash) + g_hash_table_destroy (nmc->pwds_hash); + + g_free (nmc->required_fields); + nmc_empty_output_fields (nmc); + g_ptr_array_unref (nmc->output_data); + + nmc_polkit_agent_fini (nmc); +} + +static gboolean +start (gpointer data) +{ + ArgsInfo *info = (ArgsInfo *) data; + info->nmc->return_value = parse_command_line (info->nmc, info->argc, info->argv); + + if (!info->nmc->should_wait) + g_main_loop_quit (loop); + + return FALSE; +} + + +int +main (int argc, char *argv[]) +{ + ArgsInfo args_info = { &nm_cli, argc, argv }; + + /* Set up unix signal handling */ + if (!setup_signals ()) + exit (NMC_RESULT_ERROR_UNKNOWN); + + /* Set locale to use environment variables */ + setlocale (LC_ALL, ""); + +#ifdef GETTEXT_PACKAGE + /* Set i18n stuff */ + bindtextdomain (GETTEXT_PACKAGE, NMCLI_LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); +#endif + +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + + /* Save terminal settings */ + tcgetattr (STDIN_FILENO, &termios_orig); + + /* readline init */ + rl_event_hook = event_hook_for_readline; + /* Set 0.01s timeout to mitigate slowness in readline when a broken version is used. + * See https://bugzilla.redhat.com/show_bug.cgi?id=1109946 + */ + rl_set_keyboard_input_timeout (10000); + + nmc_value_transforms_register (); + + nmc_init (&nm_cli); + g_idle_add (start, &args_info); + + loop = g_main_loop_new (NULL, FALSE); /* create main loop */ + g_main_loop_run (loop); /* run main loop */ + + /* Print result descripting text */ + if (nm_cli.return_value != NMC_RESULT_SUCCESS) { + g_printerr ("%s\n", nm_cli.return_text->str); + } + + g_main_loop_unref (loop); + nmc_cleanup (&nm_cli); + + return nm_cli.return_value; +} diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h new file mode 100644 index 00000000..32e7e5dd --- /dev/null +++ b/clients/cli/nmcli.h @@ -0,0 +1,156 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + */ + +#ifndef NMC_NMCLI_H +#define NMC_NMCLI_H + +#include "config.h" + +#include <NetworkManager.h> +#include <nm-secret-agent-old.h> + +#if WITH_POLKIT_AGENT +#include "nm-polkit-listener.h" +#else +/* polkit agent is not available; define fake NMPolkitListener */ +typedef gpointer NMPolkitListener; +#endif + +/* nmcli exit codes */ +typedef enum { + /* Indicates successful execution */ + NMC_RESULT_SUCCESS = 0, + + /* Unknown / unspecified error */ + NMC_RESULT_ERROR_UNKNOWN = 1, + + /* Wrong invocation of nmcli */ + NMC_RESULT_ERROR_USER_INPUT = 2, + + /* A timeout expired */ + NMC_RESULT_ERROR_TIMEOUT_EXPIRED = 3, + + /* Error in connection activation */ + NMC_RESULT_ERROR_CON_ACTIVATION = 4, + + /* Error in connection deactivation */ + NMC_RESULT_ERROR_CON_DEACTIVATION = 5, + + /* Error in device disconnect */ + NMC_RESULT_ERROR_DEV_DISCONNECT = 6, + + /* Error in connection deletion */ + NMC_RESULT_ERROR_CON_DEL = 7, + + /* NetworkManager is not running */ + NMC_RESULT_ERROR_NM_NOT_RUNNING = 8, + + /* nmcli and NetworkManager versions mismatch */ + NMC_RESULT_ERROR_VERSIONS_MISMATCH = 9, + + /* Connection/Device/AP not found */ + NMC_RESULT_ERROR_NOT_FOUND = 10 +} NMCResultCode; + +typedef enum { + NMC_PRINT_TERSE = 0, + NMC_PRINT_NORMAL = 1, + NMC_PRINT_PRETTY = 2 +} NMCPrintOutput; + +/* === Output fields === */ +/* Flags for NmcOutputField */ +#define NMC_OF_FLAG_FIELD_NAMES 0x00000001 /* Print field names instead of values */ +#define NMC_OF_FLAG_SECTION_PREFIX 0x00000002 /* Use the first value as section prefix for the other field names - just in multiline */ +#define NMC_OF_FLAG_MAIN_HEADER_ADD 0x00000004 /* Print main header in addition to values/field names */ +#define NMC_OF_FLAG_MAIN_HEADER_ONLY 0x00000008 /* Print main header only */ + +typedef struct _NmcOutputField { + const char *name; /* Field's name */ + const char *name_l10n; /* Field's name for translation */ + int width; /* Width in screen columns */ + struct _NmcOutputField *group; /* Points to an array with available section field names if this is a section (group) field */ + void *value; /* Value of current field - char* or char** (NULL-terminated array) */ + gboolean value_is_array; /* Whether value is char** instead of char* */ + gboolean free_value; /* Whether to free the value */ + guint32 flags; /* Flags - whether and how to print values/field names/headers */ +} NmcOutputField; + +typedef struct { + GArray *indices; /* Array of field indices to the array of allowed fields */ + char *header_name; /* Name of the output */ + int indent; /* Indent by this number of spaces */ +} NmcPrintFields; + +typedef enum { + NMC_TERM_COLOR_NORMAL = 0, + NMC_TERM_COLOR_BLACK = 1, + NMC_TERM_COLOR_RED = 2, + NMC_TERM_COLOR_GREEN = 3, + NMC_TERM_COLOR_YELLOW = 4, + NMC_TERM_COLOR_BLUE = 5, + NMC_TERM_COLOR_MAGENTA = 6, + NMC_TERM_COLOR_CYAN = 7, + NMC_TERM_COLOR_WHITE = 8 +} NmcTermColor; + +/* NmCli - main structure */ +typedef struct _NmCli { + NMClient *client; /* Pointer to NMClient of libnm */ + NMClient *(*get_client) (struct _NmCli *nmc); /* Pointer to function for creating NMClient */ + + NMCResultCode return_value; /* Return code of nmcli */ + GString *return_text; /* Reason text */ + + int timeout; /* Operation timeout */ + + const GPtrArray *connections; /* List of connections */ + + NMSecretAgentOld *secret_agent; /* Secret agent */ + GHashTable *pwds_hash; /* Hash table with passwords in passwd-file */ + NMPolkitListener *pk_listener ; /* polkit agent listener */ + + gboolean should_wait; /* Indication that nmcli should not end yet */ + gboolean nowait_flag; /* '--nowait' option; used for passing to callbacks */ + NMCPrintOutput print_output; /* Output mode */ + gboolean multiline_output; /* Multiline output instead of default tabular */ + gboolean mode_specified; /* Whether tabular/multiline mode was specified via '--mode' option */ + gboolean escape_values; /* Whether to escape ':' and '\' in terse tabular mode */ + char *required_fields; /* Required fields in output: '--fields' option */ + GPtrArray *output_data; /* GPtrArray of arrays of NmcOutputField structs - accumulates data for output */ + NmcPrintFields print_fields; /* Structure with field indices to print */ + gboolean nocheck_ver; /* Don't check nmcli and NM versions: option '--nocheck' */ + gboolean ask; /* Ask for missing parameters: option '--ask' */ + gboolean in_editor; /* Whether running the editor - nmcli con edit' */ + gboolean editor_status_line; /* Whether to display status line in connection editor */ + gboolean editor_save_confirmation; /* Whether to ask for confirmation on saving connections with 'autoconnect=yes' */ + gboolean editor_show_secrets; /* Whether to display secrets in the editor' */ + NmcTermColor editor_prompt_color; /* Color of prompt in connection editor */ +} NmCli; + +/* Error quark for GError domain */ +#define NMCLI_ERROR (nmcli_error_quark ()) +GQuark nmcli_error_quark (void); + +gboolean nmc_seen_sigint (void); +void nmc_clear_sigint (void); +void nmc_set_sigquit_internal (void); + + +#endif /* NMC_NMCLI_H */ diff --git a/clients/cli/polkit-agent.c b/clients/cli/polkit-agent.c new file mode 100644 index 00000000..a163b824 --- /dev/null +++ b/clients/cli/polkit-agent.c @@ -0,0 +1,166 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2014 Red Hat, Inc. + */ + +#include "config.h" + +#if WITH_POLKIT_AGENT + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <termios.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "polkit-agent.h" +#include "nm-polkit-listener.h" +#include "common.h" + +static char * +polkit_request (const char *request, + const char *action_id, + const char *message, + const char *icon_name, + const char *user, + gboolean echo_on, + gpointer user_data) +{ + char *response, *tmp, *p; + struct termios termios_orig, termios_new; + + g_print ("%s\n", message); + g_print ("(action_id: %s)\n", action_id); + + if (!echo_on) { + tcgetattr (STDIN_FILENO, &termios_orig); + termios_new = termios_orig; + termios_new.c_lflag &= ~(ECHO); + tcsetattr (STDIN_FILENO, TCSADRAIN, &termios_new); + } + + /* Ask user for polkit authorization password */ + if (user) { + /* chop of ": " if present */ + tmp = g_strdup (request); + p = strrchr (tmp, ':'); + if (p && !strcmp (p, ": ")) + *p = '\0'; + response = nmc_readline ("%s (%s): ", tmp, user); + g_free (tmp); + } else + response = nmc_readline ("%s", request); + g_print ("\n"); + + /* Restore original terminal settings */ + if (!echo_on) + tcsetattr (STDIN_FILENO, TCSADRAIN, &termios_orig); + + return response; +} + +static void +polkit_show_info (const char *text) +{ + g_print (_("Authentication message: %s\n"), text); +} + +static void +polkit_show_error (const char *text) +{ + g_print (_("Authentication error: %s\n"), text); +} + +static void +polkit_completed (gboolean gained_authorization) +{ + /* We don't print anything here. The outcome will be evident from + * the operation result anyway. */ +} + +gboolean +nmc_polkit_agent_init (NmCli* nmc, gboolean for_session, GError **error) +{ + PolkitAgentListener *listener; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + listener = nm_polkit_listener_new (for_session, error); + if (!listener) + return FALSE; + + nm_polkit_listener_set_request_callback (NM_POLKIT_LISTENER (listener), polkit_request, nmc); + nm_polkit_listener_set_show_info_callback (NM_POLKIT_LISTENER (listener), polkit_show_info); + nm_polkit_listener_set_show_error_callback (NM_POLKIT_LISTENER (listener), polkit_show_error); + nm_polkit_listener_set_completed_callback (NM_POLKIT_LISTENER (listener), polkit_completed); + + nmc->pk_listener = NM_POLKIT_LISTENER (listener); + return TRUE; +} + +void +nmc_polkit_agent_fini (NmCli* nmc) +{ + g_clear_object (&nmc->pk_listener); +} + +gboolean +nmc_start_polkit_agent_start_try (NmCli *nmc) +{ + GError *error = NULL; + + /* We don't register polkit agent at all when running non-interactively */ + if (!nmc->ask) + return TRUE; + + if (!nmc_polkit_agent_init (nmc, FALSE, &error)) { + g_printerr (_("Warning: polkit agent initialization failed: %s\n"), + error->message); + g_error_free (error); + return FALSE; + } + return TRUE; +} + +#else +/* polkit agent is not avalable; implement stub functions. */ + +#include <glib.h> +#include "nmcli.h" +#include "polkit-agent.h" + +gboolean +nmc_polkit_agent_init (NmCli* nmc, gboolean for_session, GError **error) +{ + return TRUE; +} + +void +nmc_polkit_agent_fini (NmCli* nmc) +{ +} + +gboolean +nmc_start_polkit_agent_start_try (NmCli *nmc) +{ + return TRUE; +} + +#endif /* #if WITH_POLKIT_AGENT */ diff --git a/clients/cli/polkit-agent.h b/clients/cli/polkit-agent.h new file mode 100644 index 00000000..e2902dc0 --- /dev/null +++ b/clients/cli/polkit-agent.h @@ -0,0 +1,30 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2014 Red Hat, Inc. + */ + +#ifndef __NMC_POLKIT_AGENT_H__ +#define __NMC_POLKIT_AGENT_H__ + +#include "nmcli.h" + +gboolean nmc_polkit_agent_init (NmCli *nmc, gboolean for_session, GError **error); +void nmc_polkit_agent_fini (NmCli* nmc); + +gboolean nmc_start_polkit_agent_start_try (NmCli *nmc); + +#endif /* __NMC_POLKIT_AGENT_H__ */ diff --git a/clients/cli/settings-docs.c b/clients/cli/settings-docs.c new file mode 100644 index 00000000..1edfddb5 --- /dev/null +++ b/clients/cli/settings-docs.c @@ -0,0 +1,388 @@ +/* Generated file. Do not edit. */ + +typedef struct { + const char *name; + const char *docs; +} NmcPropertyDesc; + +NmcPropertyDesc setting_802_11_olpc_mesh[] = { + { "channel", "Channel on which the mesh network to join is located." }, + { "dhcp-anycast-address", "Anycast DHCP MAC address used when requesting an IP address via DHCP. The specific anycast address used determines which DHCP server class answers the request." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "ssid", "SSID of the mesh network to join." }, +}; + +NmcPropertyDesc setting_802_11_wireless[] = { + { "band", "802.11 frequency band of the network. One of \"a\" for 5GHz 802.11a or \"bg\" for 2.4GHz 802.11. This will lock associations to the Wi-Fi network to the specific band, i.e. if \"a\" is specified, the device will not associate with the same network in the 2.4GHz band even if the network's settings are compatible. This setting depends on specific driver capability and may not work with all drivers." }, + { "bssid", "If specified, directs the device to only associate with the given access point. This capability is highly driver dependent and not supported by all devices. Note: this property does not control the BSSID used when creating an Ad-Hoc network and is unlikely to in the future." }, + { "channel", "Wireless channel to use for the Wi-Fi connection. The device will only join (or create for Ad-Hoc networks) a Wi-Fi network on the specified channel. Because channel numbers overlap between bands, this property also requires the \"band\" property to be set." }, + { "cloned-mac-address", "If specified, request that the Wi-Fi device use this MAC address instead of its permanent MAC address. This is known as MAC cloning or spoofing." }, + { "hidden", "If TRUE, indicates this network is a non-broadcasting network that hides its SSID. In this case various workarounds may take place, such as probe-scanning the SSID for more reliable network discovery. However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution." }, + { "mac-address", "If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing)." }, + { "mac-address-blacklist", "A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply. Each MAC address should be given in the standard hex-digits-and-colons notation (eg \"00:11:22:33:44:55\")." }, + { "mode", "Wi-Fi network mode; one of \"infrastructure\", \"adhoc\" or \"ap\". If blank, infrastructure is assumed." }, + { "mtu", "If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "rate", "If non-zero, directs the device to only use the specified bitrate for communication with the access point. Units are in Kb/s, ie 5500 = 5.5 Mbit/s. This property is highly driver dependent and not all devices support setting a static bitrate." }, + { "seen-bssids", "A list of BSSIDs (each BSSID formatted as a MAC address like \"00:11:22:33:44:55\") that have been detected as part of the Wi-Fi network. NetworkManager internally tracks previously seen BSSIDs. The property is only meant for reading and reflects the BSSID list of NetworkManager. The changes you make to this property will not be preserved." }, + { "ssid", "SSID of the Wi-Fi network. Must be specified." }, + { "tx-power", "If non-zero, directs the device to use the specified transmit power. Units are dBm. This property is highly driver dependent and not all devices support setting a static transmit power." }, +}; + +NmcPropertyDesc setting_802_11_wireless_security[] = { + { "auth-alg", "When WEP is used (ie, key-mgmt = \"none\" or \"ieee8021x\") indicate the 802.11 authentication algorithm required by the AP here. One of \"open\" for Open System, \"shared\" for Shared Key, or \"leap\" for Cisco LEAP. When using Cisco LEAP (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\") the \"leap-username\" and \"leap-password\" properties must be specified." }, + { "group", "A list of group/broadcast encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty. Each list element may be one of \"wep40\", \"wep104\", \"tkip\", or \"ccmp\"." }, + { "key-mgmt", "Key management used for the connection. One of \"none\" (WEP), \"ieee8021x\" (Dynamic WEP), \"wpa-none\" (Ad-Hoc WPA-PSK), \"wpa-psk\" (infrastructure WPA-PSK), or \"wpa-eap\" (WPA-Enterprise). This property must be set for any Wi-Fi connection that uses security." }, + { "leap-password", "The login password for legacy LEAP connections (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\")." }, + { "leap-password-flags", "Flags indicating how to handle the \"leap-password\" property." }, + { "leap-username", "The login username for legacy LEAP connections (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\")." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "pairwise", "A list of pairwise encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty. Each list element may be one of \"tkip\" or \"ccmp\"." }, + { "proto", "List of strings specifying the allowed WPA protocol versions to use. Each element may be one \"wpa\" (allow WPA) or \"rsn\" (allow WPA2/RSN). If not specified, both WPA and RSN connections are allowed." }, + { "psk", "Pre-Shared-Key for WPA networks. If the key is 64-characters long, it must contain only hexadecimal characters and is interpreted as a hexadecimal WPA key. Otherwise, the key must be between 8 and 63 ASCII characters (as specified in the 802.11i standard) and is interpreted as a WPA passphrase, and is hashed to derive the actual WPA-PSK used when connecting to the Wi-Fi network." }, + { "psk-flags", "Flags indicating how to handle the \"psk\" property." }, + { "wep-key-flags", "Flags indicating how to handle the \"wep-key0\", \"wep-key1\", \"wep-key2\", and \"wep-key3\" properties." }, + { "wep-key-type", "Controls the interpretation of WEP keys. Allowed values are NM_WEP_KEY_TYPE_KEY (1), in which case the key is either a 10- or 26-character hexadecimal string, or a 5- or 13-character ASCII password; or NM_WEP_KEY_TYPE_PASSPHRASE (2), in which case the passphrase is provided as a string and will be hashed using the de-facto MD5 method to derive the actual WEP key." }, + { "wep-key0", "Index 0 WEP key. This is the WEP key used in most networks. See the \"wep-key-type\" property for a description of how this key is interpreted." }, + { "wep-key1", "Index 1 WEP key. This WEP index is not used by most networks. See the \"wep-key-type\" property for a description of how this key is interpreted." }, + { "wep-key2", "Index 2 WEP key. This WEP index is not used by most networks. See the \"wep-key-type\" property for a description of how this key is interpreted." }, + { "wep-key3", "Index 3 WEP key. This WEP index is not used by most networks. See the \"wep-key-type\" property for a description of how this key is interpreted." }, + { "wep-tx-keyidx", "When static WEP is used (ie, key-mgmt = \"none\") and a non-default WEP key index is used by the AP, put that WEP key index here. Valid values are 0 (default key) through 3. Note that some consumer access points (like the Linksys WRT54G) number the keys 1 - 4." }, +}; + +NmcPropertyDesc setting_802_1x[] = { + { "altsubject-matches", "List of strings to be matched against the altSubjectName of the certificate presented by the authentication server. If the list is empty, no verification of the server certificate's altSubjectName is performed." }, + { "anonymous-identity", "Anonymous identity string for EAP authentication methods. Used as the unencrypted identity with EAP types that support different tunneled identity like EAP-TTLS." }, + { "ca-cert", "Contains the CA certificate if used by the EAP method specified in the \"eap\" property. Certificate data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended." }, + { "ca-path", "UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the \"ca-cert\" property." }, + { "client-cert", "Contains the client certificate if used by the EAP method specified in the \"eap\" property. Certificate data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte." }, + { "eap", "The allowed EAP method to be used when authenticating to the network with 802.1x. Valid methods are: \"leap\", \"md5\", \"tls\", \"peap\", \"ttls\", \"pwd\", and \"fast\". Each method requires different configuration using the properties of this setting; refer to wpa_supplicant documentation for the allowed combinations." }, + { "identity", "Identity string for EAP authentication methods. Often the user's user or login name." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "pac-file", "UTF-8 encoded file path containing PAC for EAP-FAST." }, + { "password", "UTF-8 encoded password used for EAP authentication methods. If both the \"password\" property and the \"password-raw\" property are specified, \"password\" is preferred." }, + { "password-flags", "Flags indicating how to handle the \"password\" property." }, + { "password-raw", "Password used for EAP authentication methods, given as a byte array to allow passwords in other encodings than UTF-8 to be used. If both the \"password\" property and the \"password-raw\" property are specified, \"password\" is preferred." }, + { "password-raw-flags", "Flags indicating how to handle the \"password-raw\" property." }, + { "phase1-fast-provisioning", "Enables or disables in-line provisioning of EAP-FAST credentials when FAST is specified as the EAP method in the \"eap\" property. Recognized values are \"0\" (disabled), \"1\" (allow unauthenticated provisioning), \"2\" (allow authenticated provisioning), and \"3\" (allow both authenticated and unauthenticated provisioning). See the wpa_supplicant documentation for more details." }, + { "phase1-peaplabel", "Forces use of the new PEAP label during key derivation. Some RADIUS servers may require forcing the new PEAP label to interoperate with PEAPv1. Set to \"1\" to force use of the new PEAP label. See the wpa_supplicant documentation for more details." }, + { "phase1-peapver", "Forces which PEAP version is used when PEAP is set as the EAP method in the \"eap\" property. When unset, the version reported by the server will be used. Sometimes when using older RADIUS servers, it is necessary to force the client to use a particular PEAP version. To do so, this property may be set to \"0\" or \"1\" to force that specific PEAP version." }, + { "phase2-altsubject-matches", "List of strings to be matched against the altSubjectName of the certificate presented by the authentication server during the inner \"phase 2\" authentication. If the list is empty, no verification of the server certificate's altSubjectName is performed." }, + { "phase2-auth", "Specifies the allowed \"phase 2\" inner non-EAP authentication methods when an EAP method that uses an inner TLS tunnel is specified in the \"eap\" property. Recognized non-EAP \"phase 2\" methods are \"pap\", \"chap\", \"mschap\", \"mschapv2\", \"gtc\", \"otp\", \"md5\", and \"tls\". Each \"phase 2\" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details." }, + { "phase2-autheap", "Specifies the allowed \"phase 2\" inner EAP-based authentication methods when an EAP method that uses an inner TLS tunnel is specified in the \"eap\" property. Recognized EAP-based \"phase 2\" methods are \"md5\", \"mschapv2\", \"otp\", \"gtc\", and \"tls\". Each \"phase 2\" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details." }, + { "phase2-ca-cert", "Contains the \"phase 2\" CA certificate if used by the EAP method specified in the \"phase2-auth\" or \"phase2-autheap\" properties. Certificate data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended." }, + { "phase2-ca-path", "UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the \"phase2-ca-cert\" property." }, + { "phase2-client-cert", "Contains the \"phase 2\" client certificate if used by the EAP method specified in the \"phase2-auth\" or \"phase2-autheap\" properties. Certificate data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended." }, + { "phase2-private-key", "Contains the \"phase 2\" inner private key when the \"phase2-auth\" or \"phase2-autheap\" property is set to \"tls\". Key data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the \"phase2-private-key-password\" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and and ending with a terminating NUL byte, and as with the blob scheme the \"phase2-private-key-password\" property must be set to the password used to decode the PKCS#12 private key and certificate." }, + { "phase2-private-key-password", "The password used to decrypt the \"phase 2\" private key specified in the \"phase2-private-key\" property when the private key either uses the path scheme, or is a PKCS#12 format key." }, + { "phase2-private-key-password-flags", "Flags indicating how to handle the \"phase2-private-key-password\" property." }, + { "phase2-subject-match", "Substring to be matched against the subject of the certificate presented by the authentication server during the inner \"phase 2\" authentication. When unset, no verification of the authentication server certificate's subject is performed." }, + { "pin", "PIN used for EAP authentication methods." }, + { "pin-flags", "Flags indicating how to handle the \"pin\" property." }, + { "private-key", "Contains the private key when the \"eap\" property is set to \"tls\". Key data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the \"private-key-password\" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and and ending with a terminating NUL byte, and as with the blob scheme the \"private-key-password\" property must be set to the password used to decode the PKCS#12 private key and certificate. WARNING: \"private-key\" is not a \"secret\" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users. Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data." }, + { "private-key-password", "The password used to decrypt the private key specified in the \"private-key\" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key." }, + { "private-key-password-flags", "Flags indicating how to handle the \"private-key-password\" property." }, + { "subject-match", "Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed." }, + { "system-ca-certs", "When TRUE, overrides the \"ca-path\" and \"phase2-ca-path\" properties using the system CA directory specified at configure time with the --system-ca-path switch. The certificates in this directory are added to the verification chain in addition to any certificates specified by the \"ca-cert\" and \"phase2-ca-cert\" properties." }, +}; + +NmcPropertyDesc setting_802_3_ethernet[] = { + { "auto-negotiate", "If TRUE, allow auto-negotiation of port speed and duplex mode. If FALSE, do not allow auto-negotiation, in which case the \"speed\" and \"duplex\" properties should be set." }, + { "cloned-mac-address", "If specified, request that the device use this MAC address instead of its permanent MAC address. This is known as MAC cloning or spoofing." }, + { "duplex", "If specified, request that the device only use the specified duplex mode. Either \"half\" or \"full\"." }, + { "mac-address", "If specified, this connection will only apply to the Ethernet device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing)." }, + { "mac-address-blacklist", "If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list. Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55)." }, + { "mtu", "If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "port", "Specific port type to use if multiple the device supports multiple attachment methods. One of \"tp\" (Twisted Pair), \"aui\" (Attachment Unit Interface), \"bnc\" (Thin Ethernet) or \"mii\" (Media Independent Interface. If the device supports only one port type, this setting is ignored." }, + { "s390-nettype", "s390 network device type; one of \"qeth\", \"lcs\", or \"ctc\", representing the different types of virtual network devices available on s390 systems." }, + { "s390-options", "Dictionary of key/value pairs of s390-specific device options. Both keys and values must be strings. Allowed keys include \"portno\", \"layer2\", \"portname\", \"protocol\", among others. Key names must contain only alphanumeric characters (ie, [a-zA-Z0-9])." }, + { "s390-subchannels", "Identifies specific subchannels that this network device uses for communication with z/VM or s390 host. Like the \"mac-address\" property for non-z/VM devices, this property can be used to ensure this connection only applies to the network device that uses these subchannels. The list should contain exactly 3 strings, and each string may only be composed of hexadecimal characters and the period (.) character." }, + { "speed", "If non-zero, request that the device use only the specified speed. In Mbit/s, ie 100 == 100Mbit/s." }, +}; + +NmcPropertyDesc setting_adsl[] = { + { "encapsulation", "Encapsulation of ADSL connection. Can be \"vcmux\" or \"llc\"." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "password", "Password used to authenticate with the ADSL service." }, + { "password-flags", "Flags indicating how to handle the \"password\" property." }, + { "protocol", "ADSL connection protocol. Can be \"pppoa\", \"pppoe\" or \"ipoatm\"." }, + { "username", "Username used to authenticate with the ADSL service." }, + { "vci", "VCI of ADSL connection" }, + { "vpi", "VPI of ADSL connection" }, +}; + +NmcPropertyDesc setting_bluetooth[] = { + { "bdaddr", "The Bluetooth address of the device." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "type", "Either \"dun\" for Dial-Up Networking connections or \"panu\" for Personal Area Networking connections to devices supporting the NAP profile." }, +}; + +NmcPropertyDesc setting_bond[] = { + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "options", "Dictionary of key/value pairs of bonding options. Both keys and values must be strings. Option names must contain only alphanumeric characters (ie, [a-zA-Z0-9])." }, +}; + +NmcPropertyDesc setting_bridge[] = { + { "ageing-time", "The Ethernet MAC address aging time, in seconds." }, + { "forward-delay", "The Spanning Tree Protocol (STP) forwarding delay, in seconds." }, + { "hello-time", "The Spanning Tree Protocol (STP) hello time, in seconds." }, + { "mac-address", "If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. When matching an existing (outside NetworkManager created) bridge, this MAC address must match." }, + { "max-age", "The Spanning Tree Protocol (STP) maximum message age, in seconds." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "priority", "Sets the Spanning Tree Protocol (STP) priority for this bridge. Lower values are \"better\"; the lowest priority bridge will be elected the root bridge." }, + { "stp", "Controls whether Spanning Tree Protocol (STP) is enabled for this bridge." }, +}; + +NmcPropertyDesc setting_bridge_port[] = { + { "hairpin-mode", "Enables or disabled \"hairpin mode\" for the port, which allows frames to be sent back out through the port the frame was received on." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "path-cost", "The Spanning Tree Protocol (STP) port cost for destinations via this port." }, + { "priority", "The Spanning Tree Protocol (STP) priority of this bridge port." }, +}; + +NmcPropertyDesc setting_cdma[] = { + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "number", "The number to dial to establish the connection to the CDMA-based mobile broadband network, if any. If not specified, the default number (#777) is used when required." }, + { "password", "The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here." }, + { "password-flags", "Flags indicating how to handle the \"password\" property." }, + { "username", "The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here." }, +}; + +NmcPropertyDesc setting_connection[] = { + { "autoconnect", "Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection." }, + { "autoconnect-priority", "The autoconnect priority. If the connection is set to autoconnect, connections with higher priority will be preferred. Defaults to 0. The higher number means higher priority." }, + { "gateway-ping-timeout", "If greater than zero, delay success of IP addressing until either the timeout is reached, or an IP gateway replies to a ping." }, + { "id", "A human readable unique identifier for the connection, like \"Work Wi-Fi\" or \"T-Mobile 3G\"." }, + { "interface-name", "The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface." }, + { "master", "Interface name of the master device or UUID of the master connection." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "permissions", "An array of strings defining what access a given user has to this connection. If this is NULL or empty, all users are allowed to access this connection. Otherwise a user is allowed to access this connection if and only if they are in this list. Each entry is of the form \"[type]:[id]:[reserved]\"; for example, \"user:dcbw:blah\". At this time only the \"user\" [type] is allowed. Any other values are ignored and reserved for future use. [id] is the username that this permission refers to, which may not contain the \":\" character. Any [reserved] information present must be ignored and is reserved for future use. All of [type], [id], and [reserved] must be valid UTF-8." }, + { "read-only", "FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified." }, + { "secondaries", "List of connection UUIDs that should be activated when the base connection itself is activated. Currently only VPN connections are supported." }, + { "slave-type", "Setting name of the device type of this slave's master connection (eg, \"bond\"), or NULL if this connection is not a slave." }, + { "timestamp", "The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved)." }, + { "type", "Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc)." }, + { "uuid", "A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the \"id\" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or \"type\" property changes. The UUID must be in the format \"2815492f-7e56-435e-b2e9-246bd7cdc664\" (ie, contains only hexadecimal characters and \"-\")." }, + { "zone", "The trust level of a the connection. Free form case-insensitive string (for example \"Home\", \"Work\", \"Public\"). NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall." }, +}; + +NmcPropertyDesc setting_dcb[] = { + { "app-fcoe-flags", "Specifies the NMSettingDcbFlags for the DCB FCoE application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." }, + { "app-fcoe-mode", "The FCoE controller mode; either \"fabric\" (default) or \"vn2vn\"." }, + { "app-fcoe-priority", "The highest User Priority (0 - 7) which FCoE frames should use, or -1 for default priority. Only used when the \"app-fcoe-flags\" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag." }, + { "app-fip-flags", "Specifies the NMSettingDcbFlags for the DCB FIP application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." }, + { "app-fip-priority", "The highest User Priority (0 - 7) which FIP frames should use, or -1 for default priority. Only used when the \"app-fip-flags\" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag." }, + { "app-iscsi-flags", "Specifies the NMSettingDcbFlags for the DCB iSCSI application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." }, + { "app-iscsi-priority", "The highest User Priority (0 - 7) which iSCSI frames should use, or -1 for default priority. Only used when the \"app-iscsi-flags\" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "priority-bandwidth", "An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the percentage of bandwidth of the priority's assigned group that the priority may use. The sum of all percentages for priorities which belong to the same group must total 100 percent." }, + { "priority-flow-control", "An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the corresponding priority should transmit priority pause." }, + { "priority-flow-control-flags", "Specifies the NMSettingDcbFlags for DCB Priority Flow Control (PFC). Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." }, + { "priority-group-bandwidth", "An array of 8 uint values, where the array index corresponds to the Priority Group ID (0 - 7) and the value indicates the percentage of link bandwidth allocated to that group. Allowed values are 0 - 100, and the sum of all values must total 100 percent." }, + { "priority-group-flags", "Specifies the NMSettingDcbFlags for DCB Priority Groups. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." }, + { "priority-group-id", "An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the Priority Group ID. Allowed Priority Group ID values are 0 - 7 or 15 for the unrestricted group." }, + { "priority-strict-bandwidth", "An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the priority may use all of the bandwidth allocated to its assigned group." }, + { "priority-traffic-class", "An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the traffic class (0 - 7) to which the priority is mapped." }, +}; + +NmcPropertyDesc setting_generic[] = { + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, +}; + +NmcPropertyDesc setting_gsm[] = { + { "apn", "The GPRS Access Point Name specifying the APN used when establishing a data session with the GSM-based network. The APN often determines how the user will be billed for their network usage and whether the user has access to the Internet or just a provider-specific walled-garden, so it is important to use the correct APN for the user's mobile broadband plan. The APN may only be composed of the characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9." }, + { "home-only", "When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "network-id", "The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration. If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible." }, + { "number", "Number to dial when establishing a PPP data session with the GSM-based mobile broadband network. Many modems do not require PPP for connections to the mobile network and thus this property should be left blank, which allows NetworkManager to select the appropriate settings automatically." }, + { "password", "The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here." }, + { "password-flags", "Flags indicating how to handle the \"password\" property." }, + { "pin", "If the SIM is locked with a PIN it must be unlocked before any other operations are requested. Specify the PIN here to allow operation of the device." }, + { "pin-flags", "Flags indicating how to handle the \"pin\" property." }, + { "username", "The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here." }, +}; + +NmcPropertyDesc setting_infiniband[] = { + { "mac-address", "If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing)." }, + { "mtu", "If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "p-key", "The InfiniBand P_Key to use for this device. A value of -1 means to use the default P_Key (aka \"the P_Key at index 0\"). Otherwise it is a 16-bit unsigned integer, whose high bit is set if it is a \"full membership\" P_Key." }, + { "parent", "The interface name of the parent device of this device. Normally NULL, but if the \"p_key\" property is set, then you must specify the base device by setting either this property or \"mac-address\"." }, + { "transport-mode", "The IP-over-InfiniBand transport mode. Either \"datagram\" or \"connected\"." }, +}; + +NmcPropertyDesc setting_ipv4[] = { + { "addresses", "Array of IP addresses." }, + { "dhcp-client-id", "A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options." }, + { "dhcp-hostname", "If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease." }, + { "dhcp-send-hostname", "If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent." }, + { "dns", "Array of IP addresses of DNS servers." }, + { "dns-search", "Array of DNS search domains." }, + { "gateway", "The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set." }, + { "ignore-auto-dns", "When \"method\" is set to \"auto\" and this property to TRUE, automatically configured nameservers and search domains are ignored and only nameservers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used." }, + { "ignore-auto-routes", "When \"method\" is set to \"auto\" and this property to TRUE, automatically configured routes are ignored and only routes specified in the \"routes\" property, if any, are used." }, + { "may-fail", "If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully." }, + { "method", "IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support \"auto\", \"manual\", and \"link-local\". See the subclass-specific documentation for other values. In general, for the \"auto\" method, properties such as \"dns\" and \"routes\" specify information that is added on to the information returned from automatic configuration. The \"ignore-auto-routes\" and \"ignore-auto-dns\" properties modify this behavior. For methods that imply no upstream network, such as \"shared\" or \"link-local\", these properties must be empty." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "never-default", "If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager." }, + { "route-metric", "The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is choosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric." }, + { "routes", "Array of IP routes." }, +}; + +NmcPropertyDesc setting_ipv6[] = { + { "addresses", "Array of IP addresses." }, + { "dhcp-hostname", "If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease." }, + { "dhcp-send-hostname", "If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent." }, + { "dns", "Array of IP addresses of DNS servers." }, + { "dns-search", "Array of DNS search domains." }, + { "gateway", "The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set." }, + { "ignore-auto-dns", "When \"method\" is set to \"auto\" and this property to TRUE, automatically configured nameservers and search domains are ignored and only nameservers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used." }, + { "ignore-auto-routes", "When \"method\" is set to \"auto\" and this property to TRUE, automatically configured routes are ignored and only routes specified in the \"routes\" property, if any, are used." }, + { "ip6-privacy", "Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941. If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64. This enhances privacy, but could cause problems in some applications, on the other hand. The permitted values are: 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses)." }, + { "may-fail", "If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully." }, + { "method", "IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support \"auto\", \"manual\", and \"link-local\". See the subclass-specific documentation for other values. In general, for the \"auto\" method, properties such as \"dns\" and \"routes\" specify information that is added on to the information returned from automatic configuration. The \"ignore-auto-routes\" and \"ignore-auto-dns\" properties modify this behavior. For methods that imply no upstream network, such as \"shared\" or \"link-local\", these properties must be empty." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "never-default", "If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager." }, + { "route-metric", "The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is choosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric." }, + { "routes", "Array of IP routes." }, +}; + +NmcPropertyDesc setting_ppp[] = { + { "baud", "If non-zero, instruct pppd to set the serial port to the specified baudrate. This value should normally be left as 0 to automatically choose the speed." }, + { "crtscts", "If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals. This value should normally be set to FALSE." }, + { "lcp-echo-failure", "If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer. The \"lcp-echo-interval\" property must also be set to a non-zero value if this property is used." }, + { "lcp-echo-interval", "If non-zero, instruct pppd to send an LCP echo-request frame to the peer every n seconds (where n is the specified value). Note that some PPP peers will respond to echo requests and some will not, and it is not possible to autodetect this." }, + { "mppe-stateful", "If TRUE, stateful MPPE is used. See pppd documentation for more information on stateful MPPE." }, + { "mru", "If non-zero, instruct pppd to request that the peer send packets no larger than the specified size. If non-zero, the MRU should be between 128 and 16384." }, + { "mtu", "If non-zero, instruct pppd to send packets no larger than the specified size." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "no-vj-comp", "If TRUE, Van Jacobsen TCP header compression will not be requested." }, + { "noauth", "If TRUE, do not require the other side (usually the PPP server) to authenticate itself to the client. If FALSE, require authentication from the remote side. In almost all cases, this should be TRUE." }, + { "nobsdcomp", "If TRUE, BSD compression will not be requested." }, + { "nodeflate", "If TRUE, \"deflate\" compression will not be requested." }, + { "refuse-chap", "If TRUE, the CHAP authentication method will not be used." }, + { "refuse-eap", "If TRUE, the EAP authentication method will not be used." }, + { "refuse-mschap", "If TRUE, the MSCHAP authentication method will not be used." }, + { "refuse-mschapv2", "If TRUE, the MSCHAPv2 authentication method will not be used." }, + { "refuse-pap", "If TRUE, the PAP authentication method will not be used." }, + { "require-mppe", "If TRUE, MPPE (Microsoft Point-to-Point Encrpytion) will be required for the PPP session. If either 64-bit or 128-bit MPPE is not available the session will fail. Note that MPPE is not used on mobile broadband connections." }, + { "require-mppe-128", "If TRUE, 128-bit MPPE (Microsoft Point-to-Point Encrpytion) will be required for the PPP session, and the \"require-mppe\" property must also be set to TRUE. If 128-bit MPPE is not available the session will fail." }, +}; + +NmcPropertyDesc setting_pppoe[] = { + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "password", "Password used to authenticate with the PPPoE service." }, + { "password-flags", "Flags indicating how to handle the \"password\" property." }, + { "service", "If specified, instruct PPPoE to only initiate sessions with access concentrators that provide the specified service. For most providers, this should be left blank. It is only required if there are multiple access concentrators or a specific service is known to be required." }, + { "username", "Username used to authenticate with the PPPoE service." }, +}; + +NmcPropertyDesc setting_serial[] = { + { "baud", "Speed to use for communication over the serial port. Note that this value usually has no effect for mobile broadband modems as they generally ignore speed settings and use the highest available speed." }, + { "bits", "Byte-width of the serial communication. The 8 in \"8n1\" for example." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "parity", "Parity setting of the serial port." }, + { "send-delay", "Time to delay between each byte sent to the modem, in microseconds." }, + { "stopbits", "Number of stop bits for communication on the serial port. Either 1 or 2. The 1 in \"8n1\" for example." }, +}; + +NmcPropertyDesc setting_team[] = { + { "config", "The JSON configuration for the team network interface. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, +}; + +NmcPropertyDesc setting_team_port[] = { + { "config", "The JSON configuration for the team port. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, +}; + +NmcPropertyDesc setting_vlan[] = { + { "egress-priority-map", "For outgoing packets, a list of mappings from Linux SKB priorities to 802.1p priorities. The mapping is given in the format \"from:to\" where both \"from\" and \"to\" are unsigned integers, ie \"7:3\"." }, + { "flags", "One or more flags which control the behavior and features of the VLAN interface. Flags include NM_VLAN_FLAG_REORDER_HEADERS (0x1) (reordering of output packet headers), NM_VLAN_FLAG_GVRP (0x2) (use of the GVRP protocol), and NM_VLAN_FLAG_LOOSE_BINDING (0x4) (loose binding of the interface to its master device's operating state)." }, + { "id", "The VLAN identifier that the interface created by this connection should be assigned." }, + { "ingress-priority-map", "For incoming packets, a list of mappings from 802.1p priorities to Linux SKB priorities. The mapping is given in the format \"from:to\" where both \"from\" and \"to\" are unsigned integers, ie \"7:3\"." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "parent", "If given, specifies the parent interface name or parent connection UUID from which this VLAN interface should be created. If this property is not specified, the connection must contain an \"802-3-ethernet\" setting with a \"mac-address\" property." }, +}; + +NmcPropertyDesc setting_vpn[] = { + { "data", "Dictionary of key/value pairs of VPN plugin specific data. Both keys and values must be strings." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "persistent", "If the VPN service supports persistence, and this property is TRUE, the VPN will attempt to stay connected across link changes and outages, until explicitly disconnected." }, + { "secrets", "Dictionary of key/value pairs of VPN plugin specific secrets like passwords or private keys. Both keys and values must be strings." }, + { "service-type", "D-Bus service name of the VPN plugin that this setting uses to connect to its network. i.e. org.freedesktop.NetworkManager.vpnc for the vpnc plugin." }, + { "user-name", "If the VPN connection requires a user name for authentication, that name should be provided here. If the connection is available to more than one user, and the VPN requires each user to supply a different name, then leave this property empty. If this property is empty, NetworkManager will automatically supply the username of the user which requested the VPN connection." }, +}; + +NmcPropertyDesc setting_wimax[] = { + { "mac-address", "If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing)." }, + { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, + { "network-name", "Network Service Provider (NSP) name of the WiMAX network this connection should use." }, +}; + + +typedef struct { + const char *name; + NmcPropertyDesc *properties; + int n_properties; +} NmcSettingDesc; + +NmcSettingDesc all_settings[] = { + + { "802-11-olpc-mesh", setting_802_11_olpc_mesh, 4 }, + { "802-11-wireless", setting_802_11_wireless, 14 }, + { "802-11-wireless-security", setting_802_11_wireless_security, 18 }, + { "802-1x", setting_802_1x, 33 }, + { "802-3-ethernet", setting_802_3_ethernet, 12 }, + { "adsl", setting_adsl, 8 }, + { "bluetooth", setting_bluetooth, 3 }, + { "bond", setting_bond, 2 }, + { "bridge", setting_bridge, 8 }, + { "bridge-port", setting_bridge_port, 4 }, + { "cdma", setting_cdma, 5 }, + { "connection", setting_connection, 15 }, + { "dcb", setting_dcb, 16 }, + { "generic", setting_generic, 1 }, + { "gsm", setting_gsm, 10 }, + { "infiniband", setting_infiniband, 6 }, + { "ipv4", setting_ipv4, 15 }, + { "ipv6", setting_ipv6, 15 }, + { "ppp", setting_ppp, 19 }, + { "pppoe", setting_pppoe, 5 }, + { "serial", setting_serial, 6 }, + { "team", setting_team, 2 }, + { "team-port", setting_team_port, 2 }, + { "vlan", setting_vlan, 6 }, + { "vpn", setting_vpn, 6 }, + { "wimax", setting_wimax, 3 }, +}; + +static int +find_by_name (gconstpointer keyv, gconstpointer cmpv) +{ + const char *key = keyv; + struct { const char *name; gpointer data; } *cmp = (gpointer)cmpv; + + return strcmp (key, cmp->name); +} + +static const char * +nmc_setting_get_property_doc (NMSetting *setting, const char *prop) +{ + NmcSettingDesc *setting_desc; + NmcPropertyDesc *property_desc; + + setting_desc = bsearch (nm_setting_get_name (setting), + all_settings, G_N_ELEMENTS (all_settings), + sizeof (NmcSettingDesc), find_by_name); + if (!setting_desc) + return NULL; + property_desc = bsearch (prop, + setting_desc->properties, setting_desc->n_properties, + sizeof (NmcPropertyDesc), find_by_name); + if (!property_desc) + return NULL; + return property_desc->docs; +} + \ No newline at end of file diff --git a/clients/cli/settings-docs.xsl b/clients/cli/settings-docs.xsl new file mode 100644 index 00000000..996644bd --- /dev/null +++ b/clients/cli/settings-docs.xsl @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + + <xsl:output + method="text" + doctype-public="-//OASIS//DTD DocBook XML V4.3//EN" + doctype-system="http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" + /> + + <xsl:template match="nm-setting-docs">/* Generated file. Do not edit. */ + +typedef struct { + const char *name; + const char *docs; +} NmcPropertyDesc; +<xsl:apply-templates select="setting" mode="properties"><xsl:sort select="@name"/></xsl:apply-templates> + +typedef struct { + const char *name; + NmcPropertyDesc *properties; + int n_properties; +} NmcSettingDesc; + +NmcSettingDesc all_settings[] = { +<xsl:apply-templates select="setting" mode="settings"><xsl:sort select="@name"/></xsl:apply-templates> +}; + +static int +find_by_name (gconstpointer keyv, gconstpointer cmpv) +{ + const char *key = keyv; + struct { const char *name; gpointer data; } *cmp = (gpointer)cmpv; + + return strcmp (key, cmp->name); +} + +static const char * +nmc_setting_get_property_doc (NMSetting *setting, const char *prop) +{ + NmcSettingDesc *setting_desc; + NmcPropertyDesc *property_desc; + + setting_desc = bsearch (nm_setting_get_name (setting), + all_settings, G_N_ELEMENTS (all_settings), + sizeof (NmcSettingDesc), find_by_name); + if (!setting_desc) + return NULL; + property_desc = bsearch (prop, + setting_desc->properties, setting_desc->n_properties, + sizeof (NmcPropertyDesc), find_by_name); + if (!property_desc) + return NULL; + return property_desc->docs; +} + </xsl:template> + + <xsl:template match="setting" mode="properties"> +NmcPropertyDesc setting_<xsl:value-of select="translate(@name,'-','_')"/>[] = {<xsl:apply-templates select="property"><xsl:sort select="@name"/></xsl:apply-templates> +}; + </xsl:template> + + <xsl:template match="property"> + <xsl:variable name="docs"> + <xsl:call-template name="escape_quotes"> + <xsl:with-param name="string" select="@description"/> + </xsl:call-template> + </xsl:variable> + { "<xsl:value-of select="@name"/>", "<xsl:value-of select="$docs"/>" },</xsl:template> + + <xsl:template match="setting" mode="settings"> + { "<xsl:value-of select="@name"/>", setting_<xsl:value-of select="translate(@name,'-','_')"/>, <xsl:value-of select="count(./property)"/> },</xsl:template> + + <xsl:template name="escape_quotes"> + <xsl:param name="string" /> + <xsl:choose> + <xsl:when test="contains($string, '"')"> + <xsl:value-of select="substring-before($string, '"')" />\"<xsl:call-template name="escape_quotes"><xsl:with-param name="string" select="substring-after($string, '"')" /></xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$string" /> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + +</xsl:stylesheet> diff --git a/clients/cli/settings.c b/clients/cli/settings.c new file mode 100644 index 00000000..7d2a46c8 --- /dev/null +++ b/clients/cli/settings.c @@ -0,0 +1,7445 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + */ + +#include "config.h" + +#include <stdlib.h> +#include <arpa/inet.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "utils.h" +#include "common.h" +#include "settings.h" +#include "nm-glib-compat.h" + +/* Forward declarations */ +static char *wep_key_type_to_string (NMWepKeyType type); + +/* Helper macro to define fields */ +#define SETTING_FIELD(setting, width) { setting, N_(setting), width, NULL, FALSE, FALSE, 0 } + +/* Available fields for NM_SETTING_CONNECTION_SETTING_NAME */ +NmcOutputField nmc_fields_setting_connection[] = { + SETTING_FIELD ("name", 15), /* 0 */ + SETTING_FIELD (NM_SETTING_CONNECTION_ID, 25), /* 1 */ + SETTING_FIELD (NM_SETTING_CONNECTION_UUID, 38), /* 2 */ + SETTING_FIELD (NM_SETTING_CONNECTION_INTERFACE_NAME, 20), /* 3 */ + SETTING_FIELD (NM_SETTING_CONNECTION_TYPE, 17), /* 4 */ + SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT, 13), /* 5 */ + SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, 10), /* 6 */ + SETTING_FIELD (NM_SETTING_CONNECTION_TIMESTAMP, 10), /* 7 */ + SETTING_FIELD (NM_SETTING_CONNECTION_READ_ONLY, 10), /* 8 */ + SETTING_FIELD (NM_SETTING_CONNECTION_PERMISSIONS, 30), /* 9 */ + SETTING_FIELD (NM_SETTING_CONNECTION_ZONE, 10), /* 10 */ + SETTING_FIELD (NM_SETTING_CONNECTION_MASTER, 20), /* 11 */ + SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE, 20), /* 12 */ + SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES, 40), /* 13 */ + SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, 30), /* 14 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_CONNECTION_ALL "name"","\ + NM_SETTING_CONNECTION_ID","\ + NM_SETTING_CONNECTION_UUID","\ + NM_SETTING_CONNECTION_INTERFACE_NAME","\ + NM_SETTING_CONNECTION_TYPE","\ + NM_SETTING_CONNECTION_AUTOCONNECT","\ + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY","\ + NM_SETTING_CONNECTION_TIMESTAMP","\ + NM_SETTING_CONNECTION_READ_ONLY","\ + NM_SETTING_CONNECTION_PERMISSIONS","\ + NM_SETTING_CONNECTION_ZONE","\ + NM_SETTING_CONNECTION_MASTER","\ + NM_SETTING_CONNECTION_SLAVE_TYPE","\ + NM_SETTING_CONNECTION_SECONDARIES","\ + NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT +#define NMC_FIELDS_SETTING_CONNECTION_COMMON NMC_FIELDS_SETTING_CONNECTION_ALL + +/* Available fields for NM_SETTING_WIRED_SETTING_NAME */ +NmcOutputField nmc_fields_setting_wired[] = { + SETTING_FIELD ("name", 17), /* 0 */ + SETTING_FIELD (NM_SETTING_WIRED_PORT, 8), /* 1 */ + SETTING_FIELD (NM_SETTING_WIRED_SPEED, 10), /* 2 */ + SETTING_FIELD (NM_SETTING_WIRED_DUPLEX, 10), /* 3 */ + SETTING_FIELD (NM_SETTING_WIRED_AUTO_NEGOTIATE, 15), /* 4 */ + SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS, 19), /* 5 */ + SETTING_FIELD (NM_SETTING_WIRED_CLONED_MAC_ADDRESS, 19), /* 6 */ + SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, 39), /* 7 */ + SETTING_FIELD (NM_SETTING_WIRED_MTU, 6), /* 8 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_SUBCHANNELS, 20), /* 9 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_NETTYPE, 15), /* 10 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_OPTIONS, 20), /* 11 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_WIRED_ALL "name"","\ + NM_SETTING_WIRED_PORT","\ + NM_SETTING_WIRED_SPEED","\ + NM_SETTING_WIRED_DUPLEX","\ + NM_SETTING_WIRED_AUTO_NEGOTIATE","\ + NM_SETTING_WIRED_MAC_ADDRESS","\ + NM_SETTING_WIRED_CLONED_MAC_ADDRESS","\ + NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST","\ + NM_SETTING_WIRED_MTU","\ + NM_SETTING_WIRED_S390_SUBCHANNELS","\ + NM_SETTING_WIRED_S390_NETTYPE","\ + NM_SETTING_WIRED_S390_OPTIONS +#define NMC_FIELDS_SETTING_WIRED_COMMON NMC_FIELDS_SETTING_WIRED_ALL + +/* Available fields for NM_SETTING_802_1X_SETTING_NAME */ +NmcOutputField nmc_fields_setting_8021X[] = { + SETTING_FIELD ("name", 10), /* 0 */ + SETTING_FIELD (NM_SETTING_802_1X_EAP, 10), /* 1 */ + SETTING_FIELD (NM_SETTING_802_1X_IDENTITY, 15), /* 2 */ + SETTING_FIELD (NM_SETTING_802_1X_ANONYMOUS_IDENTITY, 15), /* 3 */ + SETTING_FIELD (NM_SETTING_802_1X_PAC_FILE, 15), /* 4 */ + SETTING_FIELD (NM_SETTING_802_1X_CA_CERT, 10), /* 5 */ + SETTING_FIELD (NM_SETTING_802_1X_CA_PATH, 10), /* 6 */ + SETTING_FIELD (NM_SETTING_802_1X_SUBJECT_MATCH, 10), /* 7 */ + SETTING_FIELD (NM_SETTING_802_1X_ALTSUBJECT_MATCHES, 10), /* 8 */ + SETTING_FIELD (NM_SETTING_802_1X_CLIENT_CERT, 10), /* 9 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE1_PEAPVER, 10), /* 10 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE1_PEAPLABEL, 10), /* 11 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, 10), /* 12 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE2_AUTH, 10), /* 13 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE2_AUTHEAP, 10), /* 14 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE2_CA_CERT, 20), /* 15 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE2_CA_PATH, 20), /* 16 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, 10), /* 17 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES, 10), /* 18 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE2_CLIENT_CERT, 20), /* 19 */ + SETTING_FIELD (NM_SETTING_802_1X_PASSWORD, 10), /* 20 */ + SETTING_FIELD (NM_SETTING_802_1X_PASSWORD_FLAGS, 20), /* 21 */ + SETTING_FIELD (NM_SETTING_802_1X_PASSWORD_RAW, 20), /* 22 */ + SETTING_FIELD (NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, 20), /* 23 */ + SETTING_FIELD (NM_SETTING_802_1X_PRIVATE_KEY, 15), /* 24 */ + SETTING_FIELD (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, 20), /* 25 */ + SETTING_FIELD (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, 20), /* 26 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, 20), /* 27 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, 20), /* 28 */ + SETTING_FIELD (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, 20), /* 29 */ + SETTING_FIELD (NM_SETTING_802_1X_PIN, 8), /* 30 */ + SETTING_FIELD (NM_SETTING_802_1X_PIN_FLAGS, 20), /* 31 */ + SETTING_FIELD (NM_SETTING_802_1X_SYSTEM_CA_CERTS, 17), /* 32 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_802_1X_ALL "name"","\ + NM_SETTING_802_1X_EAP","\ + NM_SETTING_802_1X_IDENTITY","\ + NM_SETTING_802_1X_ANONYMOUS_IDENTITY","\ + NM_SETTING_802_1X_PAC_FILE","\ + NM_SETTING_802_1X_CA_CERT","\ + NM_SETTING_802_1X_CA_PATH","\ + NM_SETTING_802_1X_SUBJECT_MATCH","\ + NM_SETTING_802_1X_ALTSUBJECT_MATCHES","\ + NM_SETTING_802_1X_CLIENT_CERT","\ + NM_SETTING_802_1X_PHASE1_PEAPVER","\ + NM_SETTING_802_1X_PHASE1_PEAPLABEL","\ + NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING","\ + NM_SETTING_802_1X_PHASE2_AUTH","\ + NM_SETTING_802_1X_PHASE2_AUTHEAP","\ + NM_SETTING_802_1X_PHASE2_CA_CERT","\ + NM_SETTING_802_1X_PHASE2_CA_PATH","\ + NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH","\ + NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES","\ + NM_SETTING_802_1X_PHASE2_CLIENT_CERT","\ + NM_SETTING_802_1X_PASSWORD","\ + NM_SETTING_802_1X_PASSWORD_FLAGS","\ + NM_SETTING_802_1X_PASSWORD_RAW","\ + NM_SETTING_802_1X_PASSWORD_RAW_FLAGS","\ + NM_SETTING_802_1X_PRIVATE_KEY","\ + NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD","\ + NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS","\ + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY","\ + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD","\ + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS","\ + NM_SETTING_802_1X_PIN","\ + NM_SETTING_802_1X_PIN_FLAGS","\ + NM_SETTING_802_1X_SYSTEM_CA_CERTS +#define NMC_FIELDS_SETTING_802_1X_COMMON NMC_FIELDS_SETTING_802_1X_ALL + +/* Available fields for NM_SETTING_WIRELESS_SETTING_NAME */ +NmcOutputField nmc_fields_setting_wireless[] = { + SETTING_FIELD ("name", 17), /* 0 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SSID, 34), /* 1 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MODE, 15), /* 2 */ + SETTING_FIELD (NM_SETTING_WIRELESS_BAND, 10), /* 3 */ + SETTING_FIELD (NM_SETTING_WIRELESS_CHANNEL, 10), /* 4 */ + SETTING_FIELD (NM_SETTING_WIRELESS_BSSID, 19), /* 5 */ + SETTING_FIELD (NM_SETTING_WIRELESS_RATE, 10), /* 6 */ + SETTING_FIELD (NM_SETTING_WIRELESS_TX_POWER, 10), /* 7 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS, 19), /* 8 */ + SETTING_FIELD (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, 19), /* 9 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, 39), /* 10 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MTU, 6), /* 11 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SEEN_BSSIDS, 35), /* 12 */ + SETTING_FIELD (NM_SETTING_WIRELESS_HIDDEN, 10), /* 13 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_WIRELESS_ALL "name"","\ + NM_SETTING_WIRELESS_SSID","\ + NM_SETTING_WIRELESS_MODE","\ + NM_SETTING_WIRELESS_BAND","\ + NM_SETTING_WIRELESS_CHANNEL","\ + NM_SETTING_WIRELESS_BSSID","\ + NM_SETTING_WIRELESS_RATE","\ + NM_SETTING_WIRELESS_TX_POWER","\ + NM_SETTING_WIRELESS_MAC_ADDRESS","\ + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS","\ + NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST","\ + NM_SETTING_WIRELESS_MTU","\ + NM_SETTING_WIRELESS_SEEN_BSSIDS","\ + NM_SETTING_WIRELESS_HIDDEN +#define NMC_FIELDS_SETTING_WIRELESS_COMMON NMC_FIELDS_SETTING_WIRELESS_ALL + +/* Available fields for NM_SETTING_WIRELESS_SECURITY_SETTING_NAME */ +NmcOutputField nmc_fields_setting_wireless_security[] = { + SETTING_FIELD ("name", 25), /* 0 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, 10), /* 1 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, 15), /* 2 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, 10), /* 3 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_PROTO, 10), /* 4 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_PAIRWISE, 10), /* 5 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_GROUP, 10), /* 6 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, 15), /* 7 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, 10), /* 8 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, 10), /* 9 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, 10), /* 10 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, 10), /* 11 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, 20), /* 12 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, 15), /* 13 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_PSK, 6), /* 14 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, 20), /* 15 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, 15), /* 16 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, 20), /* 17 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_WIRELESS_SECURITY_ALL "name"","\ + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT","\ + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX","\ + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG","\ + NM_SETTING_WIRELESS_SECURITY_PROTO","\ + NM_SETTING_WIRELESS_SECURITY_PAIRWISE","\ + NM_SETTING_WIRELESS_SECURITY_GROUP","\ + NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME","\ + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0","\ + NM_SETTING_WIRELESS_SECURITY_WEP_KEY1","\ + NM_SETTING_WIRELESS_SECURITY_WEP_KEY2","\ + NM_SETTING_WIRELESS_SECURITY_WEP_KEY3","\ + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS","\ + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE","\ + NM_SETTING_WIRELESS_SECURITY_PSK","\ + NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS","\ + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD","\ + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS +#define NMC_FIELDS_SETTING_WIRELESS_SECURITY_COMMON NMC_FIELDS_SETTING_WIRELESS_SECURITY_ALL + +/* Available fields for NM_SETTING_IP4_CONFIG_SETTING_NAME */ +NmcOutputField nmc_fields_setting_ip4_config[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_METHOD, 10), /* 1 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_DNS, 20), /* 2 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_DNS_SEARCH, 15), /* 3 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_ADDRESSES, 20), /* 4 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_GATEWAY, 20), /* 5 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_ROUTES, 20), /* 6 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_ROUTE_METRIC, 15), /* 7 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, 19), /* 8 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, 16), /* 9 */ + SETTING_FIELD (NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, 15), /* 10 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, 19), /* 11 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, 14), /* 12 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_NEVER_DEFAULT, 15), /* 13 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_MAY_FAIL, 12), /* 14 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_IP4_CONFIG_ALL "name"","\ + NM_SETTING_IP_CONFIG_METHOD","\ + NM_SETTING_IP_CONFIG_DNS","\ + NM_SETTING_IP_CONFIG_DNS_SEARCH","\ + NM_SETTING_IP_CONFIG_ADDRESSES","\ + NM_SETTING_IP_CONFIG_GATEWAY","\ + NM_SETTING_IP_CONFIG_ROUTES","\ + NM_SETTING_IP_CONFIG_ROUTE_METRIC","\ + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES","\ + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS","\ + NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID","\ + NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME","\ + NM_SETTING_IP_CONFIG_DHCP_HOSTNAME","\ + NM_SETTING_IP_CONFIG_NEVER_DEFAULT","\ + NM_SETTING_IP_CONFIG_MAY_FAIL +#define NMC_FIELDS_SETTING_IP4_CONFIG_COMMON NMC_FIELDS_SETTING_IP4_CONFIG_ALL + +/* Available fields for NM_SETTING_IP6_CONFIG_SETTING_NAME */ +NmcOutputField nmc_fields_setting_ip6_config[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_METHOD, 10), /* 1 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_DNS, 20), /* 2 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_DNS_SEARCH, 15), /* 3 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_ADDRESSES, 20), /* 4 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_GATEWAY, 20), /* 5 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_ROUTES, 20), /* 6 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_ROUTE_METRIC, 15), /* 7 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, 19), /* 8 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, 16), /* 9 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_NEVER_DEFAULT, 15), /* 10 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_MAY_FAIL, 12), /* 11 */ + SETTING_FIELD (NM_SETTING_IP6_CONFIG_IP6_PRIVACY, 15), /* 12 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, 19), /* 13 */ + SETTING_FIELD (NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, 14), /* 14 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_IP6_CONFIG_ALL "name"","\ + NM_SETTING_IP_CONFIG_METHOD","\ + NM_SETTING_IP_CONFIG_DNS","\ + NM_SETTING_IP_CONFIG_DNS_SEARCH","\ + NM_SETTING_IP_CONFIG_ADDRESSES","\ + NM_SETTING_IP_CONFIG_GATEWAY","\ + NM_SETTING_IP_CONFIG_ROUTES","\ + NM_SETTING_IP_CONFIG_ROUTE_METRIC","\ + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES","\ + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS","\ + NM_SETTING_IP_CONFIG_NEVER_DEFAULT","\ + NM_SETTING_IP_CONFIG_MAY_FAIL","\ + NM_SETTING_IP6_CONFIG_IP6_PRIVACY","\ + NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME","\ + NM_SETTING_IP_CONFIG_DHCP_HOSTNAME +#define NMC_FIELDS_SETTING_IP6_CONFIG_COMMON NMC_FIELDS_SETTING_IP4_CONFIG_ALL + +/* Available fields for NM_SETTING_SERIAL_SETTING_NAME */ +NmcOutputField nmc_fields_setting_serial[] = { + SETTING_FIELD ("name", 10), /* 0 */ + SETTING_FIELD (NM_SETTING_SERIAL_BAUD, 10), /* 1 */ + SETTING_FIELD (NM_SETTING_SERIAL_BITS, 10), /* 2 */ + SETTING_FIELD (NM_SETTING_SERIAL_PARITY, 10), /* 3 */ + SETTING_FIELD (NM_SETTING_SERIAL_STOPBITS, 10), /* 4 */ + SETTING_FIELD (NM_SETTING_SERIAL_SEND_DELAY, 12), /* 5 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_SERIAL_ALL "name"","\ + NM_SETTING_SERIAL_BAUD","\ + NM_SETTING_SERIAL_BITS","\ + NM_SETTING_SERIAL_PARITY","\ + NM_SETTING_SERIAL_STOPBITS","\ + NM_SETTING_SERIAL_SEND_DELAY +#define NMC_FIELDS_SETTING_SERIAL_COMMON NMC_FIELDS_SETTING_SERIAL_ALL + +/* Available fields for NM_SETTING_PPP_SETTING_NAME */ +NmcOutputField nmc_fields_setting_ppp[] = { + SETTING_FIELD ("name", 10), /* 0 */ + SETTING_FIELD (NM_SETTING_PPP_NOAUTH, 10), /* 1 */ + SETTING_FIELD (NM_SETTING_PPP_REFUSE_EAP, 10), /* 2 */ + SETTING_FIELD (NM_SETTING_PPP_REFUSE_PAP, 10), /* 3 */ + SETTING_FIELD (NM_SETTING_PPP_REFUSE_CHAP, 10), /* 4 */ + SETTING_FIELD (NM_SETTING_PPP_REFUSE_MSCHAP, 10), /* 5 */ + SETTING_FIELD (NM_SETTING_PPP_REFUSE_MSCHAPV2, 10), /* 6 */ + SETTING_FIELD (NM_SETTING_PPP_NOBSDCOMP, 10), /* 7 */ + SETTING_FIELD (NM_SETTING_PPP_NODEFLATE, 10), /* 8 */ + SETTING_FIELD (NM_SETTING_PPP_NO_VJ_COMP, 10), /* 9 */ + SETTING_FIELD (NM_SETTING_PPP_REQUIRE_MPPE, 10), /* 10 */ + SETTING_FIELD (NM_SETTING_PPP_REQUIRE_MPPE_128, 10), /* 11 */ + SETTING_FIELD (NM_SETTING_PPP_MPPE_STATEFUL, 10), /* 12 */ + SETTING_FIELD (NM_SETTING_PPP_CRTSCTS, 10), /* 13 */ + SETTING_FIELD (NM_SETTING_PPP_BAUD, 10), /* 14 */ + SETTING_FIELD (NM_SETTING_PPP_MRU, 10), /* 15 */ + SETTING_FIELD (NM_SETTING_PPP_MTU, 10), /* 16 */ + SETTING_FIELD (NM_SETTING_PPP_LCP_ECHO_FAILURE, 17), /* 17 */ + SETTING_FIELD (NM_SETTING_PPP_LCP_ECHO_INTERVAL, 18), /* 18 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_PPP_ALL "name"","\ + NM_SETTING_PPP_NOAUTH","\ + NM_SETTING_PPP_REFUSE_EAP","\ + NM_SETTING_PPP_REFUSE_PAP","\ + NM_SETTING_PPP_REFUSE_CHAP","\ + NM_SETTING_PPP_REFUSE_MSCHAP","\ + NM_SETTING_PPP_REFUSE_MSCHAPV2","\ + NM_SETTING_PPP_NOBSDCOMP","\ + NM_SETTING_PPP_NODEFLATE","\ + NM_SETTING_PPP_NO_VJ_COMP","\ + NM_SETTING_PPP_REQUIRE_MPPE","\ + NM_SETTING_PPP_REQUIRE_MPPE_128","\ + NM_SETTING_PPP_MPPE_STATEFUL","\ + NM_SETTING_PPP_CRTSCTS","\ + NM_SETTING_PPP_BAUD","\ + NM_SETTING_PPP_MRU","\ + NM_SETTING_PPP_MTU","\ + NM_SETTING_PPP_LCP_ECHO_FAILURE","\ + NM_SETTING_PPP_LCP_ECHO_INTERVAL +#define NMC_FIELDS_SETTING_PPP_COMMON NMC_FIELDS_SETTING_PPP_ALL + +/* Available fields for NM_SETTING_PPPOE_SETTING_NAME */ +NmcOutputField nmc_fields_setting_pppoe[] = { + SETTING_FIELD ("name", 10), /* 0 */ + SETTING_FIELD (NM_SETTING_PPPOE_SERVICE, 12), /* 1 */ + SETTING_FIELD (NM_SETTING_PPPOE_USERNAME, 15), /* 2 */ + SETTING_FIELD (NM_SETTING_PPPOE_PASSWORD, 15), /* 3 */ + SETTING_FIELD (NM_SETTING_PPPOE_PASSWORD_FLAGS, 20), /* 4 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_PPPOE_ALL "name"","\ + NM_SETTING_PPPOE_SERVICE","\ + NM_SETTING_PPPOE_USERNAME","\ + NM_SETTING_PPPOE_PASSWORD","\ + NM_SETTING_PPPOE_PASSWORD_FLAGS +#define NMC_FIELDS_SETTING_PPPOE_COMMON NMC_FIELDS_SETTING_PPPOE_ALL + +/* Available fields for NM_SETTING_ADSL_SETTING_NAME */ +NmcOutputField nmc_fields_setting_adsl[] = { + SETTING_FIELD ("name", 10), /* 0 */ + SETTING_FIELD (NM_SETTING_ADSL_USERNAME, 15), /* 1 */ + SETTING_FIELD (NM_SETTING_ADSL_PASSWORD, 15), /* 2 */ + SETTING_FIELD (NM_SETTING_ADSL_PASSWORD_FLAGS, 20), /* 3 */ + SETTING_FIELD (NM_SETTING_ADSL_PROTOCOL, 10), /* 4 */ + SETTING_FIELD (NM_SETTING_ADSL_ENCAPSULATION, 10), /* 5 */ + SETTING_FIELD (NM_SETTING_ADSL_VPI, 10), /* 6 */ + SETTING_FIELD (NM_SETTING_ADSL_VCI, 10), /* 7 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_ADSL_ALL "name"","\ + NM_SETTING_ADSL_USERNAME","\ + NM_SETTING_ADSL_PASSWORD","\ + NM_SETTING_ADSL_PASSWORD_FLAGS","\ + NM_SETTING_ADSL_PROTOCOL","\ + NM_SETTING_ADSL_ENCAPSULATION","\ + NM_SETTING_ADSL_VPI","\ + NM_SETTING_ADSL_VCI +#define NMC_FIELDS_SETTING_ADSL_COMMON NMC_FIELDS_SETTING_ADSL_ALL + +/* Available fields for NM_SETTING_GSM_SETTING_NAME */ +NmcOutputField nmc_fields_setting_gsm[] = { + SETTING_FIELD ("name", 10), /* 0 */ + SETTING_FIELD (NM_SETTING_GSM_NUMBER, 10), /* 1 */ + SETTING_FIELD (NM_SETTING_GSM_USERNAME, 15), /* 2 */ + SETTING_FIELD (NM_SETTING_GSM_PASSWORD, 15), /* 3 */ + SETTING_FIELD (NM_SETTING_GSM_PASSWORD_FLAGS, 20), /* 4 */ + SETTING_FIELD (NM_SETTING_GSM_APN, 25), /* 5 */ + SETTING_FIELD (NM_SETTING_GSM_NETWORK_ID, 12), /* 6 */ + SETTING_FIELD (NM_SETTING_GSM_PIN, 10), /* 7 */ + SETTING_FIELD (NM_SETTING_GSM_PIN_FLAGS, 20), /* 8 */ + SETTING_FIELD (NM_SETTING_GSM_HOME_ONLY, 10), /* 9 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_GSM_ALL "name"","\ + NM_SETTING_GSM_NUMBER","\ + NM_SETTING_GSM_USERNAME","\ + NM_SETTING_GSM_PASSWORD","\ + NM_SETTING_GSM_PASSWORD_FLAGS","\ + NM_SETTING_GSM_APN","\ + NM_SETTING_GSM_NETWORK_ID","\ + NM_SETTING_GSM_PIN","\ + NM_SETTING_GSM_PIN_FLAGS","\ + NM_SETTING_GSM_HOME_ONLY +#define NMC_FIELDS_SETTING_GSM_COMMON NMC_FIELDS_SETTING_GSM_ALL + +/* Available fields for NM_SETTING_CDMA_SETTING_NAME */ +NmcOutputField nmc_fields_setting_cdma[] = { + SETTING_FIELD ("name", 10), /* 0 */ + SETTING_FIELD (NM_SETTING_CDMA_NUMBER, 15), /* 1 */ + SETTING_FIELD (NM_SETTING_CDMA_USERNAME, 15), /* 2 */ + SETTING_FIELD (NM_SETTING_CDMA_PASSWORD, 15), /* 3 */ + SETTING_FIELD (NM_SETTING_CDMA_PASSWORD_FLAGS, 20), /* 4 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_CDMA_ALL "name"","\ + NM_SETTING_CDMA_NUMBER","\ + NM_SETTING_CDMA_USERNAME","\ + NM_SETTING_CDMA_PASSWORD","\ + NM_SETTING_CDMA_PASSWORD_FLAGS +#define NMC_FIELDS_SETTING_CDMA_COMMON NMC_FIELDS_SETTING_CDMA_ALL + +/* Available fields for NM_SETTING_BLUETOOTH_SETTING_NAME */ +NmcOutputField nmc_fields_setting_bluetooth[] = { + SETTING_FIELD ("name", 11), /* 0 */ + SETTING_FIELD (NM_SETTING_BLUETOOTH_BDADDR, 19), /* 1 */ + SETTING_FIELD (NM_SETTING_BLUETOOTH_TYPE, 10), /* 2 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_BLUETOOTH_ALL "name"","\ + NM_SETTING_BLUETOOTH_BDADDR","\ + NM_SETTING_BLUETOOTH_TYPE +#define NMC_FIELDS_SETTING_BLUETOOTH_COMMON NMC_FIELDS_SETTING_BLUETOOTH_ALL + +/* Available fields for NM_SETTING_OLPC_MESH_SETTING_NAME */ +NmcOutputField nmc_fields_setting_olpc_mesh[] = { + SETTING_FIELD ("name", 18), /* 0 */ + SETTING_FIELD (NM_SETTING_OLPC_MESH_SSID, 34), /* 1 */ + SETTING_FIELD (NM_SETTING_OLPC_MESH_CHANNEL, 12), /* 2 */ + SETTING_FIELD (NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, 17), /* 3 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_OLPC_MESH_ALL "name"","\ + NM_SETTING_OLPC_MESH_SSID","\ + NM_SETTING_OLPC_MESH_CHANNEL","\ + NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS +#define NMC_FIELDS_SETTING_OLPC_MESH_COMMON NMC_FIELDS_SETTING_OLPC_MESH_ALL + +/* Available fields for NM_SETTING_VPN_SETTING_NAME */ +NmcOutputField nmc_fields_setting_vpn[] = { + SETTING_FIELD ("name", 6), /* 0 */ + SETTING_FIELD (NM_SETTING_VPN_SERVICE_TYPE, 40), /* 1 */ + SETTING_FIELD (NM_SETTING_VPN_USER_NAME, 12), /* 2 */ + SETTING_FIELD (NM_SETTING_VPN_DATA, 30), /* 3 */ + SETTING_FIELD (NM_SETTING_VPN_SECRETS, 15), /* 4 */ + SETTING_FIELD (NM_SETTING_VPN_PERSISTENT, 15), /* 5 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_VPN_ALL "name"","\ + NM_SETTING_VPN_SERVICE_TYPE","\ + NM_SETTING_VPN_USER_NAME","\ + NM_SETTING_VPN_DATA","\ + NM_SETTING_VPN_SECRETS","\ + NM_SETTING_VPN_PERSISTENT +#define NMC_FIELDS_SETTING_VPN_COMMON NMC_FIELDS_SETTING_VPN_ALL + +/* Available fields for NM_SETTING_WIMAX_SETTING_NAME */ +NmcOutputField nmc_fields_setting_wimax[] = { + SETTING_FIELD ("name", 6), /* 0 */ + SETTING_FIELD (NM_SETTING_WIMAX_MAC_ADDRESS, 19), /* 1 */ + SETTING_FIELD (NM_SETTING_WIMAX_NETWORK_NAME, 40), /* 2 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_WIMAX_ALL "name"","\ + NM_SETTING_WIMAX_MAC_ADDRESS","\ + NM_SETTING_WIMAX_NETWORK_NAME +#define NMC_FIELDS_SETTING_WIMAX_COMMON NMC_FIELDS_SETTING_WIMAX_ALL + +/* Available fields for NM_SETTING_INFINIBAND_SETTING_NAME */ +NmcOutputField nmc_fields_setting_infiniband[] = { + SETTING_FIELD ("name", 12), /* 0 */ + SETTING_FIELD (NM_SETTING_INFINIBAND_MAC_ADDRESS, 61), /* 1 */ + SETTING_FIELD (NM_SETTING_INFINIBAND_MTU, 6), /* 2 */ + SETTING_FIELD (NM_SETTING_INFINIBAND_TRANSPORT_MODE, 12), /* 3 */ + SETTING_FIELD (NM_SETTING_INFINIBAND_P_KEY, 6), /* 4 */ + SETTING_FIELD (NM_SETTING_INFINIBAND_PARENT, 16), /* 5 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_INFINIBAND_ALL "name"","\ + NM_SETTING_INFINIBAND_MAC_ADDRESS","\ + NM_SETTING_INFINIBAND_MTU"," \ + NM_SETTING_INFINIBAND_TRANSPORT_MODE"," \ + NM_SETTING_INFINIBAND_P_KEY"," \ + NM_SETTING_INFINIBAND_PARENT +#define NMC_FIELDS_SETTING_INFINIBAND_COMMON NMC_FIELDS_SETTING_INFINIBAND_ALL \ + +/* Available fields for NM_SETTING_BOND_SETTING_NAME */ +NmcOutputField nmc_fields_setting_bond[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_BOND_OPTIONS, 30), /* 1 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_BOND_ALL "name"","\ + NM_SETTING_BOND_OPTIONS +#define NMC_FIELDS_SETTING_BOND_COMMON NMC_FIELDS_SETTING_BOND_ALL + +/* Available fields for NM_SETTING_VLAN_SETTING_NAME */ +NmcOutputField nmc_fields_setting_vlan[] = { + SETTING_FIELD ("name", 6), /* 0 */ + SETTING_FIELD (NM_SETTING_VLAN_PARENT, 8), /* 1 */ + SETTING_FIELD (NM_SETTING_VLAN_ID, 6), /* 2 */ + SETTING_FIELD (NM_SETTING_VLAN_FLAGS, 45), /* 3 */ + SETTING_FIELD (NM_SETTING_VLAN_INGRESS_PRIORITY_MAP, 22), /* 4 */ + SETTING_FIELD (NM_SETTING_VLAN_EGRESS_PRIORITY_MAP, 22), /* 5 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_VLAN_ALL "name"","\ + NM_SETTING_VLAN_PARENT","\ + NM_SETTING_VLAN_ID","\ + NM_SETTING_VLAN_FLAGS","\ + NM_SETTING_VLAN_INGRESS_PRIORITY_MAP","\ + NM_SETTING_VLAN_EGRESS_PRIORITY_MAP +#define NMC_FIELDS_SETTING_VLAN_COMMON NMC_FIELDS_SETTING_VLAN_ALL + +/* Available fields for NM_SETTING_BRIDGE_SETTING_NAME */ +NmcOutputField nmc_fields_setting_bridge[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_BRIDGE_MAC_ADDRESS, 19), /* 1 */ + SETTING_FIELD (NM_SETTING_BRIDGE_STP, 5), /* 2 */ + SETTING_FIELD (NM_SETTING_BRIDGE_PRIORITY, 6), /* 3 */ + SETTING_FIELD (NM_SETTING_BRIDGE_FORWARD_DELAY, 6), /* 4 */ + SETTING_FIELD (NM_SETTING_BRIDGE_HELLO_TIME, 6), /* 5 */ + SETTING_FIELD (NM_SETTING_BRIDGE_MAX_AGE, 6), /* 6 */ + SETTING_FIELD (NM_SETTING_BRIDGE_AGEING_TIME, 6), /* 7 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_BRIDGE_ALL "name"","\ + NM_SETTING_BRIDGE_MAC_ADDRESS","\ + NM_SETTING_BRIDGE_STP","\ + NM_SETTING_BRIDGE_PRIORITY","\ + NM_SETTING_BRIDGE_FORWARD_DELAY","\ + NM_SETTING_BRIDGE_HELLO_TIME","\ + NM_SETTING_BRIDGE_MAX_AGE","\ + NM_SETTING_BRIDGE_AGEING_TIME +#define NMC_FIELDS_SETTING_BRIDGE_COMMON NMC_FIELDS_SETTING_BRIDGE_ALL + +/* Available fields for NM_SETTING_BRIDGE_PORT_SETTING_NAME */ +NmcOutputField nmc_fields_setting_bridge_port[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_BRIDGE_PORT_PRIORITY, 10), /* 1 */ + SETTING_FIELD (NM_SETTING_BRIDGE_PORT_PATH_COST, 12), /* 2 */ + SETTING_FIELD (NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, 15), /* 3 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_BRIDGE_PORT_ALL "name"","\ + NM_SETTING_BRIDGE_PORT_PRIORITY","\ + NM_SETTING_BRIDGE_PORT_PATH_COST","\ + NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE +#define NMC_FIELDS_SETTING_BRIDGE_PORT_COMMON NMC_FIELDS_SETTING_BRIDGE_PORT_ALL + +/* Available fields for NM_SETTING_TEAM_SETTING_NAME */ +NmcOutputField nmc_fields_setting_team[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_TEAM_CONFIG, 30), /* 1 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_TEAM_ALL "name"","\ + NM_SETTING_TEAM_CONFIG +#define NMC_FIELDS_SETTING_TEAM_COMMON NMC_FIELDS_SETTING_TEAM_ALL + +/* Available fields for NM_SETTING_TEAM_PORT_SETTING_NAME */ +NmcOutputField nmc_fields_setting_team_port[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_TEAM_PORT_CONFIG, 30), /* 1 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_TEAM_PORT_ALL "name"","\ + NM_SETTING_TEAM_PORT_CONFIG +#define NMC_FIELDS_SETTING_TEAM_PORT_COMMON NMC_FIELDS_SETTING_TEAM_PORT_ALL + +/* Available fields for NM_SETTING_DCB_SETTING_NAME */ +NmcOutputField nmc_fields_setting_dcb[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_DCB_APP_FCOE_FLAGS, 5), /* 1 */ + SETTING_FIELD (NM_SETTING_DCB_APP_FCOE_PRIORITY, 5), /* 2 */ + SETTING_FIELD (NM_SETTING_DCB_APP_FCOE_MODE, 8), /* 3 */ + SETTING_FIELD (NM_SETTING_DCB_APP_ISCSI_FLAGS, 5), /* 4 */ + SETTING_FIELD (NM_SETTING_DCB_APP_ISCSI_PRIORITY, 5), /* 5 */ + SETTING_FIELD (NM_SETTING_DCB_APP_FIP_FLAGS, 5), /* 6 */ + SETTING_FIELD (NM_SETTING_DCB_APP_FIP_PRIORITY, 5), /* 7 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, 5), /* 8 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL, 10), /* 9 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, 5), /* 10 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_GROUP_ID, 10), /* 11 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH, 30), /* 12 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_BANDWIDTH, 30), /* 13 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH, 10), /* 14 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS, 30), /* 15 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_DCB_ALL "name"","\ + NM_SETTING_DCB_APP_FCOE_FLAGS","\ + NM_SETTING_DCB_APP_FCOE_PRIORITY","\ + NM_SETTING_DCB_APP_FCOE_MODE","\ + NM_SETTING_DCB_APP_ISCSI_FLAGS","\ + NM_SETTING_DCB_APP_ISCSI_PRIORITY","\ + NM_SETTING_DCB_APP_FIP_FLAGS","\ + NM_SETTING_DCB_APP_FIP_PRIORITY","\ + NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS","\ + NM_SETTING_DCB_PRIORITY_FLOW_CONTROL","\ + NM_SETTING_DCB_PRIORITY_GROUP_FLAGS","\ + NM_SETTING_DCB_PRIORITY_GROUP_ID","\ + NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH","\ + NM_SETTING_DCB_PRIORITY_BANDWIDTH","\ + NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH","\ + NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS +#define NMC_FIELDS_SETTING_DCB_COMMON NMC_FIELDS_SETTING_DCB_ALL + +/*----------------------------------------------------------------------------*/ + +static char * +wep_key_type_to_string (NMWepKeyType type) +{ + switch (type) { + case NM_WEP_KEY_TYPE_KEY: + return g_strdup_printf (_("%d (key)"), type); + case NM_WEP_KEY_TYPE_PASSPHRASE: + return g_strdup_printf (_("%d (passphrase)"), type); + case NM_WEP_KEY_TYPE_UNKNOWN: + default: + return g_strdup_printf (_("%d (unknown)"), type); + } +} + +static char * +bytes_to_string (GBytes *bytes) +{ + const guint8 *data; + gsize len; + GString *cert = NULL; + int i; + + if (!bytes) + return NULL; + data = g_bytes_get_data (bytes, &len); + + cert = g_string_new (NULL); + for (i = 0; i < len; i++) + g_string_append_printf (cert, "%02X", data[i]); + + return g_string_free (cert, FALSE); +} + +static char * +vlan_flags_to_string (guint32 flags) +{ + GString *flag_str; + + if (flags == 0) + return g_strdup (_("0 (NONE)")); + + flag_str = g_string_new (NULL); + g_string_printf (flag_str, "%d (", flags); + + if (flags & NM_VLAN_FLAG_REORDER_HEADERS) + g_string_append (flag_str, _("REORDER_HEADERS, ")); + if (flags & NM_VLAN_FLAG_GVRP) + g_string_append (flag_str, _("GVRP, ")); + if (flags & NM_VLAN_FLAG_LOOSE_BINDING) + g_string_append (flag_str, _("LOOSE_BINDING, ")); + + if (flag_str->str[flag_str->len-1] == '(') + g_string_append (flag_str, _("unknown")); + else + g_string_truncate (flag_str, flag_str->len-2); /* chop off trailing ', ' */ + + g_string_append_c (flag_str, ')'); + + return g_string_free (flag_str, FALSE); +} + +static char * +vlan_priorities_to_string (NMSettingVlan *s_vlan, NMVlanPriorityMap map) +{ + GString *priorities; + int i; + + priorities = g_string_new (NULL); + for (i = 0; i < nm_setting_vlan_get_num_priorities (s_vlan, map); i++) { + guint32 from, to; + + if (nm_setting_vlan_get_priority (s_vlan, map, i, &from, &to)) + g_string_append_printf (priorities, "%d:%d,", from, to); + } + if (priorities->len) + g_string_truncate (priorities, priorities->len-1); /* chop off trailing ',' */ + + return g_string_free (priorities, FALSE); +} + +static char * +ip6_privacy_to_string (NMSettingIP6ConfigPrivacy ip6_privacy) +{ + switch (ip6_privacy) { + case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: + return g_strdup_printf (_("%d (disabled)"), ip6_privacy); + case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: + return g_strdup_printf (_("%d (enabled, prefer public IP)"), ip6_privacy); + case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: + return g_strdup_printf (_("%d (enabled, prefer temporary IP)"), ip6_privacy); + default: + return g_strdup_printf (_("%d (unknown)"), ip6_privacy); + } +} + +static char * +secret_flags_to_string (guint32 flags) +{ + GString *flag_str; + + if (flags == 0) + return g_strdup (_("0 (none)")); + + flag_str = g_string_new (NULL); + g_string_printf (flag_str, "%d (", flags); + + if (flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED) + g_string_append (flag_str, _("agent-owned, ")); + if (flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) + g_string_append (flag_str, _("not saved, ")); + if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) + g_string_append (flag_str, _("not required, ")); + + if (flag_str->str[flag_str->len-1] == '(') + g_string_append (flag_str, _("unknown")); + else + g_string_truncate (flag_str, flag_str->len-2); /* chop off trailing ', ' */ + + g_string_append_c (flag_str, ')'); + + return g_string_free (flag_str, FALSE); +} + +static void +vpn_data_item (const char *key, const char *value, gpointer user_data) +{ + GString *ret_str = (GString *) user_data; + + if (ret_str->len != 0) + g_string_append (ret_str, ", "); + + g_string_append_printf (ret_str, "%s = %s", key, value); +} + + +/* === property get functions === */ +#define DEFINE_GETTER(func_name, property_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + char *s; \ + GValue val = G_VALUE_INIT; \ + g_value_init (&val, G_TYPE_STRING); \ + g_object_get_property (G_OBJECT (setting), property_name, &val); \ + s = g_value_dup_string (&val); \ + g_value_unset (&val); \ + return s; \ + } + +#define DEFINE_SECRET_FLAGS_GETTER(func_name, property_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + guint v; \ + GValue val = G_VALUE_INIT; \ + g_value_init (&val, G_TYPE_UINT); \ + g_object_get_property (G_OBJECT (setting), property_name, &val); \ + v = g_value_get_uint (&val); \ + g_value_unset (&val); \ + return secret_flags_to_string (v); \ + } + +/* --- NM_SETTING_802_1X_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_802_1X_get_eap, NM_SETTING_802_1X_EAP) +DEFINE_GETTER (nmc_property_802_1X_get_identity, NM_SETTING_802_1X_IDENTITY) +DEFINE_GETTER (nmc_property_802_1X_get_anonymous_identity, NM_SETTING_802_1X_ANONYMOUS_IDENTITY) +DEFINE_GETTER (nmc_property_802_1X_get_pac_file, NM_SETTING_802_1X_PAC_FILE) +DEFINE_GETTER (nmc_property_802_1X_get_ca_path, NM_SETTING_802_1X_CA_PATH) +DEFINE_GETTER (nmc_property_802_1X_get_subject_match, NM_SETTING_802_1X_SUBJECT_MATCH) +DEFINE_GETTER (nmc_property_802_1X_get_altsubject_matches, NM_SETTING_802_1X_ALTSUBJECT_MATCHES) +DEFINE_GETTER (nmc_property_802_1X_get_phase1_peapver, NM_SETTING_802_1X_PHASE1_PEAPVER) +DEFINE_GETTER (nmc_property_802_1X_get_phase1_peaplabel, NM_SETTING_802_1X_PHASE1_PEAPLABEL) +DEFINE_GETTER (nmc_property_802_1X_get_phase1_fast_provisioning, NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_auth, NM_SETTING_802_1X_PHASE2_AUTH) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_autheap, NM_SETTING_802_1X_PHASE2_AUTHEAP) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_ca_path, NM_SETTING_802_1X_PHASE2_CA_PATH) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_subject_match, NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_altsubject_matches, NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES) +DEFINE_GETTER (nmc_property_802_1X_get_password, NM_SETTING_802_1X_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_802_1X_get_password_flags, NM_SETTING_802_1X_PASSWORD_FLAGS) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_802_1X_get_password_raw_flags, NM_SETTING_802_1X_PASSWORD_RAW_FLAGS) +DEFINE_GETTER (nmc_property_802_1X_get_private_key_password, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_802_1X_get_private_key_password_flags, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_private_key_password, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_802_1X_get_phase2_private_key_password_flags, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS) +DEFINE_GETTER (nmc_property_802_1X_get_pin, NM_SETTING_802_1X_PIN) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_802_1X_get_pin_flags, NM_SETTING_802_1X_PIN_FLAGS) +DEFINE_GETTER (nmc_property_802_1X_get_system_ca_certs, NM_SETTING_802_1X_SYSTEM_CA_CERTS) + +static char * +nmc_property_802_1X_get_ca_cert (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *ca_cert_str = NULL; + + scheme = nm_setting_802_1x_get_ca_cert_scheme (s_8021X); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + ca_cert_str = bytes_to_string (nm_setting_802_1x_get_ca_cert_blob (s_8021X)); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + ca_cert_str = g_strdup (nm_setting_802_1x_get_ca_cert_path (s_8021X)); + + return ca_cert_str; +} + +static char * +nmc_property_802_1X_get_client_cert (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *client_cert_str = NULL; + + scheme = nm_setting_802_1x_get_client_cert_scheme (s_8021X); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + client_cert_str = bytes_to_string (nm_setting_802_1x_get_client_cert_blob (s_8021X)); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + client_cert_str = g_strdup (nm_setting_802_1x_get_client_cert_path (s_8021X)); + + return client_cert_str; +} + +static char * +nmc_property_802_1X_get_phase2_ca_cert (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *phase2_ca_cert_str = NULL; + + scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (s_8021X); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + phase2_ca_cert_str = bytes_to_string (nm_setting_802_1x_get_phase2_ca_cert_blob (s_8021X)); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + phase2_ca_cert_str = g_strdup (nm_setting_802_1x_get_phase2_ca_cert_path (s_8021X)); + + return phase2_ca_cert_str; +} + +static char * +nmc_property_802_1X_get_phase2_client_cert (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *phase2_client_cert_str = NULL; + + scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (s_8021X); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + phase2_client_cert_str = bytes_to_string (nm_setting_802_1x_get_phase2_client_cert_blob (s_8021X)); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + phase2_client_cert_str = g_strdup (nm_setting_802_1x_get_phase2_client_cert_path (s_8021X)); + + return phase2_client_cert_str; +} + +static char * +nmc_property_802_1X_get_password_raw (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + return bytes_to_string (nm_setting_802_1x_get_password_raw (s_8021X)); +} + +static char * +nmc_property_802_1X_get_private_key (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *private_key_str = NULL; + + scheme = nm_setting_802_1x_get_private_key_scheme (s_8021X); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + private_key_str = bytes_to_string (nm_setting_802_1x_get_private_key_blob (s_8021X)); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + private_key_str = g_strdup (nm_setting_802_1x_get_private_key_path (s_8021X)); + + return private_key_str; +} + +static char * +nmc_property_802_1X_get_phase2_private_key (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *phase2_private_key_str = NULL; + + scheme = nm_setting_802_1x_get_phase2_private_key_scheme (s_8021X); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + phase2_private_key_str = bytes_to_string (nm_setting_802_1x_get_phase2_private_key_blob (s_8021X)); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + phase2_private_key_str = g_strdup (nm_setting_802_1x_get_phase2_private_key_path (s_8021X)); + + return phase2_private_key_str; +} + +/* --- NM_SETTING_ADSL_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_adsl_get_username, NM_SETTING_ADSL_USERNAME) +DEFINE_GETTER (nmc_property_adsl_get_password, NM_SETTING_ADSL_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_adsl_get_password_flags, NM_SETTING_ADSL_PASSWORD_FLAGS) +DEFINE_GETTER (nmc_property_adsl_get_protocol, NM_SETTING_ADSL_PROTOCOL) +DEFINE_GETTER (nmc_property_adsl_get_encapsulation, NM_SETTING_ADSL_ENCAPSULATION) +DEFINE_GETTER (nmc_property_adsl_get_vpi, NM_SETTING_ADSL_VPI) +DEFINE_GETTER (nmc_property_adsl_get_vci, NM_SETTING_ADSL_VCI) + +/* --- NM_SETTING_BLUETOOTH_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_bluetooth_get_bdaddr, NM_SETTING_BLUETOOTH_BDADDR) +DEFINE_GETTER (nmc_property_bluetooth_get_type, NM_SETTING_BLUETOOTH_TYPE) + +static char * +nmc_property_bond_get_options (NMSetting *setting) +{ + NMSettingBond *s_bond = NM_SETTING_BOND (setting); + GString *bond_options_s; + int i; + + bond_options_s = g_string_new (NULL); + for (i = 0; i < nm_setting_bond_get_num_options (s_bond); i++) { + const char *key, *value; + + nm_setting_bond_get_option (s_bond, i, &key, &value); + g_string_append_printf (bond_options_s, "%s=%s,", key, value); + } + g_string_truncate (bond_options_s, bond_options_s->len-1); /* chop off trailing ',' */ + + return g_string_free (bond_options_s, FALSE); +} + +/* --- NM_SETTING_BRIDGE_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_bridge_get_mac_address, NM_SETTING_BRIDGE_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_bridge_get_stp, NM_SETTING_BRIDGE_STP) +DEFINE_GETTER (nmc_property_bridge_get_priority, NM_SETTING_BRIDGE_PRIORITY) +DEFINE_GETTER (nmc_property_bridge_get_forward_delay, NM_SETTING_BRIDGE_FORWARD_DELAY) +DEFINE_GETTER (nmc_property_bridge_get_hello_time, NM_SETTING_BRIDGE_HELLO_TIME) +DEFINE_GETTER (nmc_property_bridge_get_max_age, NM_SETTING_BRIDGE_MAX_AGE) +DEFINE_GETTER (nmc_property_bridge_get_ageing_time, NM_SETTING_BRIDGE_AGEING_TIME) + +/* --- NM_SETTING_BRIDGE_PORT_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_bridge_port_get_priority, NM_SETTING_BRIDGE_PORT_PRIORITY) +DEFINE_GETTER (nmc_property_bridge_port_get_path_cost, NM_SETTING_BRIDGE_PORT_PATH_COST) +DEFINE_GETTER (nmc_property_bridge_port_get_hairpin_mode, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE) + +/* --- NM_SETTING_TEAM_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_team_get_config, NM_SETTING_TEAM_CONFIG) + +/* --- NM_SETTING_TEAM_PORT_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_team_port_get_config, NM_SETTING_TEAM_PORT_CONFIG) + +/* --- NM_SETTING_CDMA_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_cdma_get_number, NM_SETTING_CDMA_NUMBER) +DEFINE_GETTER (nmc_property_cdma_get_username, NM_SETTING_CDMA_USERNAME) +DEFINE_GETTER (nmc_property_cdma_get_password, NM_SETTING_CDMA_PASSWORD) + +DEFINE_SECRET_FLAGS_GETTER (nmc_property_cdma_get_password_flags, NM_SETTING_CDMA_PASSWORD_FLAGS) + +/* --- NM_SETTING_CONNECTION_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_connection_get_id, NM_SETTING_CONNECTION_ID) +DEFINE_GETTER (nmc_property_connection_get_uuid, NM_SETTING_CONNECTION_UUID) +DEFINE_GETTER (nmc_property_connection_get_interface_name, NM_SETTING_CONNECTION_INTERFACE_NAME) +DEFINE_GETTER (nmc_property_connection_get_type, NM_SETTING_CONNECTION_TYPE) +DEFINE_GETTER (nmc_property_connection_get_autoconnect, NM_SETTING_CONNECTION_AUTOCONNECT) +DEFINE_GETTER (nmc_property_connection_get_autoconnect_priority, NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY) +DEFINE_GETTER (nmc_property_connection_get_timestamp, NM_SETTING_CONNECTION_TIMESTAMP) +DEFINE_GETTER (nmc_property_connection_get_read_only, NM_SETTING_CONNECTION_READ_ONLY) + +static char * +nmc_property_connection_get_permissions (NMSetting *setting) +{ + NMSettingConnection *s_con = NM_SETTING_CONNECTION (setting); + GString *perm = NULL; + const char *perm_item; + const char *perm_type; + int i; + + perm = g_string_new (NULL); + for (i = 0; i < nm_setting_connection_get_num_permissions (s_con); i++) { + if (nm_setting_connection_get_permission (s_con, i, &perm_type, &perm_item, NULL)) + g_string_append_printf (perm, "%s:%s,", perm_type, perm_item); + } + if (perm->len > 0) + g_string_truncate (perm, perm->len-1); /* remove trailing , */ + + return g_string_free (perm, FALSE); +} + +DEFINE_GETTER (nmc_property_connection_get_zone, NM_SETTING_CONNECTION_ZONE) +DEFINE_GETTER (nmc_property_connection_get_master, NM_SETTING_CONNECTION_MASTER) +DEFINE_GETTER (nmc_property_connection_get_slave_type, NM_SETTING_CONNECTION_SLAVE_TYPE) +DEFINE_GETTER (nmc_property_connection_get_secondaries, NM_SETTING_CONNECTION_SECONDARIES) +DEFINE_GETTER (nmc_property_connection_get_gateway_ping_timeout, NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT) + +/* --- NM_SETTING_DCB_SETTING_NAME property get functions --- */ +static char * +dcb_flags_to_string (NMSettingDcbFlags flags) +{ + GString *flag_str; + + if (flags == 0) + return g_strdup (_("0 (disabled)")); + + flag_str = g_string_new (NULL); + g_string_printf (flag_str, "%d (", flags); + + if (flags & NM_SETTING_DCB_FLAG_ENABLE) + g_string_append (flag_str, _("enabled, ")); + if (flags & NM_SETTING_DCB_FLAG_ADVERTISE) + g_string_append (flag_str, _("advertise, ")); + if (flags & NM_SETTING_DCB_FLAG_WILLING) + g_string_append (flag_str, _("willing, ")); + + if (flag_str->str[flag_str->len-1] == '(') + g_string_append (flag_str, _("unknown")); + else + g_string_truncate (flag_str, flag_str->len-2); /* chop off trailing ', ' */ + + g_string_append_c (flag_str, ')'); + + return g_string_free (flag_str, FALSE); +} + +#define DEFINE_DCB_FLAGS_GETTER(func_name, property_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + guint v; \ + GValue val = G_VALUE_INIT; \ + g_value_init (&val, G_TYPE_UINT); \ + g_object_get_property (G_OBJECT (setting), property_name, &val); \ + v = g_value_get_uint (&val); \ + g_value_unset (&val); \ + return dcb_flags_to_string (v); \ + } + +static char * +dcb_app_priority_to_string (gint priority) +{ + return (priority == -1) ? g_strdup (_("-1 (unset)")) : g_strdup_printf ("%d", priority); +} + +#define DEFINE_DCB_APP_PRIORITY_GETTER(func_name, property_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + int v; \ + GValue val = G_VALUE_INIT; \ + g_value_init (&val, G_TYPE_INT); \ + g_object_get_property (G_OBJECT (setting), property_name, &val); \ + v = g_value_get_int (&val); \ + g_value_unset (&val); \ + return dcb_app_priority_to_string (v); \ + } + +#define DEFINE_DCB_BOOL_GETTER(func_name, getter_func_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + NMSettingDcb *s_dcb = NM_SETTING_DCB (setting); \ + GString *str; \ + guint i; \ + \ + str = g_string_new (NULL); \ + for (i = 0; i < 8; i++) { \ + if (getter_func_name (s_dcb, i)) \ + g_string_append_c (str, '1'); \ + else \ + g_string_append_c (str, '0'); \ +\ + if (i < 7) \ + g_string_append_c (str, ','); \ + } \ +\ + return g_string_free (str, FALSE); \ + } + +#define DEFINE_DCB_UINT_GETTER(func_name, getter_func_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + NMSettingDcb *s_dcb = NM_SETTING_DCB (setting); \ + GString *str; \ + guint i; \ + \ + str = g_string_new (NULL); \ + for (i = 0; i < 8; i++) { \ + g_string_append_printf (str, "%u", getter_func_name (s_dcb, i)); \ + if (i < 7) \ + g_string_append_c (str, ','); \ + } \ +\ + return g_string_free (str, FALSE); \ + } + +DEFINE_DCB_FLAGS_GETTER (nmc_property_dcb_get_app_fcoe_flags, NM_SETTING_DCB_APP_FCOE_FLAGS) +DEFINE_DCB_APP_PRIORITY_GETTER (nmc_property_dcb_get_app_fcoe_priority, NM_SETTING_DCB_APP_FCOE_PRIORITY) +DEFINE_GETTER (nmc_property_dcb_get_app_fcoe_mode, NM_SETTING_DCB_APP_FCOE_MODE) +DEFINE_DCB_FLAGS_GETTER (nmc_property_dcb_get_app_iscsi_flags, NM_SETTING_DCB_APP_ISCSI_FLAGS) +DEFINE_DCB_APP_PRIORITY_GETTER (nmc_property_dcb_get_app_iscsi_priority, NM_SETTING_DCB_APP_ISCSI_PRIORITY) +DEFINE_DCB_FLAGS_GETTER (nmc_property_dcb_get_app_fip_flags, NM_SETTING_DCB_APP_FIP_FLAGS) +DEFINE_DCB_APP_PRIORITY_GETTER (nmc_property_dcb_get_app_fip_priority, NM_SETTING_DCB_APP_FIP_PRIORITY) + +DEFINE_DCB_FLAGS_GETTER (nmc_property_dcb_get_pfc_flags, NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS) +DEFINE_DCB_BOOL_GETTER (nmc_property_dcb_get_pfc, nm_setting_dcb_get_priority_flow_control) + +DEFINE_DCB_FLAGS_GETTER (nmc_property_dcb_get_pg_flags, NM_SETTING_DCB_PRIORITY_GROUP_FLAGS) +DEFINE_DCB_UINT_GETTER (nmc_property_dcb_get_pg_group_id, nm_setting_dcb_get_priority_group_id) +DEFINE_DCB_UINT_GETTER (nmc_property_dcb_get_pg_group_bandwidth, nm_setting_dcb_get_priority_group_bandwidth) +DEFINE_DCB_UINT_GETTER (nmc_property_dcb_get_pg_bandwidth, nm_setting_dcb_get_priority_bandwidth) +DEFINE_DCB_BOOL_GETTER (nmc_property_dcb_get_pg_strict, nm_setting_dcb_get_priority_strict_bandwidth) +DEFINE_DCB_UINT_GETTER (nmc_property_dcb_get_pg_traffic_class, nm_setting_dcb_get_priority_traffic_class) + +/* --- NM_SETTING_GSM_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_gsm_get_number, NM_SETTING_GSM_NUMBER) +DEFINE_GETTER (nmc_property_gsm_get_username, NM_SETTING_GSM_USERNAME) +DEFINE_GETTER (nmc_property_gsm_get_password, NM_SETTING_GSM_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_gsm_get_password_flags, NM_SETTING_GSM_PASSWORD_FLAGS) +DEFINE_GETTER (nmc_property_gsm_get_apn, NM_SETTING_GSM_APN) +DEFINE_GETTER (nmc_property_gsm_get_network_id, NM_SETTING_GSM_NETWORK_ID) +DEFINE_GETTER (nmc_property_gsm_get_pin, NM_SETTING_GSM_PIN) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_gsm_get_pin_flags, NM_SETTING_GSM_PIN_FLAGS) +DEFINE_GETTER (nmc_property_gsm_get_home_only, NM_SETTING_GSM_HOME_ONLY) + +/* --- NM_SETTING_INFINIBAND_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_ib_get_mac_address, NM_SETTING_INFINIBAND_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_ib_get_transport_mode, NM_SETTING_INFINIBAND_TRANSPORT_MODE) + +static char * +nmc_property_ib_get_mtu (NMSetting *setting) +{ + NMSettingInfiniband *s_infiniband = NM_SETTING_INFINIBAND (setting); + int mtu; + + mtu = nm_setting_infiniband_get_mtu (s_infiniband); + if (mtu == 0) + return g_strdup (_("auto")); + else + return g_strdup_printf ("%d", mtu); +} + +static char * +nmc_property_ib_get_p_key (NMSetting *setting) +{ + NMSettingInfiniband *s_infiniband = NM_SETTING_INFINIBAND (setting); + int p_key; + + p_key = nm_setting_infiniband_get_p_key (s_infiniband); + if (p_key == -1) + return g_strdup (_("default")); + else + return g_strdup_printf ("0x%04x", p_key); +} + +DEFINE_GETTER (nmc_property_ib_get_parent, NM_SETTING_INFINIBAND_PARENT) + +/* --- NM_SETTING_IP4_CONFIG_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_ipv4_get_method, NM_SETTING_IP_CONFIG_METHOD) +DEFINE_GETTER (nmc_property_ipv4_get_dns, NM_SETTING_IP_CONFIG_DNS) +DEFINE_GETTER (nmc_property_ipv4_get_dns_search, NM_SETTING_IP_CONFIG_DNS_SEARCH) + +static char * +nmc_property_ip_get_addresses (NMSetting *setting) +{ + NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting); + GString *printable; + guint32 num_addresses, i; + NMIPAddress *addr; + + printable = g_string_new (NULL); + + num_addresses = nm_setting_ip_config_get_num_addresses (s_ip); + for (i = 0; i < num_addresses; i++) { + addr = nm_setting_ip_config_get_address (s_ip, i); + + if (printable->len > 0) + g_string_append (printable, ", "); + + g_string_append_printf (printable, "%s/%u", + nm_ip_address_get_address (addr), + nm_ip_address_get_prefix (addr)); + } + + return g_string_free (printable, FALSE); +} + +static char * +nmc_property_ipv4_get_routes (NMSetting *setting) +{ + NMSettingIPConfig *s_ip4 = NM_SETTING_IP_CONFIG (setting); + GString *printable; + guint32 num_routes, i; + NMIPRoute *route; + + printable = g_string_new (NULL); + + num_routes = nm_setting_ip_config_get_num_routes (s_ip4); + for (i = 0; i < num_routes; i++) { + route = nm_setting_ip_config_get_route (s_ip4, i); + + if (printable->len > 0) + g_string_append (printable, "; "); + + g_string_append (printable, "{ "); + + g_string_append_printf (printable, "ip = %s/%u", + nm_ip_route_get_dest (route), + nm_ip_route_get_prefix (route)); + + if (nm_ip_route_get_next_hop (route)) { + g_string_append_printf (printable, ", nh = %s", + nm_ip_route_get_next_hop (route)); + } + + if (nm_ip_route_get_metric (route) != -1) + g_string_append_printf (printable, ", mt = %u", (guint32) nm_ip_route_get_metric (route)); + + g_string_append (printable, " }"); + } + + return g_string_free (printable, FALSE); +} + +DEFINE_GETTER (nmc_property_ipv4_get_gateway, NM_SETTING_IP_CONFIG_GATEWAY) +DEFINE_GETTER (nmc_property_ipv4_get_route_metric, NM_SETTING_IP_CONFIG_ROUTE_METRIC) +DEFINE_GETTER (nmc_property_ipv4_get_ignore_auto_routes, NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES) +DEFINE_GETTER (nmc_property_ipv4_get_ignore_auto_dns, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS) +DEFINE_GETTER (nmc_property_ipv4_get_dhcp_client_id, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID) +DEFINE_GETTER (nmc_property_ipv4_get_dhcp_send_hostname, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME) +DEFINE_GETTER (nmc_property_ipv4_get_dhcp_hostname, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME) +DEFINE_GETTER (nmc_property_ipv4_get_never_default, NM_SETTING_IP_CONFIG_NEVER_DEFAULT) +DEFINE_GETTER (nmc_property_ipv4_get_may_fail, NM_SETTING_IP_CONFIG_MAY_FAIL) + +/* --- NM_SETTING_IP6_CONFIG_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_ipv6_get_method, NM_SETTING_IP_CONFIG_METHOD) +DEFINE_GETTER (nmc_property_ipv6_get_dns, NM_SETTING_IP_CONFIG_DNS) +DEFINE_GETTER (nmc_property_ipv6_get_dns_search, NM_SETTING_IP_CONFIG_DNS_SEARCH) + +static char * +nmc_property_ipv6_get_routes (NMSetting *setting) +{ + NMSettingIPConfig *s_ip6 = NM_SETTING_IP_CONFIG (setting); + GString *printable; + guint32 num_routes, i; + NMIPRoute *route; + + printable = g_string_new (NULL); + + num_routes = nm_setting_ip_config_get_num_routes (s_ip6); + for (i = 0; i < num_routes; i++) { + route = nm_setting_ip_config_get_route (s_ip6, i); + + if (printable->len > 0) + g_string_append (printable, "; "); + + g_string_append (printable, "{ "); + + g_string_append_printf (printable, "ip = %s/%u", + nm_ip_route_get_dest (route), + nm_ip_route_get_prefix (route)); + + if (nm_ip_route_get_next_hop (route)) { + g_string_append_printf (printable, ", nh = %s", + nm_ip_route_get_next_hop (route)); + } + + if (nm_ip_route_get_metric (route) != -1) + g_string_append_printf (printable, ", mt = %u", (guint32) nm_ip_route_get_metric (route)); + + g_string_append (printable, " }"); + } + + return g_string_free (printable, FALSE); +} + +DEFINE_GETTER (nmc_property_ipv6_get_gateway, NM_SETTING_IP_CONFIG_GATEWAY) +DEFINE_GETTER (nmc_property_ipv6_get_route_metric, NM_SETTING_IP_CONFIG_ROUTE_METRIC) +DEFINE_GETTER (nmc_property_ipv6_get_ignore_auto_routes, NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES) +DEFINE_GETTER (nmc_property_ipv6_get_ignore_auto_dns, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS) +DEFINE_GETTER (nmc_property_ipv6_get_never_default, NM_SETTING_IP_CONFIG_NEVER_DEFAULT) +DEFINE_GETTER (nmc_property_ipv6_get_may_fail, NM_SETTING_IP_CONFIG_MAY_FAIL) +DEFINE_GETTER (nmc_property_ipv6_get_dhcp_send_hostname, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME) +DEFINE_GETTER (nmc_property_ipv6_get_dhcp_hostname, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME) + +static char * +nmc_property_ipv6_get_ip6_privacy (NMSetting *setting) +{ + NMSettingIP6Config *s_ip6 = NM_SETTING_IP6_CONFIG (setting); + return ip6_privacy_to_string (nm_setting_ip6_config_get_ip6_privacy (s_ip6)); +} + +/* --- NM_SETTING_OLPC_MESH_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_olpc_get_channel, NM_SETTING_OLPC_MESH_CHANNEL) +DEFINE_GETTER (nmc_property_olpc_get_anycast_address, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS) + +static char * +nmc_property_olpc_get_ssid (NMSetting *setting) +{ + NMSettingOlpcMesh *s_olpc_mesh = NM_SETTING_OLPC_MESH (setting); + GBytes *ssid; + char *ssid_str = NULL; + + ssid = nm_setting_olpc_mesh_get_ssid (s_olpc_mesh); + if (ssid) { + ssid_str = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + } + + return ssid_str; +} + +/* --- NM_SETTING_PPP_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_ppp_get_noauth, NM_SETTING_PPP_NOAUTH) +DEFINE_GETTER (nmc_property_ppp_get_refuse_eap, NM_SETTING_PPP_REFUSE_EAP) +DEFINE_GETTER (nmc_property_ppp_get_refuse_pap, NM_SETTING_PPP_REFUSE_PAP) +DEFINE_GETTER (nmc_property_ppp_get_refuse_chap, NM_SETTING_PPP_REFUSE_CHAP) +DEFINE_GETTER (nmc_property_ppp_get_refuse_mschap, NM_SETTING_PPP_REFUSE_MSCHAP) +DEFINE_GETTER (nmc_property_ppp_get_refuse_mschapv2, NM_SETTING_PPP_REFUSE_MSCHAPV2) +DEFINE_GETTER (nmc_property_ppp_get_nobsdcomp, NM_SETTING_PPP_NOBSDCOMP) +DEFINE_GETTER (nmc_property_ppp_get_nodeflate, NM_SETTING_PPP_NODEFLATE) +DEFINE_GETTER (nmc_property_ppp_get_no_vj_comp, NM_SETTING_PPP_NO_VJ_COMP) +DEFINE_GETTER (nmc_property_ppp_get_require_mppe, NM_SETTING_PPP_REQUIRE_MPPE) +DEFINE_GETTER (nmc_property_ppp_get_require_mppe_128, NM_SETTING_PPP_REQUIRE_MPPE_128) +DEFINE_GETTER (nmc_property_ppp_get_mppe_stateful, NM_SETTING_PPP_MPPE_STATEFUL) +DEFINE_GETTER (nmc_property_ppp_get_crtscts, NM_SETTING_PPP_CRTSCTS) +DEFINE_GETTER (nmc_property_ppp_get_baud, NM_SETTING_PPP_BAUD) +DEFINE_GETTER (nmc_property_ppp_get_mru, NM_SETTING_PPP_MRU) +DEFINE_GETTER (nmc_property_ppp_get_mtu, NM_SETTING_PPP_MTU) +DEFINE_GETTER (nmc_property_ppp_get_lcp_echo_failure, NM_SETTING_PPP_LCP_ECHO_FAILURE) +DEFINE_GETTER (nmc_property_ppp_get_lcp_echo_interval, NM_SETTING_PPP_LCP_ECHO_INTERVAL) + +/* --- NM_SETTING_PPPOE_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_pppoe_get_service, NM_SETTING_PPPOE_SERVICE) +DEFINE_GETTER (nmc_property_pppoe_get_username, NM_SETTING_PPPOE_USERNAME) +DEFINE_GETTER (nmc_property_pppoe_get_password, NM_SETTING_PPPOE_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_pppoe_get_password_flags, NM_SETTING_PPPOE_PASSWORD_FLAGS) + +/* --- NM_SETTING_SERIAL_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_serial_get_baud, NM_SETTING_SERIAL_BAUD) +DEFINE_GETTER (nmc_property_serial_get_bits, NM_SETTING_SERIAL_BITS) +DEFINE_GETTER (nmc_property_serial_get_stopbits, NM_SETTING_SERIAL_STOPBITS) +DEFINE_GETTER (nmc_property_serial_get_send_delay, NM_SETTING_SERIAL_SEND_DELAY) + +/* --- NM_SETTING_VLAN_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_vlan_get_parent, NM_SETTING_VLAN_PARENT) +DEFINE_GETTER (nmc_property_vlan_get_id, NM_SETTING_VLAN_ID) + + +static char * +nmc_property_vlan_get_flags (NMSetting *setting) +{ + NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting); + return vlan_flags_to_string (nm_setting_vlan_get_flags (s_vlan)); +} + +static char * +nmc_property_vlan_get_ingress_priority_map (NMSetting *setting) +{ + NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting); + return vlan_priorities_to_string (s_vlan, NM_VLAN_INGRESS_MAP); +} + +static char * +nmc_property_vlan_get_egress_priority_map (NMSetting *setting) +{ + NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting); + return vlan_priorities_to_string (s_vlan, NM_VLAN_EGRESS_MAP); +} + +/* --- NM_SETTING_VPN_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_vpn_get_service_type, NM_SETTING_VPN_SERVICE_TYPE) +DEFINE_GETTER (nmc_property_vpn_get_user_name, NM_SETTING_VPN_USER_NAME) + +static char * +nmc_property_vpn_get_data (NMSetting *setting) +{ + NMSettingVpn *s_vpn = NM_SETTING_VPN (setting); + GString *data_item_str; + + data_item_str = g_string_new (NULL); + nm_setting_vpn_foreach_data_item (s_vpn, &vpn_data_item, data_item_str); + + return g_string_free (data_item_str, FALSE); +} + +static char * +nmc_property_vpn_get_secrets (NMSetting *setting) +{ + NMSettingVpn *s_vpn = NM_SETTING_VPN (setting); + GString *secret_str; + + secret_str = g_string_new (NULL); + nm_setting_vpn_foreach_secret (s_vpn, &vpn_data_item, secret_str); + + return g_string_free (secret_str, FALSE); +} + +DEFINE_GETTER (nmc_property_vpn_get_persistent, NM_SETTING_VPN_PERSISTENT) + +/* --- NM_SETTING_WIMAX_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_wimax_get_network_name, NM_SETTING_WIMAX_NETWORK_NAME) +DEFINE_GETTER (nmc_property_wimax_get_mac_address, NM_SETTING_WIMAX_MAC_ADDRESS) + +/* --- NM_SETTING_WIRED_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_wired_get_port, NM_SETTING_WIRED_PORT) +DEFINE_GETTER (nmc_property_wired_get_speed, NM_SETTING_WIRED_SPEED) +DEFINE_GETTER (nmc_property_wired_get_duplex, NM_SETTING_WIRED_DUPLEX) +DEFINE_GETTER (nmc_property_wired_get_auto_negotiate, NM_SETTING_WIRED_AUTO_NEGOTIATE) +DEFINE_GETTER (nmc_property_wired_get_mac_address, NM_SETTING_WIRED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wired_get_cloned_mac_address, NM_SETTING_WIRED_CLONED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wired_get_mac_address_blacklist, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST) +DEFINE_GETTER (nmc_property_wired_get_s390_subchannels, NM_SETTING_WIRED_S390_SUBCHANNELS) +DEFINE_GETTER (nmc_property_wired_get_s390_nettype, NM_SETTING_WIRED_S390_NETTYPE) +DEFINE_GETTER (nmc_property_wired_get_s390_options, NM_SETTING_WIRED_S390_OPTIONS) + +static char * +nmc_property_wired_get_mtu (NMSetting *setting) +{ + NMSettingWired *s_wired = NM_SETTING_WIRED (setting); + int mtu; + + mtu = nm_setting_wired_get_mtu (s_wired); + if (mtu == 0) + return g_strdup (_("auto")); + else + return g_strdup_printf ("%d", mtu); +} + +/* --- NM_SETTING_WIRELESS_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_wireless_get_mode, NM_SETTING_WIRELESS_MODE) +DEFINE_GETTER (nmc_property_wireless_get_band, NM_SETTING_WIRELESS_BAND) +DEFINE_GETTER (nmc_property_wireless_get_channel, NM_SETTING_WIRELESS_CHANNEL) +DEFINE_GETTER (nmc_property_wireless_get_bssid, NM_SETTING_WIRELESS_BSSID) +DEFINE_GETTER (nmc_property_wireless_get_rate, NM_SETTING_WIRELESS_RATE) +DEFINE_GETTER (nmc_property_wireless_get_tx_power, NM_SETTING_WIRELESS_TX_POWER) +DEFINE_GETTER (nmc_property_wireless_get_mac_address, NM_SETTING_WIRELESS_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wireless_get_cloned_mac_address, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wireless_get_mac_address_blacklist, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST) +DEFINE_GETTER (nmc_property_wireless_get_seen_bssids, NM_SETTING_WIRELESS_SEEN_BSSIDS) +DEFINE_GETTER (nmc_property_wireless_get_hidden, NM_SETTING_WIRELESS_HIDDEN) + +static char * +nmc_property_wireless_get_ssid (NMSetting *setting) +{ + NMSettingWireless *s_wireless = NM_SETTING_WIRELESS (setting); + GBytes *ssid; + char *ssid_str = NULL; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (ssid) { + ssid_str = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + } + + return ssid_str; +} + +static char * +nmc_property_wireless_get_mtu (NMSetting *setting) +{ + NMSettingWireless *s_wireless = NM_SETTING_WIRELESS (setting); + int mtu; + + mtu = nm_setting_wireless_get_mtu (s_wireless); + if (mtu == 0) + return g_strdup (_("auto")); + else + return g_strdup_printf ("%d", mtu); +} + +/* --- NM_SETTING_WIRELESS_SECURITY_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_wifi_sec_get_key_mgmt, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT) +DEFINE_GETTER (nmc_property_wifi_sec_get_wep_tx_keyidx, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX) +DEFINE_GETTER (nmc_property_wifi_sec_get_auth_alg, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG) +DEFINE_GETTER (nmc_property_wifi_sec_get_proto, NM_SETTING_WIRELESS_SECURITY_PROTO) +DEFINE_GETTER (nmc_property_wifi_sec_get_pairwise, NM_SETTING_WIRELESS_SECURITY_PAIRWISE) +DEFINE_GETTER (nmc_property_wifi_sec_get_group, NM_SETTING_WIRELESS_SECURITY_GROUP) +DEFINE_GETTER (nmc_property_wifi_sec_get_leap_username, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_wifi_sec_get_wep_key_flags, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS) +DEFINE_GETTER (nmc_property_wifi_sec_get_psk, NM_SETTING_WIRELESS_SECURITY_PSK) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_wifi_sec_get_psk_flags, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS) +DEFINE_GETTER (nmc_property_wifi_sec_get_leap_password, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_wifi_sec_get_leap_password_flags, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS) + +static char * +nmc_property_wifi_sec_get_wep_key0 (NMSetting *setting) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + return g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 0)); +} + +static char * +nmc_property_wifi_sec_get_wep_key1 (NMSetting *setting) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + return g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)); +} + +static char * +nmc_property_wifi_sec_get_wep_key2 (NMSetting *setting) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + return g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2)); +} + +static char * +nmc_property_wifi_sec_get_wep_key3 (NMSetting *setting) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + return g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3)); +} + +static char * +nmc_property_wifi_sec_get_wep_key_type (NMSetting *setting) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + return wep_key_type_to_string (nm_setting_wireless_security_get_wep_key_type (s_wireless_sec)); +} + +/*----------------------------------------------------------------------------*/ + +static void +nmc_value_transform_bool_string (const GValue *src_value, + GValue *dest_value) +{ + dest_value->data[0].v_pointer = g_strdup (src_value->data[0].v_int ? "yes" : "no"); +} + +static void +nmc_value_transform_char_string (const GValue *src_value, + GValue *dest_value) +{ + dest_value->data[0].v_pointer = g_strdup_printf ("%c", src_value->data[0].v_uint); +} + +static void __attribute__((constructor)) +register_nmcli_value_transforms (void) +{ + g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_STRING, nmc_value_transform_bool_string); + g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_STRING, nmc_value_transform_char_string); +} + +/*----------------------------------------------------------------------------*/ + +/* Main hash table storing function pointer for manipulating properties */ +static GHashTable *nmc_properties = NULL; +typedef char * (*NmcPropertyGetFunc) (NMSetting *); +typedef gboolean (*NmcPropertySetFunc) (NMSetting *, const char *, const char *, GError **); +typedef gboolean (*NmcPropertyRemoveFunc) (NMSetting *, const char *, const char *, guint32, GError **); +typedef const char * (*NmcPropertyDescribeFunc) (NMSetting *, const char *); +typedef const char * (*NmcPropertyValuesFunc) (NMSetting *, const char *); +typedef char * (*NmcPropertyOut2InFunc) (const char *); + +typedef struct { + NmcPropertyGetFunc get_func; /* func getting property values */ + NmcPropertySetFunc set_func; /* func adding/setting property values */ + NmcPropertyRemoveFunc remove_func; /* func removing items from container options */ + NmcPropertyDescribeFunc describe_func; /* func returning property description */ + NmcPropertyValuesFunc values_func; /* func returning allowed property values */ + NmcPropertyOut2InFunc out2in_func; /* func converting property values from output to input format */ +} NmcPropertyFuncs; + +/* + * We need NmCli in some _set_property functions, and they aren't passed NmCli. + * So use the global variable. + */ +/* Global variable defined in nmcli.c */ +extern NmCli nm_cli; + +NMSetting * +nmc_setting_new_for_name (const char *name) +{ + GType stype; + NMSetting *setting = NULL; + + if (name) { + stype = nm_setting_lookup_type (name); + if (stype != G_TYPE_INVALID) { + setting = g_object_new (stype, NULL); + g_warn_if_fail (NM_IS_SETTING (setting)); + } + } + return setting; +} + +static gboolean +get_answer (const char *prop, const char *value) +{ + char *tmp_str; + char *question; + gboolean answer = FALSE; + + if (value) + question = g_strdup_printf (_("Do you also want to set '%s' to '%s'? [yes]: "), prop, value); + else + question = g_strdup_printf (_("Do you also want to clear '%s'? [yes]: "), prop); + tmp_str = nmc_get_user_input (question); + if (!tmp_str || matches (tmp_str, "yes") == 0) + answer = TRUE; + g_free (tmp_str); + g_free (question); + return answer; +} + +static void ipv4_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data); +static void ipv6_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data); + +static void +ipv4_addresses_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func (object, G_CALLBACK (ipv4_method_changed_cb), NULL); + + /* If we have some IP addresses set method to 'manual'. + * Else if the method was 'manual', change it back to 'auto'. + */ + if (nm_setting_ip_config_get_num_addresses (NM_SETTING_IP_CONFIG (object))) { + if (g_strcmp0 (nm_setting_ip_config_get_method (NM_SETTING_IP_CONFIG (object)), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + if (!answered) { + answered = TRUE; + answer = get_answer ("ipv4.method", "manual"); + } + if (answer) + g_object_set (object, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL); + } + } else { + answered = FALSE; + if (!g_strcmp0 (nm_setting_ip_config_get_method (NM_SETTING_IP_CONFIG (object)), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + g_object_set (object, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + } + + g_signal_handlers_unblock_by_func (object, G_CALLBACK (ipv4_method_changed_cb), NULL); +} + +static void +ipv4_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static GValue value = G_VALUE_INIT; + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func (object, G_CALLBACK (ipv4_addresses_changed_cb), NULL); + + /* If method != manual, remove addresses (save them for restoring them later when method becomes 'manual' */ + if (g_strcmp0 (nm_setting_ip_config_get_method (NM_SETTING_IP_CONFIG (object)), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + if (nm_setting_ip_config_get_num_addresses (NM_SETTING_IP_CONFIG (object))) { + if (!answered) { + answered = TRUE; + answer = get_answer ("ipv4.addresses", NULL); + } + if (answer) { + if (G_IS_VALUE (&value)) + g_value_unset (&value); + nmc_property_get_gvalue (NM_SETTING (object), NM_SETTING_IP_CONFIG_ADDRESSES, &value); + g_object_set (object, NM_SETTING_IP_CONFIG_ADDRESSES, NULL, NULL); + } + } + } else { + answered = FALSE; + if (G_IS_VALUE (&value)) { + nmc_property_set_gvalue (NM_SETTING (object), NM_SETTING_IP_CONFIG_ADDRESSES, &value); + g_value_unset (&value); + } + } + + g_signal_handlers_unblock_by_func (object, G_CALLBACK (ipv4_addresses_changed_cb), NULL); +} + +static void +ipv6_addresses_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func (object, G_CALLBACK (ipv6_method_changed_cb), NULL); + + /* If we have some IP addresses set method to 'manual'. + * Else if the method was 'manual', change it back to 'auto'. + */ + if (nm_setting_ip_config_get_num_addresses (NM_SETTING_IP_CONFIG (object))) { + if (g_strcmp0 (nm_setting_ip_config_get_method (NM_SETTING_IP_CONFIG (object)), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + if (!answered) { + answered = TRUE; + answer = get_answer ("ipv6.method", "manual"); + } + if (answer) + g_object_set (object, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, NULL); + } + } else { + answered = FALSE; + if (!g_strcmp0 (nm_setting_ip_config_get_method (NM_SETTING_IP_CONFIG (object)), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) + g_object_set (object, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); + } + + g_signal_handlers_unblock_by_func (object, G_CALLBACK (ipv6_method_changed_cb), NULL); +} + +static void +ipv6_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static GValue value = G_VALUE_INIT; + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func (object, G_CALLBACK (ipv6_addresses_changed_cb), NULL); + + /* If method != manual, remove addresses (save them for restoring them later when method becomes 'manual' */ + if (g_strcmp0 (nm_setting_ip_config_get_method (NM_SETTING_IP_CONFIG (object)), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + if (nm_setting_ip_config_get_num_addresses (NM_SETTING_IP_CONFIG (object))) { + if (!answered) { + answered = TRUE; + answer = get_answer ("ipv6.addresses", NULL); + } + if (answer) { + if (G_IS_VALUE (&value)) + g_value_unset (&value); + nmc_property_get_gvalue (NM_SETTING (object), NM_SETTING_IP_CONFIG_ADDRESSES, &value); + g_object_set (object, NM_SETTING_IP_CONFIG_ADDRESSES, NULL, NULL); + } + } + } else { + answered = FALSE; + if (G_IS_VALUE (&value)) { + nmc_property_set_gvalue (NM_SETTING (object), NM_SETTING_IP_CONFIG_ADDRESSES, &value); + g_value_unset (&value); + } + } + + g_signal_handlers_unblock_by_func (object, G_CALLBACK (ipv6_addresses_changed_cb), NULL); +} + +static void +wireless_band_channel_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + const char *value = NULL, *mode; + char str[16]; + NMSettingWireless *s_wireless = NM_SETTING_WIRELESS (object); + + if (strcmp (g_param_spec_get_name (pspec), NM_SETTING_WIRELESS_BAND) == 0) { + value = nm_setting_wireless_get_band (s_wireless); + if (!value) + return; + } else { + guint32 channel = nm_setting_wireless_get_channel (s_wireless); + + if (channel == 0) + return; + + g_snprintf (str, sizeof (str), "%d", nm_setting_wireless_get_channel (s_wireless)); + value = str; + } + + mode = nm_setting_wireless_get_mode (NM_SETTING_WIRELESS (object)); + if (!mode || !*mode || strcmp (mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0) { + g_print (_("Warning: %s.%s set to '%s', but it might be ignored in infrastructure mode\n"), + nm_setting_get_name (NM_SETTING (s_wireless)), g_param_spec_get_name (pspec), + value); + } +} + +static void +connection_master_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NMSettingConnection *s_con = NM_SETTING_CONNECTION (object); + NMConnection *connection = NM_CONNECTION (user_data); + NMSetting *s_ipv4, *s_ipv6; + const char *value, *tmp_str; + + value = nm_setting_connection_get_master (s_con); + if (value) { + s_ipv4 = nm_connection_get_setting_by_name (connection, NM_SETTING_IP4_CONFIG_SETTING_NAME); + s_ipv6 = nm_connection_get_setting_by_name (connection, NM_SETTING_IP6_CONFIG_SETTING_NAME); + if (s_ipv4 || s_ipv6) { + g_print (_("Warning: setting %s.%s requires removing ipv4 and ipv6 settings\n"), + nm_setting_get_name (NM_SETTING (s_con)), g_param_spec_get_name (pspec)); + tmp_str = nmc_get_user_input (_("Do you want to remove them? [yes] ")); + if (!tmp_str || matches (tmp_str, "yes") == 0) { + if (s_ipv4) + nm_connection_remove_setting (connection, G_OBJECT_TYPE (s_ipv4)); + if (s_ipv6) + nm_connection_remove_setting (connection, G_OBJECT_TYPE (s_ipv6)); + } + } + } +} + +void +nmc_setting_ip4_connect_handlers (NMSettingIPConfig *setting) +{ + g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); + + g_signal_connect (setting, "notify::" NM_SETTING_IP_CONFIG_ADDRESSES, + G_CALLBACK (ipv4_addresses_changed_cb), NULL); + g_signal_connect (setting, "notify::" NM_SETTING_IP_CONFIG_METHOD, + G_CALLBACK (ipv4_method_changed_cb), NULL); +} + +void +nmc_setting_ip6_connect_handlers (NMSettingIPConfig *setting) +{ + g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); + + g_signal_connect (setting, "notify::" NM_SETTING_IP_CONFIG_ADDRESSES, + G_CALLBACK (ipv6_addresses_changed_cb), NULL); + g_signal_connect (setting, "notify::" NM_SETTING_IP_CONFIG_METHOD, + G_CALLBACK (ipv6_method_changed_cb), NULL); +} + +void +nmc_setting_wireless_connect_handlers (NMSettingWireless *setting) +{ + g_return_if_fail (NM_IS_SETTING_WIRELESS (setting)); + + g_signal_connect (setting, "notify::" NM_SETTING_WIRELESS_BAND, + G_CALLBACK (wireless_band_channel_changed_cb), NULL); + g_signal_connect (setting, "notify::" NM_SETTING_WIRELESS_CHANNEL, + G_CALLBACK (wireless_band_channel_changed_cb), NULL); +} + +void +nmc_setting_connection_connect_handlers (NMSettingConnection *setting, NMConnection *connection) +{ + g_return_if_fail (NM_IS_SETTING_CONNECTION (setting)); + + g_signal_connect (setting, "notify::" NM_SETTING_CONNECTION_MASTER, + G_CALLBACK (connection_master_changed_cb), connection); +} + +/* + * Customize some properties of the setting so that the setting has sensible + * values. + */ +void +nmc_setting_custom_init (NMSetting *setting) +{ + g_return_if_fail (NM_IS_SETTING (setting)); + + if (NM_IS_SETTING_IP4_CONFIG (setting)) { + g_object_set (NM_SETTING_IP_CONFIG (setting), + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + nmc_setting_ip4_connect_handlers (NM_SETTING_IP_CONFIG (setting)); + } else if (NM_IS_SETTING_IP6_CONFIG (setting)) { + g_object_set (NM_SETTING_IP_CONFIG (setting), + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + nmc_setting_ip6_connect_handlers (NM_SETTING_IP_CONFIG (setting)); + } else if (NM_IS_SETTING_WIRELESS (setting)) { + g_object_set (NM_SETTING_WIRELESS (setting), + NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, + NULL); + nmc_setting_wireless_connect_handlers (NM_SETTING_WIRELESS (setting)); + } +} + +/* === SetFunc, RemoveFunc, DescribeFunc, ValuesFunc functions === */ +static gboolean +verify_string_list (char **strv, + const char *prop, + gboolean (*validate_func) (const char *), + GError **error) +{ + char **iter; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + for (iter = strv; iter && *iter; iter++) { + if (**iter == '\0') + continue; + if (validate_func) { + if (!validate_func (*iter)) { + g_set_error (error, 1, 0, _("'%s' is not valid"), + *iter); + return FALSE; + } + } + } + return TRUE; +} + +/* Validate 'val' number against to int property spec */ +static gboolean +validate_int (NMSetting *setting, const char* prop, gint val, GError **error) +{ + GParamSpec *pspec; + GValue value = G_VALUE_INIT; + gboolean success = TRUE; + + g_value_init (&value, G_TYPE_INT); + g_value_set_int (&value, val); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + g_assert (G_IS_PARAM_SPEC (pspec)); + if (g_param_value_validate (pspec, &value)) { + GParamSpecInt *pspec_int = (GParamSpecInt *) pspec; + g_set_error (error, 1, 0, _("'%d' is not valid; use <%d-%d>"), + val, pspec_int->minimum, pspec_int->maximum); + success = FALSE; + } + g_value_unset (&value); + return success; +} + +static gboolean +validate_int64 (NMSetting *setting, const char* prop, gint64 val, GError **error) +{ + GParamSpec *pspec; + GValue value = G_VALUE_INIT; + gboolean success = TRUE; + + g_value_init (&value, G_TYPE_INT64); + g_value_set_int64 (&value, val); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + g_assert (G_IS_PARAM_SPEC (pspec)); + if (g_param_value_validate (pspec, &value)) { + GParamSpecInt64 *pspec_int = (GParamSpecInt64 *) pspec; + G_STATIC_ASSERT (sizeof (long long) >= sizeof (gint64)); + g_set_error (error, 1, 0, _("'%lld' is not valid; use <%lld-%lld>"), + (long long) val, (long long) pspec_int->minimum, (long long) pspec_int->maximum); + success = FALSE; + } + g_value_unset (&value); + return success; +} + +/* Validate 'val' number against to uint property spec */ +static gboolean +validate_uint (NMSetting *setting, const char* prop, guint val, GError **error) +{ + GParamSpec *pspec; + GValue value = G_VALUE_INIT; + gboolean success = TRUE; + + g_value_init (&value, G_TYPE_UINT); + g_value_set_uint (&value, val); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + g_assert (G_IS_PARAM_SPEC (pspec)); + if (g_param_value_validate (pspec, &value)) { + GParamSpecUInt *pspec_uint = (GParamSpecUInt *) pspec; + g_set_error (error, 1, 0, _("'%u' is not valid; use <%u-%u>"), + val, pspec_uint->minimum, pspec_uint->maximum); + success = FALSE; + } + g_value_unset (&value); + return success; +} + +static gboolean +check_and_set_string (NMSetting *setting, + const char *prop, + const char *val, + const char **valid_strv, + GError **error) +{ + const char *checked_val; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + checked_val = nmc_string_is_valid (val, valid_strv, error); + if (!checked_val) + return FALSE; + + g_object_set (setting, prop, checked_val, NULL); + return TRUE; +} + +#define DEFINE_SETTER_STR_LIST_MULTI(def_func, s_macro, set_func) \ + static gboolean \ + def_func (NMSetting *setting, \ + const char *prop, \ + const char *val, \ + const char **valid_strv, \ + GError **error) \ + { \ + char **strv = NULL, **iter; \ + const char *item; \ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \ + strv = nmc_strsplit_set (val, " \t,", 0); \ + for (iter = strv; iter && *iter; iter++) { \ + if (!(item = nmc_string_is_valid (g_strstrip (*iter), valid_strv, error))) { \ + g_strfreev (strv); \ + return FALSE; \ + } \ + set_func (s_macro (setting), item); \ + } \ + g_strfreev (strv); \ + return TRUE; \ + } + +#define DEFINE_SETTER_OPTIONS(def_func, s_macro, s_type, add_func, valid_func1, valid_func2) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \ + { \ + char **strv = NULL, **iter; \ + const char **(*valid_func1_p) (s_type *) = valid_func1; \ + const char * (*valid_func2_p) (const char *, const char *, GError **) = valid_func2; \ + const char *opt_name, *opt_val; \ + \ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \ + \ + strv = nmc_strsplit_set (val, ",", 0); \ + for (iter = strv; iter && *iter; iter++) { \ + char *left = g_strstrip (*iter); \ + char *right = strchr (left, '='); \ + if (!right) { \ + g_set_error (error, 1, 0, _("'%s' is not valid; use <option>=<value>"), *iter); \ + g_strfreev (strv); \ + return FALSE; \ + } \ + *right++ = '\0'; \ + \ + if (valid_func1_p) { \ + const char **valid_options = valid_func1_p (s_macro (setting)); \ + if (!(opt_name = nmc_string_is_valid (g_strstrip (left), valid_options, error))) { \ + g_strfreev (strv); \ + return FALSE; \ + } \ + } else \ + opt_name = g_strstrip (left);\ + \ + opt_val = g_strstrip (right); \ + if (valid_func2_p) { \ + if (!(opt_val = valid_func2_p ((const char *) left, (const char *) opt_val, error))) { \ + g_strfreev (strv); \ + return FALSE; \ + }\ + }\ + add_func (s_macro (setting), opt_name, opt_val); \ + } \ + g_strfreev (strv); \ + return TRUE; \ + } + +#define DEFINE_REMOVER_INDEX(def_func, s_macro, num_func, rem_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *option, guint32 idx, GError **error) \ + { \ + guint32 num; \ + if (option) { \ + g_set_error (error, 1, 0, _("index '%s' is not valid"), option); \ + return FALSE; \ + } \ + num = num_func (s_macro (setting)); \ + if (num == 0) { \ + g_set_error_literal (error, 1, 0, _("no item to remove")); \ + return FALSE; \ + } \ + if (idx >= num) { \ + g_set_error (error, 1, 0, _("index '%d' is not in range <0-%d>"), idx, num - 1); \ + return FALSE; \ + } \ + rem_func (s_macro (setting), idx); \ + return TRUE; \ + } + +#define DEFINE_REMOVER_INDEX_OR_VALUE(def_func, s_macro, num_func, rem_func_idx, rem_func_val) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *value, guint32 idx, GError **error) \ + { \ + guint32 num; \ + if (value) { \ + gboolean ret; \ + char *value_stripped = g_strstrip (g_strdup (value)); \ + ret = rem_func_val (s_macro (setting), value_stripped, error); \ + g_free (value_stripped); \ + return ret; \ + } \ + num = num_func (s_macro (setting)); \ + if (num == 0) { \ + g_set_error_literal (error, 1, 0, _("no item to remove")); \ + return FALSE; \ + } \ + if (idx >= num) { \ + g_set_error (error, 1, 0, _("index '%d' is not in range <0-%d>"), idx, num - 1); \ + return FALSE; \ + } \ + rem_func_idx (s_macro (setting), idx); \ + return TRUE; \ + } + +#define DEFINE_REMOVER_OPTION(def_func, s_macro, rem_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *option, guint32 idx, GError **error) \ + { \ + gboolean success = FALSE; \ + if (option && *option) { \ + success = rem_func (s_macro (setting), option); \ + if (!success) \ + g_set_error (error, 1, 0, _("invalid option '%s'"), option); \ + } else \ + g_set_error_literal (error, 1, 0, _("missing option")); \ + return success; \ + } + +#define DEFINE_ALLOWED_VAL_FUNC(def_func, valid_values) \ + static const char * \ + def_func (NMSetting *setting, const char *prop) \ + { \ + static char *values = NULL; \ + if (G_UNLIKELY (values == NULL)) \ + values = g_strjoinv (", ", (char **) valid_values); \ + return values; \ + } + +/* --- generic property setter functions --- */ +static gboolean +nmc_property_set_string (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_object_set (setting, prop, val, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_uint (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + unsigned long val_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_uint (val, TRUE, 0, G_MAXUINT, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid number (or out of range)"), val); + return FALSE; + } + + /* Validate the number according to the property spec */ + if (!validate_uint (setting, prop, (guint) val_int, error)) + return FALSE; + + g_object_set (setting, prop, (guint) val_int, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_int (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + long int val_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_int (val, TRUE, G_MININT, G_MAXINT, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid number (or out of range)"), val); + return FALSE; + } + + /* Validate the number according to the property spec */ + if (!validate_int (setting, prop, (gint) val_int, error)) + return FALSE; + + g_object_set (setting, prop, (gint) val_int, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_int64 (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + long val_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_int (val, FALSE, 0, 0, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid number (or out of range)"), val); + return FALSE; + } + + /* Validate the number according to the property spec */ + if (!validate_int64 (setting, prop, (gint64) val_int, error)) + return FALSE; + + g_object_set (setting, prop, (gint64) val_int, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_bool (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + gboolean val_bool; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_bool (val, &val_bool, error)) + return FALSE; + + g_object_set (setting, prop, val_bool, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_ssid (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + GBytes *ssid; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (strlen (val) > 32) { + g_set_error (error, 1, 0, _("'%s' is not valid"), val); + return FALSE; + } + + ssid = g_bytes_new (val, strlen (val)); + g_object_set (setting, prop, ssid, NULL); + g_bytes_unref (ssid); + return TRUE; +} + +static gboolean +nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nm_utils_hwaddr_valid (val, ETH_ALEN)) { + g_set_error (error, 1, 0, _("'%s' is not a valid Ethernet MAC"), val); + return FALSE; + } + + g_object_set (setting, prop, val, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_mtu (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + const char *mtu = val; + + if (strcmp (mtu, "auto") == 0) + mtu = "0"; + + return nmc_property_set_uint (setting, prop, mtu, error); +} + +static gboolean +nmc_property_set_ifname (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nm_utils_iface_valid_name (val)) { + g_set_error (error, 1, 0, _("'%s' is not a valid interface name"), val); + return FALSE; + } + g_object_set (setting, prop, val, NULL); + return TRUE; +} + +#define ALL_SECRET_FLAGS \ + (NM_SETTING_SECRET_FLAG_NONE | \ + NM_SETTING_SECRET_FLAG_AGENT_OWNED | \ + NM_SETTING_SECRET_FLAG_NOT_SAVED | \ + NM_SETTING_SECRET_FLAG_NOT_REQUIRED) + +static gboolean +nmc_property_set_secret_flags (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + unsigned long flags = 0, val_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + for (iter = strv; iter && *iter; iter++) { + if (!nmc_string_to_uint (*iter, TRUE, 0, ALL_SECRET_FLAGS, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid flag number; use <0-%d>"), + *iter, ALL_SECRET_FLAGS); + g_strfreev (strv); + return FALSE; + } + flags += val_int; + } + g_strfreev (strv); + + /* Validate the flags number */ + if (flags > ALL_SECRET_FLAGS) { + flags = ALL_SECRET_FLAGS; + g_print (_("Warning: '%s' sum is higher than all flags => all flags set\n"), val); + } + + g_object_set (setting, prop, (guint) flags, NULL); + return TRUE; +} + +static gboolean +nmc_util_is_domain (const char *domain) +{ + //FIXME: implement + return TRUE; +} + +static gboolean +nmc_property_set_byte_array (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + char *val_strip; + const char *delimiters = " \t,"; + long int val_int; + GBytes *bytes; + GByteArray *array = NULL; + gboolean success = TRUE; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + val_strip = g_strstrip (g_strdup (val)); + + /* First try hex string in the format of AAbbCCDd */ + bytes = nm_utils_hexstr2bin (val_strip); + if (bytes) { + array = g_bytes_unref_to_array (bytes); + goto done; + } + + /* Otherwise, consider the following format: AA b 0xCc D */ + strv = nmc_strsplit_set (val_strip, delimiters, 0); + array = g_byte_array_sized_new (g_strv_length (strv)); + for (iter = strv; iter && *iter; iter++) { + if (!nmc_string_to_int_base (g_strstrip (*iter), 16, TRUE, 0, 255, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid hex character"), *iter); + success = FALSE; + goto done; + } + g_byte_array_append (array, (const guint8 *) &val_int, 1); + } + +done: + if (success) + g_object_set (setting, prop, array, NULL); + + g_strfreev (strv); + if (array) + g_byte_array_free (array, TRUE); + return success; +} + +#define DEFINE_SETTER_MAC_BLACKLIST(def_func, s_macro, add_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \ + { \ + guint8 buf[32]; \ + char **list = NULL, **iter; \ + GSList *macaddr_blacklist = NULL; \ + \ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \ + \ + list = nmc_strsplit_set (val, " \t,", 0); \ + for (iter = list; iter && *iter; iter++) { \ + if (!nm_utils_hwaddr_aton (*iter, buf, ETH_ALEN)) { \ + g_set_error (error, 1, 0, _("'%s' is not a valid MAC"), *iter); \ + g_strfreev (list); \ + g_slist_free (macaddr_blacklist); \ + return FALSE; \ + } \ + } \ + \ + for (iter = list; iter && *iter; iter++) \ + add_func (s_macro (setting), *iter); \ + \ + g_strfreev (list); \ + return TRUE; \ + } + +static char * +nmc_property_out2in_cut_paren (const char *out_format) +{ + const char *p; + size_t n; + + p = strstr (out_format, " ("); + n = p ? p - out_format : strlen (out_format); + return g_strndup (out_format, n); +} + +/* --- NM_SETTING_CONNECTION_SETTING_NAME property setter functions --- */ +#if 0 +/* + * Setting/removing UUID has been forbidden. + * Should it be enabled later, this function can be used. + */ +static gboolean +nmc_property_con_set_uuid (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nm_utils_is_uuid (val)) { + g_set_error (error, 1, 0, _("'%s' is not a valid UUID"), val); + return FALSE; + } + g_object_set (setting, prop, val, NULL); + return TRUE; +} +#endif + +/* 'permissions' */ +/* define from libnm-core/nm-setting-connection.c */ +#define PERM_USER_PREFIX "user:" + +static gboolean +permissions_valid (const char *perm) +{ + if (!perm || perm[0] == '\0') + return FALSE; + + if (strncmp (perm, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0) { + if ( strlen (perm) <= strlen (PERM_USER_PREFIX) + || strchr (perm + strlen (PERM_USER_PREFIX), ':')) + return FALSE; + } else { + if (strchr (perm, ':')) + return FALSE; + } + + return TRUE; +} + +static gboolean +nmc_property_connection_set_permissions (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL; + guint i = 0; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + if (!verify_string_list (strv, prop, permissions_valid, error)) { + g_strfreev (strv); + return FALSE; + } + + for (i = 0; strv && strv[i]; i++) { + const char *user; + + if (strncmp (strv[i], PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0) + user = strv[i]+strlen (PERM_USER_PREFIX); + else + user = strv[i]; + + nm_setting_connection_add_permission (NM_SETTING_CONNECTION (setting), "user", user, NULL); + } + + return TRUE; +} + +static gboolean +_validate_and_remove_connection_permission (NMSettingConnection *setting, + const char *perm, + GError **error) +{ + gboolean ret; + + ret = nm_setting_connection_remove_permission_by_value (setting, "user", perm, NULL); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain permission '%s'"), perm); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_connection_remove_permissions, + NM_SETTING_CONNECTION, + nm_setting_connection_get_num_permissions, + nm_setting_connection_remove_permission, + _validate_and_remove_connection_permission) + +static const char * +nmc_property_connection_describe_permissions (NMSetting *setting, const char *prop) +{ + return _("Enter a list of user permissions. This is a list of user names formatted as:\n" + " [user:]<user name 1>, [user:]<user name 2>,...\n" + "The items can be separated by commas or spaces.\n\n" + "Example: alice bob charlie\n"); +} + +/* 'master' */ +static gboolean +nmc_property_con_set_master (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!val) + ; + else if (!*val) + val = NULL; + else if ( !nm_utils_iface_valid_name (val) + && !nm_utils_is_uuid (val)) { + g_set_error (error, 1, 0, + _("'%s' is not valid master; use ifname or connection UUID"), + val); + return FALSE; + } + g_object_set (setting, prop, val, NULL); + return TRUE; +} + +/* 'slave-type' */ +static const char *con_valid_slave_types[] = { + NM_SETTING_BOND_SETTING_NAME, + NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_VLAN_SETTING_NAME, + NULL +}; + +static gboolean +nmc_property_con_set_slave_type (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, con_valid_slave_types, error); +} + + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_con_allowed_slave_type, con_valid_slave_types) + +/* 'secondaries' */ +static gboolean +nmc_property_connection_set_secondaries (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + NMConnection *con; + char **strv = NULL, **iter; + guint i = 0; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + for (iter = strv; iter && *iter; iter++) { + if (**iter == '\0') + continue; + + if (nm_utils_is_uuid (*iter)) { + con = nmc_find_connection (nm_cli.connections, + "uuid", *iter, NULL); + if (!con) + g_print (_("Warning: %s is not an UUID of any existing connection profile\n"), *iter); + else { + /* Currenly NM only supports VPN connections as secondaries */ + if (!nm_connection_is_type (con, NM_SETTING_VPN_SETTING_NAME)) { + g_set_error (error, 1, 0, _("'%s' is not a VPN connection profile"), *iter); + g_strfreev (strv); + return FALSE; + } + } + } else { + con = nmc_find_connection (nm_cli.connections, + "id", *iter, NULL); + if (!con) { + g_set_error (error, 1, 0, _("'%s' is not a name of any exiting profile"), *iter); + g_strfreev (strv); + return FALSE; + } + + /* Currenly NM only supports VPN connections as secondaries */ + if (!nm_connection_is_type (con, NM_SETTING_VPN_SETTING_NAME)) { + g_set_error (error, 1, 0, _("'%s' is not a VPN connection profile"), *iter); + g_strfreev (strv); + return FALSE; + } + + /* translate id to uuid */ + g_free (*iter); + *iter = g_strdup (nm_connection_get_uuid (con)); + } + } + + while (strv && strv[i]) + nm_setting_connection_add_secondary (NM_SETTING_CONNECTION (setting), strv[i++]); + g_strfreev (strv); + + return TRUE; +} + +static gboolean +_validate_and_remove_connection_secondary (NMSettingConnection *setting, + const char *secondary_uuid, + GError **error) +{ + gboolean ret; + + if (!nm_utils_is_uuid (secondary_uuid)) { + g_set_error (error, 1, 0, + _("the value '%s' is not a valid UUID"), secondary_uuid); + return FALSE; + } + + ret = nm_setting_connection_remove_secondary_by_value (setting, secondary_uuid); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain UUID '%s'"), secondary_uuid); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_connection_remove_secondaries, + NM_SETTING_CONNECTION, + nm_setting_connection_get_num_secondaries, + nm_setting_connection_remove_secondary, + _validate_and_remove_connection_secondary) + +static const char * +nmc_property_connection_describe_secondaries (NMSetting *setting, const char *prop) +{ + return _("Enter secondary connections that should be activated when this connection is\n" + "activated. Connections can be specified either by UUID or ID (name). nmcli\n" + "transparently translates names to UUIDs. Note that NetworkManager only supports\n" + "VPNs as secondary connections at the moment.\n" + "The items can be separated by commas or spaces.\n\n" + "Example: private-openvpn, fe6ba5d8-c2fc-4aae-b2e3-97efddd8d9a7\n"); +} + +/* --- NM_SETTING_802_1X_SETTING_NAME property setter functions --- */ +#define DEFINE_SETTER_STR_LIST(def_func, set_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \ + { \ + char **strv = NULL; \ + guint i = 0; \ + \ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \ + \ + strv = nmc_strsplit_set (val, " \t,", 0); \ + while (strv && strv[i]) \ + set_func (NM_SETTING_802_1X (setting), strv[i++]); \ + g_strfreev (strv); \ + return TRUE; \ + } + +#define DEFINE_SETTER_CERT(def_func, set_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \ + { \ + const char *SCHEME_PATH = "file://"; \ + char *val_strip = g_strstrip (g_strdup (val)); \ + char *p = val_strip; \ + gboolean success; \ + \ + if (strncmp (val_strip, SCHEME_PATH, strlen (SCHEME_PATH)) == 0) \ + p += strlen (SCHEME_PATH); \ + \ + success = set_func (NM_SETTING_802_1X (setting), \ + p, \ + NM_SETTING_802_1X_CK_SCHEME_PATH, \ + NULL, \ + error); \ + g_free (val_strip); \ + return success; \ + } + +#define DEFINE_SETTER_PRIV_KEY(def_func, pwd_func, set_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \ + { \ + char **strv = NULL; \ + const char *SCHEME_PATH = "file://"; \ + char *val_strip = g_strstrip (g_strdup (val)); \ + char *p = val_strip; \ + const char *path, *password; \ + gboolean success; \ + \ + if (strncmp (val_strip, SCHEME_PATH, strlen (SCHEME_PATH)) == 0) \ + p += strlen (SCHEME_PATH); \ + \ + strv = nmc_strsplit_set (p, " \t,", 2); \ + path = strv[0]; \ + if (g_strv_length (strv) == 2) \ + password = strv[1]; \ + else \ + password = pwd_func (NM_SETTING_802_1X (setting)); \ + if (password) { \ + char *tmp_pwd = g_strdup (password); \ + success = set_func (NM_SETTING_802_1X (setting), \ + path, \ + tmp_pwd, \ + NM_SETTING_802_1X_CK_SCHEME_PATH, \ + NULL, \ + error); \ + g_free (tmp_pwd); \ + } else { \ + success = FALSE; \ + g_set_error_literal (error, 1, 0, _("private key password not provided")); \ + } \ + g_free (val_strip); \ + g_strfreev (strv); \ + return success; \ + } + +/* 'eap' */ +DEFINE_SETTER_STR_LIST_MULTI (check_and_add_802_1X_eap, + NM_SETTING_802_1X, + nm_setting_802_1x_add_eap_method) +static gboolean +nmc_property_802_1X_set_eap (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd", NULL }; + return check_and_add_802_1X_eap (setting, prop, val, valid_eap, error); +} + +static gboolean +_validate_and_remove_eap_method (NMSetting8021x *setting, + const char *eap, + GError **error) +{ + gboolean ret; + + ret = nm_setting_802_1x_remove_eap_method_by_value(setting, eap); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain EAP method '%s'"), eap); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_802_1X_remove_eap, + NM_SETTING_802_1X, + nm_setting_802_1x_get_num_eap_methods, + nm_setting_802_1x_remove_eap_method, + _validate_and_remove_eap_method) + +/* 'ca-cert' */ +DEFINE_SETTER_CERT (nmc_property_802_1X_set_ca_cert, nm_setting_802_1x_set_ca_cert) + +/* 'altsubject-matches' */ +DEFINE_SETTER_STR_LIST (nmc_property_802_1X_set_altsubject_matches, nm_setting_802_1x_add_altsubject_match) + +static gboolean +_validate_and_remove_altsubject_match (NMSetting8021x *setting, + const char *altsubject_match, + GError **error) +{ + gboolean ret; + + ret = nm_setting_802_1x_remove_altsubject_match_by_value (setting, altsubject_match); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain alternative subject match '%s'"), + altsubject_match); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_802_1X_remove_altsubject_matches, + NM_SETTING_802_1X, + nm_setting_802_1x_get_num_altsubject_matches, + nm_setting_802_1x_remove_altsubject_match, + _validate_and_remove_altsubject_match) + +/* 'client-cert' */ +DEFINE_SETTER_CERT (nmc_property_802_1X_set_client_cert, nm_setting_802_1x_set_client_cert) + +/* 'phase2-ca-cert' */ +DEFINE_SETTER_CERT (nmc_property_802_1X_set_phase2_ca_cert, nm_setting_802_1x_set_phase2_ca_cert) + +/* 'phase2-altsubject-matches' */ +DEFINE_SETTER_STR_LIST (nmc_property_802_1X_set_phase2_altsubject_matches, nm_setting_802_1x_add_phase2_altsubject_match) + +static gboolean +_validate_and_remove_phase2_altsubject_match (NMSetting8021x *setting, + const char *phase2_altsubject_match, + GError **error) +{ + gboolean ret; + + ret = nm_setting_802_1x_remove_phase2_altsubject_match_by_value (setting, phase2_altsubject_match); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain \"phase2\" alternative subject match '%s'"), + phase2_altsubject_match); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_802_1X_remove_phase2_altsubject_matches, + NM_SETTING_802_1X, + nm_setting_802_1x_get_num_phase2_altsubject_matches, + nm_setting_802_1x_remove_phase2_altsubject_match, + _validate_and_remove_phase2_altsubject_match) + +/* 'phase2-client-cert' */ +DEFINE_SETTER_CERT (nmc_property_802_1X_set_phase2_client_cert, nm_setting_802_1x_set_phase2_client_cert) + +/* 'private-key' */ +DEFINE_SETTER_PRIV_KEY (nmc_property_802_1X_set_private_key, + nm_setting_802_1x_get_private_key_password, + nm_setting_802_1x_set_private_key) + +/* 'phase2-private-key' */ +DEFINE_SETTER_PRIV_KEY (nmc_property_802_1X_set_phase2_private_key, + nm_setting_802_1x_get_phase2_private_key_password, + nm_setting_802_1x_set_phase2_private_key) + +static const char * +nmc_property_802_1X_describe_private_key (NMSetting *setting, const char *prop) +{ + return _("Enter path to a private key and the key password (if not set yet):\n" + " <file path> [<password>]\n" + "Example: /home/cimrman/jara-priv-key Dardanely\n"); +} + +/* 'phase1-peapver' */ +static const char *_802_1X_valid_phase1_peapvers[] = { "0", "1", NULL }; + +static gboolean +nmc_property_802_1X_set_phase1_peapver (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _802_1X_valid_phase1_peapvers, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_802_1X_allowed_phase1_peapver, _802_1X_valid_phase1_peapvers) + +/* 'phase1-peaplabel' */ +static const char *_802_1X_valid_phase1_peaplabels[] = { "0", "1", NULL }; + +static gboolean +nmc_property_802_1X_set_phase1_peaplabel (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _802_1X_valid_phase1_peaplabels, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_802_1X_allowed_phase1_peaplabel, _802_1X_valid_phase1_peaplabels) + +/* 'phase1-fast-provisioning' */ +static const char *_802_1X_valid_phase1_fast_provisionings[] = { "0", "1", "2", "3", NULL }; + +static gboolean +nmc_property_802_1X_set_phase1_fast_provisioning (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _802_1X_valid_phase1_fast_provisionings, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_802_1X_allowed_phase1_fast_provisioning, _802_1X_valid_phase1_fast_provisionings) + +/* 'phase2-auth' */ +static const char *_802_1X_valid_phase2_auths[] = + { "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls", NULL }; + +static gboolean +nmc_property_802_1X_set_phase2_auth (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _802_1X_valid_phase2_auths, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_802_1X_allowed_phase2_auth, _802_1X_valid_phase2_auths) + +/* 'phase2-autheap' */ +static const char *_802_1X_valid_phase2_autheaps[] = { "md5", "mschapv2", "otp", "gtc", "tls", NULL }; +static gboolean +nmc_property_802_1X_set_phase2_autheap (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _802_1X_valid_phase2_autheaps, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_802_1X_allowed_phase2_autheap, _802_1X_valid_phase2_autheaps) + +/* 'password-raw' */ +static gboolean +nmc_property_802_1X_set_password_raw (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return nmc_property_set_byte_array (setting, prop, val, error); +} + +static const char * +nmc_property_802_1X_describe_password_raw (NMSetting *setting, const char *prop) +{ + return _("Enter bytes as a list of hexadecimal values.\n" + "Two formats are accepted:\n" + "(a) a string of hexadecimal digits, where each two digits represent one byte\n" + "(b) space-separated list of bytes written as hexadecimal digits " + "(with optional 0x/0X prefix, and optional leading 0).\n\n" + "Examples: ab0455a6ea3a74C2\n" + " ab 4 55 0xa6 ea 3a 74 C2\n"); +} + +/* --- NM_SETTING_ADSL_SETTING_NAME property setter functions --- */ +/* 'protocol' */ +static const char *adsl_valid_protocols[] = { + NM_SETTING_ADSL_PROTOCOL_PPPOA, + NM_SETTING_ADSL_PROTOCOL_PPPOE, + NM_SETTING_ADSL_PROTOCOL_IPOATM, + NULL +}; + +static gboolean +nmc_property_adsl_set_protocol (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, adsl_valid_protocols, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_adsl_allowed_protocol, adsl_valid_protocols) + +/* 'encapsulation' */ +static const char *adsl_valid_encapsulations[] = { + NM_SETTING_ADSL_ENCAPSULATION_VCMUX, + NM_SETTING_ADSL_ENCAPSULATION_LLC, + NULL +}; + +static gboolean +nmc_property_adsl_set_encapsulation (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, adsl_valid_encapsulations, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_adsl_allowed_encapsulation, adsl_valid_encapsulations) + +/* --- NM_SETTING_BLUETOOTH_SETTING_NAME property setter functions --- */ +/* 'type' */ +static gboolean +nmc_property_bluetooth_set_type (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + const char *types[] = { + NM_SETTING_BLUETOOTH_TYPE_DUN, + NM_SETTING_BLUETOOTH_TYPE_PANU, + NULL }; + + return check_and_set_string (setting, prop, val, types, error); +} + +/* --- NM_SETTING_BOND_SETTING_NAME property setter functions --- */ +/* 'options' */ +/* example: miimon=100,mode=balance-rr, updelay=5 */ +static gboolean +_validate_and_remove_bond_option (NMSettingBond *setting, const char *option) +{ + const char *opt; + const char **valid_options; + + valid_options = nm_setting_bond_get_valid_options (setting); + opt = nmc_string_is_valid (option, valid_options, NULL); + + if (opt) + return nm_setting_bond_remove_option (setting, opt); + else + return FALSE; +} + +/* Validate bonding 'options' values */ +static const char * +_validate_bond_option_value (const char *option, const char *value, GError **error) +{ + if (!g_strcmp0 (option, NM_SETTING_BOND_OPTION_MODE)) + return nmc_bond_validate_mode (value, error); + + return value; +} + +DEFINE_SETTER_OPTIONS (nmc_property_bond_set_options, + NM_SETTING_BOND, + NMSettingBond, + nm_setting_bond_add_option, + nm_setting_bond_get_valid_options, + _validate_bond_option_value) +DEFINE_REMOVER_OPTION (nmc_property_bond_remove_option_options, + NM_SETTING_BOND, + _validate_and_remove_bond_option) + +static const char * +nmc_property_bond_describe_options (NMSetting *setting, const char *prop) +{ + static char *desc = NULL; + const char **valid_options; + char *options_str; + + if (G_UNLIKELY (desc == NULL)) { + valid_options = nm_setting_bond_get_valid_options (NM_SETTING_BOND (setting)); + options_str = g_strjoinv (", ", (char **) valid_options); + + desc = g_strdup_printf (_("Enter a list of bonding options formatted as:\n" + " option = <value>, option = <value>,... \n" + "Valid options are: %s\n" + "'mode' can be provided as a name or a number:\n" + "balance-rr = 0\n" + "active-backup = 1\n" + "balance-xor = 2\n" + "broadcast = 3\n" + "802.3ad = 4\n" + "balance-tlb = 5\n" + "balance-alb = 6\n\n" + "Example: mode=2,miimon=120\n"), options_str); + g_free (options_str); + } + return desc; +} + +static const char * +nmc_property_bond_allowed_options (NMSetting *setting, const char *prop) +{ + const char **valid_options; + static char *allowed_vals = NULL; + + if (G_UNLIKELY (allowed_vals == NULL)) { + valid_options = nm_setting_bond_get_valid_options (NM_SETTING_BOND (setting)); + allowed_vals = g_strjoinv (", ", (char **) valid_options); + } + return allowed_vals; +} + +/* --- NM_SETTING_INFINIBAND_SETTING_NAME property setter functions --- */ +/* 'mac-addresss' */ +static gboolean +nmc_property_ib_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nm_utils_hwaddr_valid (val, INFINIBAND_ALEN)) { + g_set_error (error, 1, 0, _("'%s' is not a valid InfiniBand MAC"), val); + return FALSE; + } + + g_object_set (setting, prop, val, NULL); + return TRUE; +} + +/* 'transport-mode' */ +static const char *ib_valid_transport_modes[] = { "datagram", "connected", NULL }; + +static gboolean +nmc_property_ib_set_transport_mode (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, ib_valid_transport_modes, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_ib_allowed_transport_mode, ib_valid_transport_modes) + +/* 'p-key' */ +static gboolean +nmc_property_ib_set_p_key (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + gboolean p_key_valid = FALSE; + long p_key_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!strncasecmp (val, "0x", 2)) + p_key_valid = nmc_string_to_int_base (val + 2, 16, TRUE, 0, G_MAXUINT16, &p_key_int); + else + p_key_valid = nmc_string_to_int (val, TRUE, -1, G_MAXUINT16, &p_key_int); + + if (!p_key_valid) { + if (strcmp (val, "default") == 0) + p_key_int = -1; + else { + g_set_error (error, 1, 0, _("'%s' is not a valid IBoIP P_Key"), val); + return FALSE; + } + } + g_object_set (setting, prop, (gint) p_key_int, NULL); + return TRUE; +} + +/* --- IP4 / IP6 shared functions --- */ +static NMIPAddress * +_parse_ip_address (int family, const char *address, GError **error) +{ + char *value = g_strdup (address); + NMIPAddress *ipaddr; + + ipaddr = nmc_parse_and_build_address (family, g_strstrip (value), error); + g_free (value); + return ipaddr; +} + +static NMIPRoute * +_parse_ip_route (int family, const char *route, GError **error) +{ + char *value = g_strdup (route); + char **routev; + guint len; + NMIPRoute *iproute = NULL; + + routev = nmc_strsplit_set (g_strstrip (value), " \t", 0); + len = g_strv_length (routev); + if (len < 1 || len > 3) { + g_set_error (error, 1, 0, _("'%s' is not valid (the format is: ip[/prefix] [next-hop] [metric])"), + route); + goto finish; + } + iproute = nmc_parse_and_build_route (family, routev[0], routev[1], len >= 2 ? routev[2] : NULL, error); + +finish: + g_free (value); + g_strfreev (routev); + return iproute; +} + +/* --- NM_SETTING_IP4_CONFIG_SETTING_NAME property setter functions --- */ +/* 'method' */ +static const char *ipv4_valid_methods[] = { + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP4_CONFIG_METHOD_SHARED, + NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NULL +}; + +static gboolean +nmc_property_ipv4_set_method (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + /* Silently accept "static" and convert to "manual" */ + if (val && strlen (val) > 1 && matches (val, "static") == 0) + val = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; + + return check_and_set_string (setting, prop, val, ipv4_valid_methods, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_ipv4_allowed_method, ipv4_valid_methods) + +/* 'dns' */ +static gboolean +nmc_property_ipv4_set_dns (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter, *addr; + guint32 ip4_addr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + for (iter = strv; iter && *iter; iter++) { + addr = g_strstrip (*iter); + if (inet_pton (AF_INET, addr, &ip4_addr) < 1) { + g_set_error (error, 1, 0, _("invalid IPv4 address '%s'"), addr); + g_strfreev (strv); + return FALSE; + } + nm_setting_ip_config_add_dns (NM_SETTING_IP_CONFIG (setting), addr); + } + g_strfreev (strv); + return TRUE; +} + +static gboolean +_validate_and_remove_ipv4_dns (NMSettingIPConfig *setting, + const char *dns, + GError **error) +{ + guint32 ip4_addr; + gboolean ret; + + if (inet_pton (AF_INET, dns, &ip4_addr) < 1) { + g_set_error (error, 1, 0, _("invalid IPv4 address '%s'"), dns); + return FALSE; + } + + ret = nm_setting_ip_config_remove_dns_by_value (setting, dns); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain DNS server '%s'"), dns); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv4_remove_dns, + NM_SETTING_IP_CONFIG, + nm_setting_ip_config_get_num_dns, + nm_setting_ip_config_remove_dns, + _validate_and_remove_ipv4_dns) + +static const char * +nmc_property_ipv4_describe_dns (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv4 addresses of DNS servers.\n\n" + "Example: 8.8.8.8, 8.8.4.4\n"); +} + +/* 'dns-search' */ +static gboolean +nmc_property_ipv4_set_dns_search (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL; + guint i = 0; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + if (!verify_string_list (strv, prop, nmc_util_is_domain, error)) { + g_strfreev (strv); + return FALSE; + } + + while (strv && strv[i]) + nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (setting), strv[i++]); + g_strfreev (strv); + + return TRUE; +} + +static gboolean +_validate_and_remove_ipv4_dns_search (NMSettingIPConfig *setting, + const char *dns_search, + GError **error) +{ + gboolean ret; + + ret = nm_setting_ip_config_remove_dns_search_by_value (setting, dns_search); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain DNS search domain '%s'"), + dns_search); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv4_remove_dns_search, + NM_SETTING_IP_CONFIG, + nm_setting_ip_config_get_num_dns_searches, + nm_setting_ip_config_remove_dns_search, + _validate_and_remove_ipv4_dns_search) + +/* 'addresses' */ +static NMIPAddress * +_parse_ipv4_address (const char *address, GError **error) +{ + return _parse_ip_address (AF_INET, address, error); +} + +static gboolean +nmc_property_ipv4_set_addresses (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + NMIPAddress *ip4addr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, ",", 0); + for (iter = strv; iter && *iter; iter++) { + ip4addr = _parse_ipv4_address (*iter, error); + if (!ip4addr) { + g_strfreev (strv); + return FALSE; + } + nm_setting_ip_config_add_address (NM_SETTING_IP_CONFIG (setting), ip4addr); + nm_ip_address_unref (ip4addr); + } + g_strfreev (strv); + return TRUE; +} + +static gboolean +_validate_and_remove_ipv4_address (NMSettingIPConfig *setting, + const char *address, + GError **error) +{ + NMIPAddress *ip4addr; + gboolean ret; + + ip4addr = _parse_ipv4_address (address, error); + if (!ip4addr) + return FALSE; + + ret = nm_setting_ip_config_remove_address_by_value (setting, ip4addr); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain IP address '%s'"), address); + nm_ip_address_unref (ip4addr); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv4_remove_addresses, + NM_SETTING_IP_CONFIG, + nm_setting_ip_config_get_num_addresses, + nm_setting_ip_config_remove_address, + _validate_and_remove_ipv4_address) + +static const char * +nmc_property_ipv4_describe_addresses (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv4 addresses formatted as:\n" + " ip[/prefix], ip[/prefix],...\n" + "Missing prefix is regarded as prefix of 32.\n\n" + "Example: 192.168.1.5/24, 10.0.0.11/24\n"); +} + +/* 'gateway' */ +static gboolean +nmc_property_ipv4_set_gateway (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + NMIPAddress *ip4addr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (strchr (val, '/')) { + g_set_error (error, 1, 0, + _("invalid gateway address '%s'"), val); + return FALSE; + } + ip4addr = _parse_ipv4_address (val, error); + if (!ip4addr) + return FALSE; + + g_object_set (setting, prop, val, NULL); + nm_ip_address_unref (ip4addr); + return TRUE; +} + +/* 'routes' */ +static NMIPRoute * +_parse_ipv4_route (const char *route, GError **error) +{ + return _parse_ip_route (AF_INET, route, error); +} + +static gboolean +nmc_property_ipv4_set_routes (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + NMIPRoute *ip4route; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, ",", 0); + for (iter = strv; iter && *iter; iter++) { + ip4route = _parse_ipv4_route (*iter, error); + if (!ip4route) { + g_strfreev (strv); + return FALSE; + } + nm_setting_ip_config_add_route (NM_SETTING_IP_CONFIG (setting), ip4route); + nm_ip_route_unref (ip4route); + } + g_strfreev (strv); + return TRUE; +} + +static gboolean +_validate_and_remove_ipv4_route (NMSettingIPConfig *setting, + const char *route, + GError **error) +{ + NMIPRoute *ip4route; + gboolean ret; + + ip4route = _parse_ipv4_route (route, error); + if (!ip4route) + return FALSE; + + ret = nm_setting_ip_config_remove_route_by_value (setting, ip4route); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain route '%s'"), route); + nm_ip_route_unref (ip4route); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv4_remove_routes, + NM_SETTING_IP_CONFIG, + nm_setting_ip_config_get_num_routes, + nm_setting_ip_config_remove_route, + _validate_and_remove_ipv4_route) + +static const char * +nmc_property_ipv4_describe_routes (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv4 routes formatted as:\n" + " ip[/prefix] [next-hop] [metric],...\n\n" + "Missing prefix is regarded as a prefix of 32.\n" + "Missing next-hop is regarded as 0.0.0.0.\n" + "Missing metric means default (NM/kernel will set a default value).\n\n" + "Examples: 192.168.2.0/24 192.168.2.1 3, 10.1.0.0/16 10.0.0.254\n" + " 10.1.2.0/24\n"); +} + +static char * +nmc_property_out2in_routes (const char *out_format) +{ + GRegex *regex; + GString *str; + char **strv; + int i; + + str = g_string_sized_new (128); + regex = g_regex_new ("\\{ dst = ([^/]+)/([^,]+), nh = ([^,]+), mt = ([^ ]+) \\}", 0, 0, NULL); + + strv = g_regex_split (regex, out_format, 0); + for (i = 1; strv && strv[i] && strv[i+1] && strv[i+2] && strv[i+3]; i=i+5) { + g_string_append (str, strv[i]); /* IP */ + g_string_append_c (str, '/'); + g_string_append (str, strv[i+1]); /* prefix */ + g_string_append_c (str, ' '); + g_string_append (str, strv[i+2]); /* next hop */ + g_string_append_c (str, ' '); + g_string_append (str, strv[i+3]); /* metric */ + g_string_append (str, ", "); + } + if (str->len > 0) + g_string_truncate (str, str->len - 2); + + g_strfreev (strv); + g_regex_unref (regex); + + return g_string_free (str, FALSE); +} + +/* --- NM_SETTING_IP6_CONFIG_SETTING_NAME property setter functions --- */ +/* 'method' */ +static const char *ipv6_valid_methods[] = { + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NM_SETTING_IP6_CONFIG_METHOD_SHARED, + NULL +}; + +static gboolean +nmc_property_ipv6_set_method (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + /* Silently accept "static" and convert to "manual" */ + if (val && strlen (val) > 1 && matches (val, "static") == 0) + val = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; + + return check_and_set_string (setting, prop, val, ipv6_valid_methods, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_ipv6_allowed_method, ipv6_valid_methods) + +/* 'dns' */ +static gboolean +nmc_property_ipv6_set_dns (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter, *addr; + struct in6_addr ip6_addr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + for (iter = strv; iter && *iter; iter++) { + addr = g_strstrip (*iter); + if (inet_pton (AF_INET6, addr, &ip6_addr) < 1) { + g_set_error (error, 1, 0, _("invalid IPv6 address '%s'"), addr); + g_strfreev (strv); + return FALSE; + } + nm_setting_ip_config_add_dns (NM_SETTING_IP_CONFIG (setting), addr); + } + g_strfreev (strv); + return TRUE; +} + +static gboolean +_validate_and_remove_ipv6_dns (NMSettingIPConfig *setting, + const char *dns, + GError **error) +{ + struct in6_addr ip6_addr; + gboolean ret; + + if (inet_pton (AF_INET6, dns, &ip6_addr) < 1) { + g_set_error (error, 1, 0, _("invalid IPv6 address '%s'"), dns); + return FALSE; + } + + ret = nm_setting_ip_config_remove_dns_by_value (setting, dns); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain DNS server '%s'"), dns); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv6_remove_dns, + NM_SETTING_IP_CONFIG, + nm_setting_ip_config_get_num_dns, + nm_setting_ip_config_remove_dns, + _validate_and_remove_ipv6_dns) + +static const char * +nmc_property_ipv6_describe_dns (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv6 addresses of DNS servers. If the IPv6 " + "configuration method is 'auto' these DNS servers are appended " + "to those (if any) returned by automatic configuration. DNS " + "servers cannot be used with the 'shared' or 'link-local' IPv6 " + "configuration methods, as there is no upstream network. In " + "all other IPv6 configuration methods, these DNS " + "servers are used as the only DNS servers for this connection.\n\n" + "Example: 2607:f0d0:1002:51::4, 2607:f0d0:1002:51::1\n"); +} + +/* 'dns-search' */ +static gboolean +nmc_property_ipv6_set_dns_search (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL; + guint i = 0; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + if (!verify_string_list (strv, prop, nmc_util_is_domain, error)) { + g_strfreev (strv); + return FALSE; + } + + while (strv && strv[i]) + nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (setting), strv[i++]); + g_strfreev (strv); + + return TRUE; +} + +static gboolean +_validate_and_remove_ipv6_dns_search (NMSettingIPConfig *setting, + const char *dns_search, + GError **error) +{ + gboolean ret; + + ret = nm_setting_ip_config_remove_dns_search_by_value (setting, dns_search); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain DNS search domain '%s'"), + dns_search); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv6_remove_dns_search, + NM_SETTING_IP_CONFIG, + nm_setting_ip_config_get_num_dns_searches, + nm_setting_ip_config_remove_dns_search, + _validate_and_remove_ipv6_dns_search) + +/* 'addresses' */ +static NMIPAddress * +_parse_ipv6_address (const char *address, GError **error) +{ + return _parse_ip_address (AF_INET6, address, error); +} + +static gboolean +nmc_property_ipv6_set_addresses (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + NMIPAddress *ip6addr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, ",", 0); + for (iter = strv; iter && *iter; iter++) { + ip6addr = _parse_ipv6_address (*iter, error); + if (!ip6addr) { + g_strfreev (strv); + return FALSE; + } + nm_setting_ip_config_add_address (NM_SETTING_IP_CONFIG (setting), ip6addr); + nm_ip_address_unref (ip6addr); + } + g_strfreev (strv); + return TRUE; +} + +static gboolean +_validate_and_remove_ipv6_address (NMSettingIPConfig *setting, + const char *address, + GError **error) +{ + NMIPAddress *ip6addr; + gboolean ret; + + ip6addr = _parse_ipv6_address (address, error); + if (!ip6addr) + return FALSE; + + ret = nm_setting_ip_config_remove_address_by_value (setting, ip6addr); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain IP address '%s'"), address); + nm_ip_address_unref (ip6addr); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv6_remove_addresses, + NM_SETTING_IP_CONFIG, + nm_setting_ip_config_get_num_addresses, + nm_setting_ip_config_remove_address, + _validate_and_remove_ipv6_address) + +static const char * +nmc_property_ipv6_describe_addresses (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv6 addresses formatted as:\n" + " ip[/prefix], ip[/prefix],...\n" + "Missing prefix is regarded as prefix of 128.\n\n" + "Example: 2607:f0d0:1002:51::4/64, 1050:0:0:0:5:600:300c:326b\n"); +} + +/* 'gateway' */ +static gboolean +nmc_property_ipv6_set_gateway (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + NMIPAddress *ip6addr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (strchr (val, '/')) { + g_set_error (error, 1, 0, + _("invalid gateway address '%s'"), val); + return FALSE; + } + ip6addr = _parse_ipv6_address (val, error); + if (!ip6addr) + return FALSE; + + g_object_set (setting, prop, val, NULL); + nm_ip_address_unref (ip6addr); + return TRUE; +} + +/* 'routes' */ +static NMIPRoute * +_parse_ipv6_route (const char *route, GError **error) +{ + return _parse_ip_route (AF_INET6, route, error); +} + +static gboolean +nmc_property_ipv6_set_routes (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + NMIPRoute *ip6route; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, ",", 0); + for (iter = strv; iter && *iter; iter++) { + ip6route = _parse_ipv6_route (*iter, error); + if (!ip6route) { + g_strfreev (strv); + return FALSE; + } + nm_setting_ip_config_add_route (NM_SETTING_IP_CONFIG (setting), ip6route); + nm_ip_route_unref (ip6route); + } + g_strfreev (strv); + return TRUE; +} + +static gboolean +_validate_and_remove_ipv6_route (NMSettingIPConfig *setting, + const char *route, + GError **error) +{ + NMIPRoute *ip6route; + gboolean ret; + + ip6route = _parse_ipv6_route (route, error); + if (!ip6route) + return FALSE; + + ret = nm_setting_ip_config_remove_route_by_value (setting, ip6route); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain route '%s'"), route); + nm_ip_route_unref (ip6route); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv6_remove_routes, + NM_SETTING_IP_CONFIG, + nm_setting_ip_config_get_num_routes, + nm_setting_ip_config_remove_route, + _validate_and_remove_ipv6_route) + +static const char * +nmc_property_ipv6_describe_routes (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv6 routes formatted as:\n" + " ip[/prefix] [next-hop] [metric],...\n\n" + "Missing prefix is regarded as a prefix of 128.\n" + "Missing next-hop is regarded as \"::\".\n" + "Missing metric means default (NM/kernel will set a default value).\n\n" + "Examples: 2001:db8:beef:2::/64 2001:db8:beef::2, 2001:db8:beef:3::/64 2001:db8:beef::3 2\n" + " abbe::/64 55\n"); +} + +static gboolean +nmc_property_ipv6_set_ip6_privacy (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + unsigned long val_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_uint (val, FALSE, 0, 0, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a number"), val); + return FALSE; + } + + if ( val_int != NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED + && val_int != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR + && val_int != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR) { + g_set_error (error, 1, 0, _("'%s' is not valid; use 0, 1, or 2"), val); + return FALSE; + } + + g_object_set (setting, prop, val_int, NULL); + return TRUE; +} + +/* --- NM_SETTING_OLPC_MESH_SETTING_NAME property setter functions --- */ +static gboolean +nmc_property_olpc_set_channel (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + unsigned long chan_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_uint (val, TRUE, 1, 13, &chan_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid channel; use <1-13>"), val); + return FALSE; + } + g_object_set (setting, prop, chan_int, NULL); + return TRUE; +} + + +/* --- NM_SETTING_SERIAL_SETTING_NAME property setter functions --- */ +static char * +nmc_property_serial_get_parity (NMSetting *setting) +{ + NMSettingSerial *s_serial = NM_SETTING_SERIAL (setting); + + switch (nm_setting_serial_get_parity (s_serial)) { + case NM_SETTING_SERIAL_PARITY_EVEN: + return g_strdup ("even"); + case NM_SETTING_SERIAL_PARITY_ODD: + return g_strdup ("odd"); + default: + case NM_SETTING_SERIAL_PARITY_NONE: + return g_strdup ("none"); + } +} + +static gboolean +nmc_property_serial_set_parity (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + NMSettingSerialParity parity; + + if (val[0] == 'E' || val[0] == 'e') + parity = NM_SETTING_SERIAL_PARITY_EVEN; + else if (val[0] == 'O' || val[0] == 'o') + parity = NM_SETTING_SERIAL_PARITY_ODD; + else if (val[0] == 'N' || val[0] == 'n') + parity = NM_SETTING_SERIAL_PARITY_NONE; + else { + g_set_error (error, 1, 0, _("'%s' is not valid; use [e, o, n]"), val); + return FALSE; + } + + g_object_set (setting, prop, parity, NULL); + return TRUE; +} + +/* --- NM_SETTING_TEAM_SETTING_NAME property functions --- */ +/* --- NM_SETTING_TEAM_PORT_SETTING_NAME property functions --- */ +static gboolean +nmc_property_team_set_config (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char *json = NULL; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_team_check_config (val, &json, error)) { + return FALSE; + } + g_object_set (setting, prop, json, NULL); + g_free (json); + return TRUE; +} + +static const char * +nmc_property_team_describe_config (NMSetting *setting, const char *prop) +{ + return _("nmcli can accepts both direct JSON configuration data and a file name containing " + "the configuration. In the latter case the file is read and the contents is put " + "into this property.\n\n" + "Examples: set team.config " + "{ \"device\": \"team0\", \"runner\": {\"name\": \"roundrobin\"}, \"ports\": {\"eth1\": {}, \"eth2\": {}} }\n" + " set team.config /etc/my-team.conf\n"); +} + +/* --- NM_SETTING_VLAN_SETTING_NAME property setter functions --- */ +static gboolean +nmc_property_vlan_set_prio_map (NMSetting *setting, + const char *prop, + const char *val, + NMVlanPriorityMap map_type, + GError **error) +{ + char **prio_map, **p; + + prio_map = nmc_vlan_parse_priority_maps (val, map_type, error); + if (!prio_map) + return FALSE; + + for (p = prio_map; p && *p; p++) + nm_setting_vlan_add_priority_str (NM_SETTING_VLAN (setting), map_type, *p); + + g_strfreev (prio_map); + return TRUE; +} + +static gboolean +nmc_property_vlan_remove_prio_map (NMSetting *setting, + const char *prop, + guint32 idx, + NMVlanPriorityMap map_type, + GError **error) +{ + guint32 num; + + num = nm_setting_vlan_get_num_priorities (NM_SETTING_VLAN (setting), map_type); + if (num == 0) { + g_set_error_literal (error, 1, 0, _("no priority to remove")); + return FALSE; + } + if (idx >= num) { + g_set_error (error, 1, 0, _("index '%d' is not in the range of <0-%d>"), + idx, num - 1); + return FALSE; + } + + nm_setting_vlan_remove_priority (NM_SETTING_VLAN (setting), map_type, idx); + return TRUE; +} + +static gboolean +nmc_property_vlan_set_ingress_priority_map (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return nmc_property_vlan_set_prio_map (setting, prop, val, NM_VLAN_INGRESS_MAP, error); +} + +static gboolean +nmc_property_vlan_set_egress_priority_map (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return nmc_property_vlan_set_prio_map (setting, prop, val, NM_VLAN_EGRESS_MAP, error); +} + +static gboolean +nmc_property_vlan_remove_priority_map (NMSetting *setting, + const char *prop, + const char *value, + guint32 idx, + NMVlanPriorityMap map, + GError **error) +{ + /* If value != NULL, remove by value */ + if (value) { + gboolean ret; + char **prio_map; + char *val = g_strdup (value); + + prio_map = nmc_vlan_parse_priority_maps (val, map, error); + if (!prio_map) + return FALSE; + if (prio_map[1]) + g_print (_("Warning: only one mapping at a time is supported; taking the first one (%s)\n"), + prio_map[0]); + ret = nm_setting_vlan_remove_priority_str_by_value (NM_SETTING_VLAN (setting), + map, + prio_map[0]); + + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain mapping '%s'"), prio_map[0]); + g_free (val); + g_strfreev (prio_map); + return ret; + } + + /* Else remove by index */ + return nmc_property_vlan_remove_prio_map (setting, prop, idx, map, error); +} + +static gboolean +nmc_property_vlan_remove_ingress_priority_map (NMSetting *setting, + const char *prop, + const char *value, + guint32 idx, + GError **error) +{ + return nmc_property_vlan_remove_priority_map (setting, + prop, + value, + idx, + NM_VLAN_INGRESS_MAP, + error); +} + +static gboolean +nmc_property_vlan_remove_egress_priority_map (NMSetting *setting, + const char *prop, + const char *value, + guint32 idx, + GError **error) +{ + return nmc_property_vlan_remove_priority_map (setting, + prop, + value, + idx, + NM_VLAN_EGRESS_MAP, + error); +} + +/* --- NM_SETTING_VPN_SETTING_NAME property setter functions --- */ +/* 'data' */ +DEFINE_SETTER_OPTIONS (nmc_property_vpn_set_data, + NM_SETTING_VPN, + NMSettingVpn, + nm_setting_vpn_add_data_item, + NULL, + NULL) +DEFINE_REMOVER_OPTION (nmc_property_vpn_remove_option_data, + NM_SETTING_VPN, + nm_setting_vpn_remove_data_item) + +/* 'secrets' */ +DEFINE_SETTER_OPTIONS (nmc_property_vpn_set_secrets, + NM_SETTING_VPN, + NMSettingVpn, + nm_setting_vpn_add_secret, + NULL, + NULL) +DEFINE_REMOVER_OPTION (nmc_property_vpn_remove_option_secret, + NM_SETTING_VPN, + nm_setting_vpn_remove_secret) + +/* --- NM_SETTING_WIMAX_SETTING_NAME property setter functions --- */ +/* No specific functions */ + +/* --- NM_SETTING_WIRED_SETTING_NAME property setter functions --- */ +#if 0 +/* + * Do not allow setting 'port' and 'duplex' for now. They are not implemented in + * NM core, nor in ifcfg-rh plugin. Enable this when it gets done. + */ +/* 'port' */ +static const char *wired_valid_ports[] = { "tp", "aui", "bnc", "mii", NULL }; + +static gboolean +nmc_property_wired_set_port (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wired_valid_ports, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wired_allowed_port, wired_valid_ports) + +/* 'duplex' */ +static const char *wired_valid_duplexes[] = { "half", "full", NULL }; + +static gboolean +nmc_property_wired_set_duplex (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wired_valid_duplexes, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wired_allowed_duplex, wired_valid_duplexes) +#endif + +/* 'mac-address-blacklist' */ +DEFINE_SETTER_MAC_BLACKLIST (nmc_property_wired_set_mac_address_blacklist, + NM_SETTING_WIRED, + nm_setting_wired_add_mac_blacklist_item) + +static gboolean +_validate_and_remove_wired_mac_blacklist_item (NMSettingWired *setting, + const char *mac, + GError **error) +{ + gboolean ret; + guint8 buf[32]; + + if (!nm_utils_hwaddr_aton (mac, buf, ETH_ALEN)) { + g_set_error (error, 1, 0, _("'%s' is not a valid MAC address"), mac); + return FALSE; + } + + ret = nm_setting_wired_remove_mac_blacklist_item_by_value (setting, mac); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain MAC address '%s'"), mac); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_wired_remove_mac_address_blacklist, + NM_SETTING_WIRED, + nm_setting_wired_get_num_mac_blacklist_items, + nm_setting_wired_remove_mac_blacklist_item, + _validate_and_remove_wired_mac_blacklist_item) + +/* 's390-subchannels' */ +static gboolean +nmc_property_wired_set_s390_subchannels (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL; + int len; + + strv = nmc_strsplit_set (val, " ,\t", 0); + len = g_strv_length (strv); + if (len != 2 && len != 3) { + g_set_error (error, 1, 0, _("'%s' is not valid; 2 or 3 strings should be provided"), + val); + g_strfreev (strv); + return FALSE; + } + + g_object_set (setting, prop, strv, NULL); + g_strfreev (strv); + return TRUE; +} + +static const char * +nmc_property_wired_describe_s390_subchannels (NMSetting *setting, const char *prop) +{ + return _("Enter a list of subchannels (comma or space separated).\n\n" + "Example: 0.0.0e20 0.0.0e21 0.0.0e22\n"); +} + +/* 's390-nettype' */ +static const char *wired_valid_s390_nettypes[] = { "qeth", "lcs", "ctc", NULL }; + +static gboolean +nmc_property_wired_set_s390_nettype (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wired_valid_s390_nettypes, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wired_allowed_s390_nettype, wired_valid_s390_nettypes) + +/* 's390-options' */ +DEFINE_SETTER_OPTIONS (nmc_property_wired_set_s390_options, + NM_SETTING_WIRED, + NMSettingWired, + nm_setting_wired_add_s390_option, + nm_setting_wired_get_valid_s390_options, + NULL) +DEFINE_REMOVER_OPTION (nmc_property_wired_remove_option_s390_options, + NM_SETTING_WIRED, + nm_setting_wired_remove_s390_option) + +static const char * +nmc_property_wired_allowed_s390_options (NMSetting *setting, const char *prop) +{ + const char **valid_options; + static char *allowed_vals = NULL; + + if (G_UNLIKELY (allowed_vals == NULL)) { + valid_options = nm_setting_wired_get_valid_s390_options (NM_SETTING_WIRED (setting)); + allowed_vals = g_strjoinv (", ", (char **) valid_options); + } + return allowed_vals; +} + +static const char * +nmc_property_wired_describe_s390_options (NMSetting *setting, const char *prop) +{ + static char *desc = NULL; + const char **valid_options; + char *options_str; + + if (G_UNLIKELY (desc == NULL)) { + valid_options = nm_setting_wired_get_valid_s390_options (NM_SETTING_WIRED (setting)); + options_str = g_strjoinv (", ", (char **) valid_options); + + desc = g_strdup_printf (_("Enter a list of S/390 options formatted as:\n" + " option = <value>, option = <value>,...\n" + "Valid options are: %s\n"), + options_str); + g_free (options_str); + } + return desc; +} + +/* --- NM_SETTING_WIRELESS_SETTING_NAME property setter functions --- */ +/* 'mode' */ +static const char *wifi_valid_modes[] = { + NM_SETTING_WIRELESS_MODE_INFRA, + NM_SETTING_WIRELESS_MODE_ADHOC, + NM_SETTING_WIRELESS_MODE_AP, + NULL +}; + +static gboolean +nmc_property_wifi_set_mode (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wifi_valid_modes, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_allowed_mode, wifi_valid_modes) + +/* 'band' */ +static const char *wifi_valid_bands[] = { "a", "bg", NULL }; + +static gboolean +nmc_property_wifi_set_band (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wifi_valid_bands, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_allowed_band, wifi_valid_bands) + +/* 'channel' */ +static gboolean +nmc_property_wifi_set_channel (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + unsigned long chan_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_uint (val, FALSE, 0, 0, &chan_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid channel"), val); + return FALSE; + } + + if ( !nm_utils_wifi_is_channel_valid (chan_int, "a") + && !nm_utils_wifi_is_channel_valid (chan_int, "bg")) { + g_set_error (error, 1, 0, _("'%ld' is not a valid channel"), chan_int); + return FALSE; + } + + g_object_set (setting, prop, chan_int, NULL); + return TRUE; +} + +/* 'mac-address-blacklist' */ +DEFINE_SETTER_MAC_BLACKLIST (nmc_property_wireless_set_mac_address_blacklist, + NM_SETTING_WIRELESS, + nm_setting_wireless_add_mac_blacklist_item) + +static gboolean +_validate_and_remove_wifi_mac_blacklist_item (NMSettingWireless *setting, + const char *mac, + GError **error) +{ + gboolean ret; + guint8 buf[32]; + + if (!nm_utils_hwaddr_aton (mac, buf, ETH_ALEN)) { + g_set_error (error, 1, 0, _("'%s' is not a valid MAC address"), mac); + return FALSE; + } + + ret = nm_setting_wireless_remove_mac_blacklist_item_by_value (setting, mac); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain MAC address '%s'"), mac); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_wireless_remove_mac_address_blacklist, + NM_SETTING_WIRELESS, + nm_setting_wireless_get_num_mac_blacklist_items, + nm_setting_wireless_remove_mac_blacklist_item, + _validate_and_remove_wifi_mac_blacklist_item) + +/* --- NM_SETTING_WIRELESS_SECURITY_SETTING_NAME property setter functions --- */ +/* 'key-mgmt' */ +static const char *wifi_sec_valid_key_mgmts[] = { "none", "ieee8021x", "wpa-none", "wpa-psk", "wpa-eap", NULL }; + +static gboolean +nmc_property_wifi_sec_set_key_mgmt (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wifi_sec_valid_key_mgmts, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_sec_allowed_key_mgmt, wifi_sec_valid_key_mgmts) + +/* 'auth-alg' */ +static const char *wifi_sec_valid_auth_algs[] = { "open", "shared", "leap", NULL }; + +static gboolean +nmc_property_wifi_sec_set_auth_alg (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wifi_sec_valid_auth_algs, error); +} + +DEFINE_SETTER_STR_LIST_MULTI (check_and_add_wifi_sec_proto, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_add_proto) +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_sec_allowed_auth_alg, wifi_sec_valid_auth_algs) + +/* 'proto' */ +static const char *wifi_sec_valid_protos[] = { "wpa", "rsn", NULL }; + +static gboolean +nmc_property_wifi_sec_set_proto (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_add_wifi_sec_proto (setting, prop, val, wifi_sec_valid_protos, error); +} + +static gboolean +_validate_and_remove_wifi_sec_proto (NMSettingWirelessSecurity *setting, + const char *proto, + GError **error) +{ + gboolean ret; + const char *valid; + + valid = nmc_string_is_valid (proto, wifi_sec_valid_protos, error); + if (!valid) + return FALSE; + + ret = nm_setting_wireless_security_remove_proto_by_value (setting, proto); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain protocol '%s'"), proto); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_wifi_sec_remove_proto, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_get_num_protos, + nm_setting_wireless_security_remove_proto, + _validate_and_remove_wifi_sec_proto) + +DEFINE_SETTER_STR_LIST_MULTI (check_and_add_wifi_sec_pairwise, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_add_pairwise) +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_sec_allowed_proto, wifi_sec_valid_protos) + +/* 'pairwise' */ +static const char *wifi_sec_valid_pairwises[] = { "tkip", "ccmp", NULL }; + +static gboolean +nmc_property_wifi_sec_set_pairwise (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_add_wifi_sec_pairwise (setting, prop, val, wifi_sec_valid_pairwises, error); +} + +static gboolean +_validate_and_remove_wifi_sec_pairwise (NMSettingWirelessSecurity *setting, + const char *pairwise, + GError **error) +{ + gboolean ret; + const char *valid; + + valid = nmc_string_is_valid (pairwise, wifi_sec_valid_pairwises, error); + if (!valid) + return FALSE; + + ret = nm_setting_wireless_security_remove_pairwise_by_value (setting, pairwise); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain protocol '%s'"), pairwise); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_wifi_sec_remove_pairwise, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_get_num_pairwise, + nm_setting_wireless_security_remove_pairwise, + _validate_and_remove_wifi_sec_pairwise) + +DEFINE_SETTER_STR_LIST_MULTI (check_and_add_wifi_sec_group, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_add_group) +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_sec_allowed_pairwise, wifi_sec_valid_pairwises) + +/* 'group' */ +static const char *wifi_sec_valid_groups[] = { "wep40", "wep104", "tkip", "ccmp", NULL }; + +static gboolean +nmc_property_wifi_sec_set_group (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_add_wifi_sec_group (setting, prop, val, wifi_sec_valid_groups, error); +} + +static gboolean +_validate_and_remove_wifi_sec_group (NMSettingWirelessSecurity *setting, + const char *group, + GError **error) +{ + gboolean ret; + const char *valid; + + valid = nmc_string_is_valid (group, wifi_sec_valid_groups, error); + if (!valid) + return FALSE; + + ret = nm_setting_wireless_security_remove_group_by_value (setting, group); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain protocol '%s'"), group); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_wifi_sec_remove_group, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_get_num_groups, + nm_setting_wireless_security_remove_group, + _validate_and_remove_wifi_sec_group) +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_sec_allowed_group, wifi_sec_valid_groups) + +/* 'wep-key' */ +static gboolean +nmc_property_wifi_set_wep_key (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + NMWepKeyType guessed_type = NM_WEP_KEY_TYPE_UNKNOWN; + NMWepKeyType type; + guint32 prev_idx, idx; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* Get currently set type */ + type = nm_setting_wireless_security_get_wep_key_type (NM_SETTING_WIRELESS_SECURITY (setting)); + + /* Guess key type */ + if (nm_utils_wep_key_valid (val, NM_WEP_KEY_TYPE_KEY)) + guessed_type = NM_WEP_KEY_TYPE_KEY; + else if (nm_utils_wep_key_valid (val, NM_WEP_KEY_TYPE_PASSPHRASE)) + guessed_type = NM_WEP_KEY_TYPE_PASSPHRASE; + + if (guessed_type == NM_WEP_KEY_TYPE_UNKNOWN) { + g_set_error (error, 1, 0, _("'%s' is not valid"), val); + return FALSE; + } + + if (type != NM_WEP_KEY_TYPE_UNKNOWN && type != guessed_type) { + if (nm_utils_wep_key_valid (val, type)) + guessed_type = type; + else { + g_set_error (error, 1, 0, + _("'%s' not compatible with %s '%s', please change the key or set the right %s first."), + val, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, wep_key_type_to_string (type), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE); + return FALSE; + } + } + prev_idx = nm_setting_wireless_security_get_wep_tx_keyidx (NM_SETTING_WIRELESS_SECURITY (setting)); + idx = prop[strlen (prop) - 1] - '0'; + g_print (_("WEP key is guessed to be of '%s'\n"), wep_key_type_to_string (guessed_type)); + if (idx != prev_idx) + g_print (_("WEP key index set to '%d'\n"), idx); + + g_object_set (setting, prop, val, NULL); + g_object_set (setting, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, guessed_type, NULL); + if (idx != prev_idx) + g_object_set (setting, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, idx, NULL); + return TRUE; +} + +/* 'wep-key-type' */ +static gboolean +nmc_property_wifi_set_wep_key_type (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + unsigned long type_int; + const char *valid_wep_types[] = { "unknown", "key", "passphrase", NULL }; + const char *type_str = NULL; + const char *key0, *key1,* key2, *key3; + NMWepKeyType type = NM_WEP_KEY_TYPE_UNKNOWN; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_uint (val, TRUE, 0, 2, &type_int)) { + if (!(type_str = nmc_string_is_valid (val, valid_wep_types, NULL))) { + g_set_error (error, 1, 0, _("'%s' not among [0 (unknown), 1 (key), 2 (passphrase)]"), val); + return FALSE; + } + if (type_str == valid_wep_types[1]) + type = NM_WEP_KEY_TYPE_KEY; + else if (type_str == valid_wep_types[2]) + type = NM_WEP_KEY_TYPE_PASSPHRASE; + } else + type = (NMWepKeyType) type_int; + + /* Check type compatibility with set keys */ + key0 = nm_setting_wireless_security_get_wep_key (NM_SETTING_WIRELESS_SECURITY (setting), 0); + key1 = nm_setting_wireless_security_get_wep_key (NM_SETTING_WIRELESS_SECURITY (setting), 1); + key2 = nm_setting_wireless_security_get_wep_key (NM_SETTING_WIRELESS_SECURITY (setting), 2); + key3 = nm_setting_wireless_security_get_wep_key (NM_SETTING_WIRELESS_SECURITY (setting), 3); + if (key0 && !nm_utils_wep_key_valid (key0, type)) + g_print (_("Warning: '%s' is not compatible with '%s' type, please change or delete the key.\n"), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, wep_key_type_to_string (type)); + if (key1 && !nm_utils_wep_key_valid (key1, type)) + g_print (_("Warning: '%s' is not compatible with '%s' type, please change or delete the key.\n"), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, wep_key_type_to_string (type)); + if (key2 && !nm_utils_wep_key_valid (key2, type)) + g_print (_("Warning: '%s' is not compatible with '%s' type, please change or delete the key.\n"), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, wep_key_type_to_string (type)); + if (key3 && !nm_utils_wep_key_valid (key3, type)) + g_print (_("Warning: '%s' is not compatible with '%s' type, please change or delete the key.\n"), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, wep_key_type_to_string (type)); + + g_object_set (setting, prop, type, NULL); + return TRUE; +} + +static const char * +nmc_property_wifi_describe_wep_key_type (NMSetting *setting, const char *prop) +{ + static char *desc = NULL; + + if (G_UNLIKELY (desc == NULL)) { + desc = g_strdup_printf (_("Enter the type of WEP keys. The accepted values are: " + "0 or unknown, 1 or key, and 2 or passphrase.\n")); + } + return desc; +} + +/* 'psk' */ +static gboolean +nmc_property_wifi_set_psk (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nm_utils_wpa_psk_valid (val)) { + g_set_error (error, 1, 0, _("'%s' is not a valid PSK"), val); + return FALSE; + } + g_object_set (setting, prop, val, NULL); + return TRUE; +} + +#define DCB_ALL_FLAGS (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + +/* DCB stuff */ +static gboolean +nmc_property_dcb_set_flags (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; + long int t; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* Check for overall hex numeric value */ + if (nmc_string_to_int_base (val, 0, TRUE, 0, DCB_ALL_FLAGS, &t)) + flags = (guint) t; + else { + /* Check for individual flag numbers */ + strv = nmc_strsplit_set (val, " \t,", 0); + for (iter = strv; iter && *iter; iter++) { + if (!nmc_string_to_int_base (*iter, 0, TRUE, 0, DCB_ALL_FLAGS, &t)) + t = -1; + + if ( g_ascii_strcasecmp (*iter, "enable") == 0 + || g_ascii_strcasecmp (*iter, "enabled") == 0 + || t == NM_SETTING_DCB_FLAG_ENABLE) + flags |= NM_SETTING_DCB_FLAG_ENABLE; + else if ( g_ascii_strcasecmp (*iter, "advertise") == 0 + || t == NM_SETTING_DCB_FLAG_ADVERTISE) + flags |= NM_SETTING_DCB_FLAG_ADVERTISE; + else if ( g_ascii_strcasecmp (*iter, "willing") == 0 + || t == NM_SETTING_DCB_FLAG_WILLING) + flags |= NM_SETTING_DCB_FLAG_WILLING; + else if ( g_ascii_strcasecmp (*iter, "disable") == 0 + || g_ascii_strcasecmp (*iter, "disabled") == 0 + || t == 0) { + /* pass */ + } else { + g_set_error (error, 1, 0, _("'%s' is not a valid DCB flag"), *iter); + return FALSE; + } + } + g_strfreev (strv); + } + + /* Validate the number according to the property spec */ + if (!validate_uint (setting, prop, (guint) flags, error)) + return FALSE; + + g_object_set (setting, prop, (guint) flags, NULL); + return TRUE; +} + +static gboolean +nmc_property_dcb_set_priority (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + long int priority = 0; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_int (val, FALSE, -1, 7, &priority)) { + g_set_error (error, 1, 0, _("'%s' is not a DCB app priority"), val); + return FALSE; + } + + /* Validate the number according to the property spec */ + if (!validate_int (setting, prop, (gint) priority, error)) + return FALSE; + + g_object_set (setting, prop, (gint) priority, NULL); + return TRUE; +} + +static gboolean +dcb_parse_uint_array (const char *val, + guint max, + guint other, + guint *out_array, + GError **error) +{ + char **items, **iter; + guint i = 0; + + g_return_val_if_fail (out_array != NULL, FALSE); + + items = g_strsplit_set (val, ",", -1); + if (g_strv_length (items) != 8) { + g_set_error_literal (error, 1, 0, _("must contain 8 comma-separated numbers")); + goto error; + } + + for (iter = items; iter && *iter; iter++) { + long int num = 0; + gboolean success; + + *iter = g_strstrip (*iter); + success = nmc_string_to_int_base (*iter, 10, TRUE, 0, other ? other : max, &num); + + /* If number is greater than 'max' it must equal 'other' */ + if (success && other && (num > max) && (num != other)) + success = FALSE; + + if (!success) { + if (other) { + g_set_error (error, 1, 0, _("'%s' not a number between 0 and %u (inclusive) or %u"), + *iter, max, other); + } else { + g_set_error (error, 1, 0, _("'%s' not a number between 0 and %u (inclusive)"), + *iter, max); + } + goto error; + } + out_array[i++] = (guint) num; + } + + return TRUE; + +error: + g_strfreev (items); + return FALSE; +} + +static void +dcb_check_feature_enabled (NMSettingDcb *s_dcb, const char *flags_prop) +{ + NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; + + g_object_get (s_dcb, flags_prop, &flags, NULL); + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) + g_print (_("Warning: changes will have no effect until '%s' includes 1 (enabled)\n\n"), flags_prop); +} + +static gboolean +nmc_property_dcb_set_pfc (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + guint i = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!dcb_parse_uint_array (val, 1, 0, nums, error)) + return FALSE; + + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_flow_control (NM_SETTING_DCB (setting), i, !!nums[i]); + + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS); + return TRUE; +} + +static gboolean +nmc_property_dcb_set_pg_group_id (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + guint i = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!dcb_parse_uint_array (val, 7, 15, nums, error)) + return FALSE; + + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_group_id (NM_SETTING_DCB (setting), i, nums[i]); + + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS); + return TRUE; +} + +static gboolean +nmc_property_dcb_set_pg_group_bandwidth (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + guint i = 0, sum = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!dcb_parse_uint_array (val, 100, 0, nums, error)) + return FALSE; + + for (i = 0; i < 8; i++) + sum += nums[i]; + if (sum != 100) { + g_set_error_literal (error, 1, 0, _("bandwidth percentages must total 100%%")); + return FALSE; + } + + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_group_bandwidth (NM_SETTING_DCB (setting), i, nums[i]); + + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS); + return TRUE; +} + +static gboolean +nmc_property_dcb_set_pg_bandwidth (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + guint i = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!dcb_parse_uint_array (val, 100, 0, nums, error)) + return FALSE; + + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_bandwidth (NM_SETTING_DCB (setting), i, nums[i]); + + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS); + return TRUE; +} + +static gboolean +nmc_property_dcb_set_pg_strict (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + guint i = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!dcb_parse_uint_array (val, 1, 0, nums, error)) + return FALSE; + + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_strict_bandwidth (NM_SETTING_DCB (setting), i, !!nums[i]); + + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS); + return TRUE; +} + +static gboolean +nmc_property_dcb_set_pg_traffic_class (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + guint i = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!dcb_parse_uint_array (val, 7, 0, nums, error)) + return FALSE; + + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_traffic_class (NM_SETTING_DCB (setting), i, nums[i]); + + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS); + return TRUE; +} + +/* 'app-fcoe-mode' */ +static const char *_dcb_valid_fcoe_modes[] = { NM_SETTING_DCB_FCOE_MODE_FABRIC, + NM_SETTING_DCB_FCOE_MODE_VN2VN, + NULL }; + +static gboolean +nmc_property_dcb_set_app_fcoe_mode (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _dcb_valid_fcoe_modes, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_dcb_allowed_app_fcoe_modes, _dcb_valid_fcoe_modes) + +/*----------------------------------------------------------------------------*/ + +static void +nmc_add_prop_funcs (char *key, + NmcPropertyGetFunc get_func, + NmcPropertySetFunc set_func, + NmcPropertyRemoveFunc remove_func, + NmcPropertyDescribeFunc describe_func, + NmcPropertyValuesFunc values_func, + NmcPropertyOut2InFunc out2in_func) +{ + NmcPropertyFuncs *item = g_malloc0 (sizeof (NmcPropertyFuncs)); + item->get_func = get_func; + item->set_func = set_func; + item->remove_func = remove_func; + item->describe_func = describe_func; + item->values_func = values_func; + item->out2in_func = out2in_func; + + g_hash_table_insert (nmc_properties, key, item); +} + +/* concatenate setting name and property name */ +#define GLUE(A,B) (g_strconcat ((NM_SETTING_##A##_SETTING_NAME),(NM_SETTING_##A##_##B), NULL)) +#define GLUE_IP(A,B) (g_strconcat ((NM_SETTING_IP##A##_CONFIG_SETTING_NAME),(NM_SETTING_IP_CONFIG_##B), NULL)) + +void +nmc_properties_init (void) +{ + if (G_LIKELY (nmc_properties)) + return; + + /* create properties hash table */ + nmc_properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + /* Add editable properties for NM_SETTING_802_1X_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (802_1X, EAP), + nmc_property_802_1X_get_eap, + nmc_property_802_1X_set_eap, + nmc_property_802_1X_remove_eap, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, IDENTITY), + nmc_property_802_1X_get_identity, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, ANONYMOUS_IDENTITY), + nmc_property_802_1X_get_anonymous_identity, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PAC_FILE), + nmc_property_802_1X_get_pac_file, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, CA_CERT), + nmc_property_802_1X_get_ca_cert, + nmc_property_802_1X_set_ca_cert, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, CA_PATH), + nmc_property_802_1X_get_ca_path, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, SUBJECT_MATCH), + nmc_property_802_1X_get_subject_match, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, ALTSUBJECT_MATCHES), + nmc_property_802_1X_get_altsubject_matches, + nmc_property_802_1X_set_altsubject_matches, + nmc_property_802_1X_remove_altsubject_matches, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, CLIENT_CERT), + nmc_property_802_1X_get_client_cert, + nmc_property_802_1X_set_client_cert, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE1_PEAPVER), + nmc_property_802_1X_get_phase1_peapver, + nmc_property_802_1X_set_phase1_peapver, + NULL, + NULL, + nmc_property_802_1X_allowed_phase1_peapver, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE1_PEAPLABEL), + nmc_property_802_1X_get_phase1_peaplabel, + nmc_property_802_1X_set_phase1_peaplabel, + NULL, + NULL, + nmc_property_802_1X_allowed_phase1_peaplabel, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE1_FAST_PROVISIONING), + nmc_property_802_1X_get_phase1_fast_provisioning, + nmc_property_802_1X_set_phase1_fast_provisioning, + NULL, + NULL, + nmc_property_802_1X_allowed_phase1_fast_provisioning, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_AUTH), + nmc_property_802_1X_get_phase2_auth, + nmc_property_802_1X_set_phase2_auth, + NULL, + NULL, + nmc_property_802_1X_allowed_phase2_auth, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_AUTHEAP), + nmc_property_802_1X_get_phase2_autheap, + nmc_property_802_1X_set_phase2_autheap, + NULL, + NULL, + nmc_property_802_1X_allowed_phase2_autheap, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_CA_CERT), + nmc_property_802_1X_get_phase2_ca_cert, + nmc_property_802_1X_set_phase2_ca_cert, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_CA_PATH), + nmc_property_802_1X_get_phase2_ca_path, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_SUBJECT_MATCH), + nmc_property_802_1X_get_phase2_subject_match, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_ALTSUBJECT_MATCHES), + nmc_property_802_1X_get_phase2_altsubject_matches, + nmc_property_802_1X_set_phase2_altsubject_matches, + nmc_property_802_1X_remove_phase2_altsubject_matches, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_CLIENT_CERT), + nmc_property_802_1X_get_phase2_client_cert, + nmc_property_802_1X_set_phase2_client_cert, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PASSWORD), + nmc_property_802_1X_get_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PASSWORD_FLAGS), + nmc_property_802_1X_get_password_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (802_1X, PASSWORD_RAW), + nmc_property_802_1X_get_password_raw, + nmc_property_802_1X_set_password_raw, + NULL, + nmc_property_802_1X_describe_password_raw, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PASSWORD_RAW_FLAGS), + nmc_property_802_1X_get_password_raw_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (802_1X, PRIVATE_KEY), + nmc_property_802_1X_get_private_key, + nmc_property_802_1X_set_private_key, + NULL, + nmc_property_802_1X_describe_private_key, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PRIVATE_KEY_PASSWORD), + nmc_property_802_1X_get_private_key_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PRIVATE_KEY_PASSWORD_FLAGS), + nmc_property_802_1X_get_private_key_password_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_PRIVATE_KEY), + nmc_property_802_1X_get_phase2_private_key, + nmc_property_802_1X_set_phase2_private_key, + NULL, + nmc_property_802_1X_describe_private_key, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_PRIVATE_KEY_PASSWORD), + nmc_property_802_1X_get_phase2_private_key_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_PRIVATE_KEY_PASSWORD_FLAGS), + nmc_property_802_1X_get_phase2_private_key_password_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (802_1X, PIN), + nmc_property_802_1X_get_pin, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PIN_FLAGS), + nmc_property_802_1X_get_pin_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (802_1X, SYSTEM_CA_CERTS), + nmc_property_802_1X_get_system_ca_certs, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_ADSL_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (ADSL, USERNAME), + nmc_property_adsl_get_username, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (ADSL, PASSWORD), + nmc_property_adsl_get_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (ADSL, PASSWORD_FLAGS), + nmc_property_adsl_get_password_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (ADSL, PROTOCOL), + nmc_property_adsl_get_protocol, + nmc_property_adsl_set_protocol, + NULL, + NULL, + nmc_property_adsl_allowed_protocol, + NULL); + nmc_add_prop_funcs (GLUE (ADSL, ENCAPSULATION), + nmc_property_adsl_get_encapsulation, + nmc_property_adsl_set_encapsulation, + NULL, + NULL, + nmc_property_adsl_allowed_encapsulation, + NULL); + nmc_add_prop_funcs (GLUE (ADSL, VPI), + nmc_property_adsl_get_vpi, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (ADSL, VCI), + nmc_property_adsl_get_vci, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_BLUETOOTH_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (BLUETOOTH, BDADDR), + nmc_property_bluetooth_get_bdaddr, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BLUETOOTH, TYPE), + nmc_property_bluetooth_get_type, + nmc_property_bluetooth_set_type, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_BOND_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (BOND, OPTIONS), + nmc_property_bond_get_options, + nmc_property_bond_set_options, + nmc_property_bond_remove_option_options, + nmc_property_bond_describe_options, + nmc_property_bond_allowed_options, + NULL); + + /* Add editable properties for NM_SETTING_BRIDGE_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (BRIDGE, MAC_ADDRESS), + nmc_property_bridge_get_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, STP), + nmc_property_bridge_get_stp, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, PRIORITY), + nmc_property_bridge_get_priority, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, FORWARD_DELAY), + nmc_property_bridge_get_forward_delay, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, HELLO_TIME), + nmc_property_bridge_get_hello_time, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, MAX_AGE), + nmc_property_bridge_get_max_age, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, AGEING_TIME), + nmc_property_bridge_get_ageing_time, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_BRIDGE_PORT_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (BRIDGE_PORT, PRIORITY), + nmc_property_bridge_port_get_priority, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE_PORT, PATH_COST), + nmc_property_bridge_port_get_path_cost, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE_PORT, HAIRPIN_MODE), + nmc_property_bridge_port_get_hairpin_mode, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_CDMA_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (CDMA, NUMBER), + nmc_property_cdma_get_number, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CDMA, USERNAME), + nmc_property_cdma_get_username, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CDMA, PASSWORD), + nmc_property_cdma_get_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CDMA, PASSWORD_FLAGS), + nmc_property_cdma_get_password_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + + /* Add editable properties for NM_SETTING_CONNECTION_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (CONNECTION, ID), + nmc_property_connection_get_id, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, UUID), + nmc_property_connection_get_uuid, + NULL, /* forbid setting/removing UUID */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, INTERFACE_NAME), + nmc_property_connection_get_interface_name, + nmc_property_set_ifname, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, TYPE), + nmc_property_connection_get_type, + NULL, /* read-only */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, AUTOCONNECT), + nmc_property_connection_get_autoconnect, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, AUTOCONNECT_PRIORITY), + nmc_property_connection_get_autoconnect_priority, + nmc_property_set_int, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, TIMESTAMP), + nmc_property_connection_get_timestamp, + NULL, /* read-only */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, READ_ONLY), + nmc_property_connection_get_read_only, + NULL, /* 'read-only' is read-only :-) */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, PERMISSIONS), + nmc_property_connection_get_permissions, + nmc_property_connection_set_permissions, + nmc_property_connection_remove_permissions, + nmc_property_connection_describe_permissions, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, ZONE), + nmc_property_connection_get_zone, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, MASTER), + nmc_property_connection_get_master, + nmc_property_con_set_master, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, SLAVE_TYPE), + nmc_property_connection_get_slave_type, + nmc_property_con_set_slave_type, + NULL, + NULL, + nmc_property_con_allowed_slave_type, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, SECONDARIES), + nmc_property_connection_get_secondaries, + nmc_property_connection_set_secondaries, + nmc_property_connection_remove_secondaries, + nmc_property_connection_describe_secondaries, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, GATEWAY_PING_TIMEOUT), + nmc_property_connection_get_gateway_ping_timeout, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_DCB_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (DCB, APP_FCOE_FLAGS), + nmc_property_dcb_get_app_fcoe_flags, + nmc_property_dcb_set_flags, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_FCOE_MODE), + nmc_property_dcb_get_app_fcoe_mode, + nmc_property_dcb_set_app_fcoe_mode, + NULL, + NULL, + nmc_property_dcb_allowed_app_fcoe_modes, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_FCOE_PRIORITY), + nmc_property_dcb_get_app_fcoe_priority, + nmc_property_dcb_set_priority, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_ISCSI_FLAGS), + nmc_property_dcb_get_app_iscsi_flags, + nmc_property_dcb_set_flags, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_ISCSI_PRIORITY), + nmc_property_dcb_get_app_iscsi_priority, + nmc_property_dcb_set_priority, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_FIP_FLAGS), + nmc_property_dcb_get_app_fip_flags, + nmc_property_dcb_set_flags, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_FIP_PRIORITY), + nmc_property_dcb_get_app_fip_priority, + nmc_property_dcb_set_priority, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_FLOW_CONTROL_FLAGS), + nmc_property_dcb_get_pfc_flags, + nmc_property_dcb_set_flags, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_FLOW_CONTROL), + nmc_property_dcb_get_pfc, + nmc_property_dcb_set_pfc, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_GROUP_FLAGS), + nmc_property_dcb_get_pg_flags, + nmc_property_dcb_set_flags, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_GROUP_ID), + nmc_property_dcb_get_pg_group_id, + nmc_property_dcb_set_pg_group_id, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_GROUP_BANDWIDTH), + nmc_property_dcb_get_pg_group_bandwidth, + nmc_property_dcb_set_pg_group_bandwidth, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_BANDWIDTH), + nmc_property_dcb_get_pg_bandwidth, + nmc_property_dcb_set_pg_bandwidth, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_STRICT_BANDWIDTH), + nmc_property_dcb_get_pg_strict, + nmc_property_dcb_set_pg_strict, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_TRAFFIC_CLASS), + nmc_property_dcb_get_pg_traffic_class, + nmc_property_dcb_set_pg_traffic_class, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_GSM_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (GSM, NUMBER), + nmc_property_gsm_get_number, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, USERNAME), + nmc_property_gsm_get_username, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, PASSWORD), + nmc_property_gsm_get_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, PASSWORD_FLAGS), + nmc_property_gsm_get_password_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (GSM, APN), + nmc_property_gsm_get_apn, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, NETWORK_ID), + nmc_property_gsm_get_network_id, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, PIN), + nmc_property_gsm_get_pin, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, PIN_FLAGS), + nmc_property_gsm_get_pin_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (GSM, HOME_ONLY), + nmc_property_gsm_get_home_only, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_INFINIBAND_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (INFINIBAND, MAC_ADDRESS), + nmc_property_ib_get_mac_address, + nmc_property_ib_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (INFINIBAND, MTU), + nmc_property_ib_get_mtu, + nmc_property_set_mtu, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (INFINIBAND, TRANSPORT_MODE), + nmc_property_ib_get_transport_mode, + nmc_property_ib_set_transport_mode, + NULL, + NULL, + nmc_property_ib_allowed_transport_mode, + NULL); + nmc_add_prop_funcs (GLUE (INFINIBAND, P_KEY), + nmc_property_ib_get_p_key, + nmc_property_ib_set_p_key, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (INFINIBAND, PARENT), + nmc_property_ib_get_parent, + nmc_property_set_ifname, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_IP4_CONFIG_SETTING_NAME */ + nmc_add_prop_funcs (GLUE_IP (4, METHOD), + nmc_property_ipv4_get_method, + nmc_property_ipv4_set_method, + NULL, + NULL, + nmc_property_ipv4_allowed_method, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, DNS), + nmc_property_ipv4_get_dns, + nmc_property_ipv4_set_dns, + nmc_property_ipv4_remove_dns, + nmc_property_ipv4_describe_dns, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, DNS_SEARCH), + nmc_property_ipv4_get_dns_search, + nmc_property_ipv4_set_dns_search, + nmc_property_ipv4_remove_dns_search, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, ADDRESSES), + nmc_property_ip_get_addresses, + nmc_property_ipv4_set_addresses, + nmc_property_ipv4_remove_addresses, + nmc_property_ipv4_describe_addresses, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, GATEWAY), + nmc_property_ipv4_get_gateway, + nmc_property_ipv4_set_gateway, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, ROUTES), + nmc_property_ipv4_get_routes, + nmc_property_ipv4_set_routes, + nmc_property_ipv4_remove_routes, + nmc_property_ipv4_describe_routes, + NULL, + nmc_property_out2in_routes); + nmc_add_prop_funcs (GLUE_IP (4, ROUTE_METRIC), + nmc_property_ipv4_get_route_metric, + nmc_property_set_int64, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, IGNORE_AUTO_ROUTES), + nmc_property_ipv4_get_ignore_auto_routes, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, IGNORE_AUTO_DNS), + nmc_property_ipv4_get_ignore_auto_dns, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, DHCP_CLIENT_ID), + nmc_property_ipv4_get_dhcp_client_id, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, DHCP_SEND_HOSTNAME), + nmc_property_ipv4_get_dhcp_send_hostname, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, DHCP_HOSTNAME), + nmc_property_ipv4_get_dhcp_hostname, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, NEVER_DEFAULT), + nmc_property_ipv4_get_never_default, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (4, MAY_FAIL), + nmc_property_ipv4_get_may_fail, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_IP6_CONFIG_SETTING_NAME */ + nmc_add_prop_funcs (GLUE_IP (6, METHOD), + nmc_property_ipv6_get_method, + nmc_property_ipv6_set_method, + NULL, + NULL, + nmc_property_ipv6_allowed_method, + NULL); + nmc_add_prop_funcs (GLUE_IP (6, DNS), + nmc_property_ipv6_get_dns, + nmc_property_ipv6_set_dns, + nmc_property_ipv6_remove_dns, + nmc_property_ipv6_describe_dns, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (6, DNS_SEARCH), + nmc_property_ipv6_get_dns_search, + nmc_property_ipv6_set_dns_search, + nmc_property_ipv6_remove_dns_search, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (6, ADDRESSES), + nmc_property_ip_get_addresses, + nmc_property_ipv6_set_addresses, + nmc_property_ipv6_remove_addresses, + nmc_property_ipv6_describe_addresses, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (6, GATEWAY), + nmc_property_ipv6_get_gateway, + nmc_property_ipv6_set_gateway, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (6, ROUTES), + nmc_property_ipv6_get_routes, + nmc_property_ipv6_set_routes, + nmc_property_ipv6_remove_routes, + nmc_property_ipv6_describe_routes, + NULL, + nmc_property_out2in_routes); + nmc_add_prop_funcs (GLUE_IP (6, ROUTE_METRIC), + nmc_property_ipv6_get_route_metric, + nmc_property_set_int64, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (6, IGNORE_AUTO_ROUTES), + nmc_property_ipv6_get_ignore_auto_routes, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (6, IGNORE_AUTO_DNS), + nmc_property_ipv6_get_ignore_auto_dns, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (6, NEVER_DEFAULT), + nmc_property_ipv6_get_never_default, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (6, MAY_FAIL), + nmc_property_ipv6_get_may_fail, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, IP6_PRIVACY), + nmc_property_ipv6_get_ip6_privacy, + nmc_property_ipv6_set_ip6_privacy, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE_IP (6, DHCP_SEND_HOSTNAME), + nmc_property_ipv6_get_dhcp_send_hostname, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE_IP (6, DHCP_HOSTNAME), + nmc_property_ipv6_get_dhcp_hostname, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_OLPC_MESH_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (OLPC_MESH, SSID), + nmc_property_olpc_get_ssid, + nmc_property_set_ssid, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (OLPC_MESH, CHANNEL), + nmc_property_olpc_get_channel, + nmc_property_olpc_set_channel, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (OLPC_MESH, DHCP_ANYCAST_ADDRESS), + nmc_property_olpc_get_anycast_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_PPP_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (PPP, NOAUTH), + nmc_property_ppp_get_noauth, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REFUSE_EAP), + nmc_property_ppp_get_refuse_eap, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REFUSE_PAP), + nmc_property_ppp_get_refuse_pap, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REFUSE_CHAP), + nmc_property_ppp_get_refuse_chap, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REFUSE_MSCHAP), + nmc_property_ppp_get_refuse_mschap, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REFUSE_MSCHAPV2), + nmc_property_ppp_get_refuse_mschapv2, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, NOBSDCOMP), + nmc_property_ppp_get_nobsdcomp, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, NODEFLATE), + nmc_property_ppp_get_nodeflate, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, NO_VJ_COMP), + nmc_property_ppp_get_no_vj_comp, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REQUIRE_MPPE), + nmc_property_ppp_get_require_mppe, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REQUIRE_MPPE_128), + nmc_property_ppp_get_require_mppe_128, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, MPPE_STATEFUL), + nmc_property_ppp_get_mppe_stateful, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, CRTSCTS), + nmc_property_ppp_get_crtscts, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, BAUD), + nmc_property_ppp_get_baud, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, MRU), + nmc_property_ppp_get_mru, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, MTU), + nmc_property_ppp_get_mtu, + nmc_property_set_mtu, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, LCP_ECHO_FAILURE), + nmc_property_ppp_get_lcp_echo_failure, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, LCP_ECHO_INTERVAL), + nmc_property_ppp_get_lcp_echo_interval, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_PPPOE_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (PPPOE, SERVICE), + nmc_property_pppoe_get_service, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPPOE, USERNAME), + nmc_property_pppoe_get_username, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPPOE, PASSWORD), + nmc_property_pppoe_get_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPPOE, PASSWORD_FLAGS), + nmc_property_pppoe_get_password_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + + /* Add editable properties for NM_SETTING_SERIAL_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (SERIAL, BAUD), + nmc_property_serial_get_baud, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (SERIAL, BITS), + nmc_property_serial_get_bits, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (SERIAL, PARITY), + nmc_property_serial_get_parity, + nmc_property_serial_set_parity, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (SERIAL, STOPBITS), + nmc_property_serial_get_stopbits, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (SERIAL, SEND_DELAY), + nmc_property_serial_get_send_delay, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_TEAM_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (TEAM, CONFIG), + nmc_property_team_get_config, + nmc_property_team_set_config, + NULL, + nmc_property_team_describe_config, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_TEAM_PORT_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (TEAM_PORT, CONFIG), + nmc_property_team_port_get_config, + nmc_property_team_set_config, + NULL, + nmc_property_team_describe_config, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_VLAN_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (VLAN, PARENT), + nmc_property_vlan_get_parent, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VLAN, ID), + nmc_property_vlan_get_id, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VLAN, FLAGS), + nmc_property_vlan_get_flags, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VLAN, INGRESS_PRIORITY_MAP), + nmc_property_vlan_get_ingress_priority_map, + nmc_property_vlan_set_ingress_priority_map, + nmc_property_vlan_remove_ingress_priority_map, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VLAN, EGRESS_PRIORITY_MAP), + nmc_property_vlan_get_egress_priority_map, + nmc_property_vlan_set_egress_priority_map, + nmc_property_vlan_remove_egress_priority_map, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_VPN_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (VPN, SERVICE_TYPE), + nmc_property_vpn_get_service_type, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VPN, USER_NAME), + nmc_property_vpn_get_user_name, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VPN, DATA), + nmc_property_vpn_get_data, + nmc_property_vpn_set_data, + nmc_property_vpn_remove_option_data, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VPN, SECRETS), + nmc_property_vpn_get_secrets, + nmc_property_vpn_set_secrets, + nmc_property_vpn_remove_option_secret, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VPN, PERSISTENT), + nmc_property_vpn_get_persistent, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_WIMAX_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (WIMAX, NETWORK_NAME), + nmc_property_wimax_get_network_name, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIMAX, MAC_ADDRESS), + nmc_property_wimax_get_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_WIRED_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (WIRED, PORT), + nmc_property_wired_get_port, + NULL, /*nmc_property_wired_set_port,*/ + NULL, + NULL, + NULL, /*nmc_property_wired_allowed_port,*/ + NULL); + nmc_add_prop_funcs (GLUE (WIRED, SPEED), + nmc_property_wired_get_speed, + NULL, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, DUPLEX), + nmc_property_wired_get_duplex, + NULL, /*nmc_property_wired_set_duplex,*/ + NULL, + NULL, + NULL, + NULL); /*nmc_property_wired_allowed_duplex);*/ + nmc_add_prop_funcs (GLUE (WIRED, AUTO_NEGOTIATE), + nmc_property_wired_get_auto_negotiate, + NULL, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, MAC_ADDRESS), + nmc_property_wired_get_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, CLONED_MAC_ADDRESS), + nmc_property_wired_get_cloned_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, MAC_ADDRESS_BLACKLIST), + nmc_property_wired_get_mac_address_blacklist, + nmc_property_wired_set_mac_address_blacklist, + nmc_property_wired_remove_mac_address_blacklist, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, MTU), + nmc_property_wired_get_mtu, + nmc_property_set_mtu, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, S390_SUBCHANNELS), + nmc_property_wired_get_s390_subchannels, + nmc_property_wired_set_s390_subchannels, + NULL, + nmc_property_wired_describe_s390_subchannels, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, S390_NETTYPE), + nmc_property_wired_get_s390_nettype, + nmc_property_wired_set_s390_nettype, + NULL, + NULL, + nmc_property_wired_allowed_s390_nettype, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, S390_OPTIONS), + nmc_property_wired_get_s390_options, + nmc_property_wired_set_s390_options, + nmc_property_wired_remove_option_s390_options, + nmc_property_wired_describe_s390_options, + nmc_property_wired_allowed_s390_options, + NULL); + + /* Add editable properties for NM_SETTING_WIRELESS_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (WIRELESS, SSID), + nmc_property_wireless_get_ssid, + nmc_property_set_ssid, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, MODE), + nmc_property_wireless_get_mode, + nmc_property_wifi_set_mode, + NULL, + NULL, + nmc_property_wifi_allowed_mode, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, BAND), + nmc_property_wireless_get_band, + nmc_property_wifi_set_band, + NULL, + NULL, + nmc_property_wifi_allowed_band, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, CHANNEL), + nmc_property_wireless_get_channel, + nmc_property_wifi_set_channel, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, BSSID), + nmc_property_wireless_get_bssid, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + /* + * Do not allow setting 'rate' and 'tx-power'. They are not implemented in + * NM core, nor in ifcfg-rh plugin (thus not preserved over re-reading). + */ + nmc_add_prop_funcs (GLUE (WIRELESS, RATE), + nmc_property_wireless_get_rate, + NULL, /* editing rate disabled */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, TX_POWER), + nmc_property_wireless_get_tx_power, + NULL, /* editing tx-power disabled */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, MAC_ADDRESS), + nmc_property_wireless_get_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, CLONED_MAC_ADDRESS), + nmc_property_wireless_get_cloned_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, MAC_ADDRESS_BLACKLIST), + nmc_property_wireless_get_mac_address_blacklist, + nmc_property_wireless_set_mac_address_blacklist, + nmc_property_wireless_remove_mac_address_blacklist, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, SEEN_BSSIDS), + nmc_property_wireless_get_seen_bssids, + NULL, /* read-only */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, MTU), + nmc_property_wireless_get_mtu, + nmc_property_set_mtu, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, HIDDEN), + nmc_property_wireless_get_hidden, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_WIRELESS_SECURITY_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, KEY_MGMT), + nmc_property_wifi_sec_get_key_mgmt, + nmc_property_wifi_sec_set_key_mgmt, + NULL, + NULL, + nmc_property_wifi_sec_allowed_key_mgmt, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_TX_KEYIDX), + nmc_property_wifi_sec_get_wep_tx_keyidx, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, AUTH_ALG), + nmc_property_wifi_sec_get_auth_alg, + nmc_property_wifi_sec_set_auth_alg, + NULL, + NULL, + nmc_property_wifi_sec_allowed_auth_alg, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, PROTO), + nmc_property_wifi_sec_get_proto, + nmc_property_wifi_sec_set_proto, + nmc_property_wifi_sec_remove_proto, + NULL, + nmc_property_wifi_sec_allowed_proto, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, PAIRWISE), + nmc_property_wifi_sec_get_pairwise, + nmc_property_wifi_sec_set_pairwise, + nmc_property_wifi_sec_remove_pairwise, + NULL, + nmc_property_wifi_sec_allowed_pairwise, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, GROUP), + nmc_property_wifi_sec_get_group, + nmc_property_wifi_sec_set_group, + nmc_property_wifi_sec_remove_group, + NULL, + nmc_property_wifi_sec_allowed_group, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, LEAP_USERNAME), + nmc_property_wifi_sec_get_leap_username, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY0), + nmc_property_wifi_sec_get_wep_key0, + nmc_property_wifi_set_wep_key, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY1), + nmc_property_wifi_sec_get_wep_key1, + nmc_property_wifi_set_wep_key, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY2), + nmc_property_wifi_sec_get_wep_key2, + nmc_property_wifi_set_wep_key, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY3), + nmc_property_wifi_sec_get_wep_key3, + nmc_property_wifi_set_wep_key, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY_FLAGS), + nmc_property_wifi_sec_get_wep_key_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY_TYPE), + nmc_property_wifi_sec_get_wep_key_type, + nmc_property_wifi_set_wep_key_type, + NULL, + nmc_property_wifi_describe_wep_key_type, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, PSK), + nmc_property_wifi_sec_get_psk, + nmc_property_wifi_set_psk, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, PSK_FLAGS), + nmc_property_wifi_sec_get_psk_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, LEAP_PASSWORD), + nmc_property_wifi_sec_get_leap_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, LEAP_PASSWORD_FLAGS), + nmc_property_wifi_sec_get_leap_password_flags, + nmc_property_set_secret_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); +} + +void +nmc_properties_cleanup () +{ + if (nmc_properties) + g_hash_table_destroy (nmc_properties); +} + +static const NmcPropertyFuncs * +nmc_properties_find (const char *s_name, const char *p_name) +{ + NmcPropertyFuncs *item = NULL; + char *key; + + /* FIXME: maybe it's better to init it globally */ + nmc_properties_init (); + + key = g_strdup_printf ("%s%s", s_name, p_name); + item = (NmcPropertyFuncs *) g_hash_table_lookup (nmc_properties, key); + g_free (key); + + return item; +} + +static char * +get_property_val (NMSetting *setting, const char *prop, gboolean convert, GError **error) +{ + const NmcPropertyFuncs *item; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->get_func) { + char *prop_val = item->get_func (setting); + if (convert && item->out2in_func) { + char *converted = item->out2in_func (prop_val); + g_free (prop_val); + return converted; + } else + return prop_val; + } + + g_set_error_literal (error, 1, 0, _("don't know how to get the property value")); + return NULL; +} + +/* + * Generic function for getting property value. + * + * Gets property value as a string by calling specialized functions. + * + * Returns: current property value. The caller must free the returned string. + */ +char * +nmc_setting_get_property (NMSetting *setting, const char *prop, GError **error) +{ + return get_property_val (setting, prop, FALSE, error); +} + +/* + * The same as nmc_setting_get_property(), but in addition converts + * usual output format into a simpler one, used as input in the editor. + */ +char * +nmc_setting_get_property_out2in (NMSetting *setting, const char *prop, GError **error) +{ + return get_property_val (setting, prop, TRUE, error); +} + +/* + * Generic function for setting property value. + * + * Sets property=val in setting by calling specialized functions. + * If val is NULL then default property value is set. + * + * Returns: TRUE on success; FALSE on failure and sets error + */ +gboolean +nmc_setting_set_property (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + const NmcPropertyFuncs *item; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->set_func) { + if (!val) { + /* No value argument sets default value */ + nmc_property_set_default_value (setting, prop); + return TRUE; + } + return item->set_func (setting, prop, val, error); + } + + g_set_error_literal (error, 1, 0, _("the property can't be changed")); + return FALSE; +} + +void +nmc_property_set_default_value (NMSetting *setting, const char *prop) +{ + GValue value = G_VALUE_INIT; + GParamSpec *param_spec; + + param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + if (param_spec) { + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (param_spec)); + g_param_value_set_default (param_spec, &value); + g_object_set_property (G_OBJECT (setting), prop, &value); + } +} + +/* + * Generic function for reseting (single value) properties. + * + * The function resets the property value to the default one. It respects + * nmcli restrictions for changing properties. So if 'set_func' is NULL, + * reseting the value is denied. + * + * Returns: TRUE on success; FALSE on failure and sets error + */ +gboolean +nmc_setting_reset_property (NMSetting *setting, const char *prop, GError **error) +{ + const NmcPropertyFuncs *item; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->set_func) { + nmc_property_set_default_value (setting, prop); + return TRUE; + } + g_set_error_literal (error, 1, 0, _("the property can't be changed")); + return FALSE; +} + +/* + * Generic function for removing items for collection-type properties. + * + * If 'option' is not NULL, it tries to remove it, otherwise 'idx' is used. + * For single-value properties (not having specialized remove function) this + * function does nothing and just returns TRUE. + * + * Returns: TRUE on success; FALSE on failure and sets error + */ +gboolean +nmc_setting_remove_property_option (NMSetting *setting, + const char *prop, + const char *option, + guint32 idx, + GError **error) +{ + const NmcPropertyFuncs *item; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->remove_func) + return item->remove_func (setting, prop, option, idx, error); + + return TRUE; +} + +/* + * Get valid property names for a setting. + * + * Returns: string array with the properties or NULL on failure. + * The returned value should be freed with g_strfreev() + */ +char ** +nmc_setting_get_valid_properties (NMSetting *setting) +{ + char **valid_props = NULL; + GParamSpec **props, **iter; + guint num; + int i; + + /* Iterate through properties */ + i = 0; + props = g_object_class_list_properties (G_OBJECT_GET_CLASS (G_OBJECT (setting)), &num); + valid_props = g_malloc0 (sizeof (char*) * (num + 1)); + for (iter = props; iter && *iter; iter++) { + const char *key_name = g_param_spec_get_name (*iter); + + /* Add all properties except for "name" that is non-editable */ + if (g_strcmp0 (key_name, "name") != 0) + valid_props[i++] = g_strdup (key_name); + } + valid_props[i] = NULL; + + return valid_props; +} + +/* + * Return allowed values for 'prop' as a string. + */ +const char * +nmc_setting_get_property_allowed_values (NMSetting *setting, const char *prop) +{ + + const NmcPropertyFuncs *item; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->values_func) + return item->values_func (setting, prop); + + return NULL; +} + +#if defined (BUILD_SETTING_DOCS) || defined (HAVE_SETTING_DOCS) +#include "settings-docs.c" +#else +#define nmc_setting_get_property_doc(setting, prop) _("(not available)") +#endif + +/* + * Create a description string for a property. + * + * It returns a description got from property documentation, concatenated with + * nmcli specific description (if it exists). + * + * Returns: property description or NULL on failure. The caller must free the string. + */ +char * +nmc_setting_get_property_desc (NMSetting *setting, const char *prop) +{ + const NmcPropertyFuncs *item; + const char *setting_desc = NULL; + const char *setting_desc_title = ""; + const char *nmcli_desc = NULL; + const char *nmcli_desc_title = ""; + const char *nmcli_nl = ""; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + + setting_desc = nmc_setting_get_property_doc (setting, prop); + if (setting_desc) + setting_desc_title = _("[NM property description]"); + + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->describe_func) { + nmcli_desc = item->describe_func (setting, prop); + nmcli_desc_title = _("[nmcli specific description]"); + nmcli_nl = "\n"; + } + + return g_strdup_printf ("%s\n%s\n%s%s%s%s", + setting_desc_title, + setting_desc ? setting_desc : "", + nmcli_nl, nmcli_desc_title, nmcli_nl, + nmcli_desc ? nmcli_desc : ""); +} + +/* + * Gets setting:prop property value and returns it in 'value'. + * Caller is responsible for freeing the GValue resources using g_value_unset() + */ +gboolean +nmc_property_get_gvalue (NMSetting *setting, const char *prop, GValue *value) +{ + GParamSpec *param_spec; + + param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + if (param_spec) { + memset (value, 0, sizeof (GValue)); + g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (param_spec)); + g_object_get_property (G_OBJECT (setting), prop, value); + return TRUE; + } + return FALSE; +} + +/* + * Sets setting:prop property value from 'value'. + */ +gboolean +nmc_property_set_gvalue (NMSetting *setting, const char *prop, GValue *value) +{ + GParamSpec *param_spec; + + param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + if (param_spec && G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (param_spec)) { + g_object_set_property (G_OBJECT (setting), prop, value); + return TRUE; + } + return FALSE; +} + +/*----------------------------------------------------------------------------*/ + +#define GET_SECRET(show, setting, func) \ + (show ? func (setting) : g_strdup (_("<hidden>"))) + +static gboolean +setting_connection_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingConnection *s_con = NM_SETTING_CONNECTION (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (s_con), FALSE); + + tmpl = nmc_fields_setting_connection; + tmpl_len = sizeof (nmc_fields_setting_connection); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_CONNECTION_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_connection_get_id (setting)); + set_val_str (arr, 2, nmc_property_connection_get_uuid (setting)); + set_val_str (arr, 3, nmc_property_connection_get_interface_name (setting)); + set_val_str (arr, 4, nmc_property_connection_get_type (setting)); + set_val_str (arr, 5, nmc_property_connection_get_autoconnect (setting)); + set_val_str (arr, 6, nmc_property_connection_get_autoconnect_priority (setting)); + set_val_str (arr, 7, nmc_property_connection_get_timestamp (setting)); + set_val_str (arr, 8, nmc_property_connection_get_read_only (setting)); + set_val_str (arr, 9, nmc_property_connection_get_permissions (setting)); + set_val_str (arr, 10, nmc_property_connection_get_zone (setting)); + set_val_str (arr, 11, nmc_property_connection_get_master (setting)); + set_val_str (arr, 12, nmc_property_connection_get_slave_type (setting)); + set_val_str (arr, 13, nmc_property_connection_get_secondaries (setting)); + set_val_str (arr, 14, nmc_property_connection_get_gateway_ping_timeout (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_wired_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingWired *s_wired = NM_SETTING_WIRED (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_WIRED (s_wired), FALSE); + + tmpl = nmc_fields_setting_wired; + tmpl_len = sizeof (nmc_fields_setting_wired); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_WIRED_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_wired_get_port (setting)); + set_val_str (arr, 2, nmc_property_wired_get_speed (setting)); + set_val_str (arr, 3, nmc_property_wired_get_duplex (setting)); + set_val_str (arr, 4, nmc_property_wired_get_auto_negotiate (setting)); + set_val_str (arr, 5, nmc_property_wired_get_mac_address (setting)); + set_val_str (arr, 6, nmc_property_wired_get_cloned_mac_address (setting)); + set_val_str (arr, 7, nmc_property_wired_get_mac_address_blacklist (setting)); + set_val_str (arr, 8, nmc_property_wired_get_mtu (setting)); + set_val_str (arr, 9, nmc_property_wired_get_s390_subchannels (setting)); + set_val_str (arr, 10, nmc_property_wired_get_s390_nettype (setting)); + set_val_str (arr, 11, nmc_property_wired_get_s390_options (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_802_1X_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSetting8021x *s_8021x = NM_SETTING_802_1X (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (s_8021x), FALSE); + + tmpl = nmc_fields_setting_8021X; + tmpl_len = sizeof (nmc_fields_setting_8021X); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_802_1X_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_802_1X_get_eap (setting)); + set_val_str (arr, 2, nmc_property_802_1X_get_identity (setting)); + set_val_str (arr, 3, nmc_property_802_1X_get_anonymous_identity (setting)); + set_val_str (arr, 4, nmc_property_802_1X_get_pac_file (setting)); + set_val_str (arr, 5, nmc_property_802_1X_get_ca_cert (setting)); + set_val_str (arr, 6, nmc_property_802_1X_get_ca_path (setting)); + set_val_str (arr, 7, nmc_property_802_1X_get_subject_match (setting)); + set_val_str (arr, 8, nmc_property_802_1X_get_altsubject_matches (setting)); + set_val_str (arr, 9, nmc_property_802_1X_get_client_cert (setting)); + set_val_str (arr, 10, nmc_property_802_1X_get_phase1_peapver (setting)); + set_val_str (arr, 11, nmc_property_802_1X_get_phase1_peaplabel (setting)); + set_val_str (arr, 12, nmc_property_802_1X_get_phase1_fast_provisioning (setting)); + set_val_str (arr, 13, nmc_property_802_1X_get_phase2_auth (setting)); + set_val_str (arr, 14, nmc_property_802_1X_get_phase2_autheap (setting)); + set_val_str (arr, 15, nmc_property_802_1X_get_phase2_ca_cert (setting)); + set_val_str (arr, 16, nmc_property_802_1X_get_phase2_ca_path (setting)); + set_val_str (arr, 17, nmc_property_802_1X_get_phase2_subject_match (setting)); + set_val_str (arr, 18, nmc_property_802_1X_get_phase2_altsubject_matches (setting)); + set_val_str (arr, 19, nmc_property_802_1X_get_phase2_client_cert (setting)); + set_val_str (arr, 20, GET_SECRET (secrets, setting, nmc_property_802_1X_get_password)); + set_val_str (arr, 21, nmc_property_802_1X_get_password_flags (setting)); + set_val_str (arr, 22, GET_SECRET (secrets, setting, nmc_property_802_1X_get_password_raw)); + set_val_str (arr, 23, nmc_property_802_1X_get_password_raw_flags (setting)); + set_val_str (arr, 24, nmc_property_802_1X_get_private_key (setting)); + set_val_str (arr, 25, GET_SECRET (secrets, setting, nmc_property_802_1X_get_private_key_password)); + set_val_str (arr, 26, nmc_property_802_1X_get_private_key_password_flags (setting)); + set_val_str (arr, 27, nmc_property_802_1X_get_phase2_private_key (setting)); + set_val_str (arr, 28, GET_SECRET (secrets, setting, nmc_property_802_1X_get_phase2_private_key_password)); + set_val_str (arr, 29, nmc_property_802_1X_get_phase2_private_key_password_flags (setting)); + set_val_str (arr, 30, GET_SECRET (secrets, setting, nmc_property_802_1X_get_pin)); + set_val_str (arr, 31, nmc_property_802_1X_get_pin_flags (setting)); + set_val_str (arr, 32, nmc_property_802_1X_get_system_ca_certs (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_wireless_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingWireless *s_wireless = NM_SETTING_WIRELESS (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (s_wireless), FALSE); + + tmpl = nmc_fields_setting_wireless; + tmpl_len = sizeof (nmc_fields_setting_wireless); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_WIRELESS_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_wireless_get_ssid (setting)); + set_val_str (arr, 2, nmc_property_wireless_get_mode (setting)); + set_val_str (arr, 3, nmc_property_wireless_get_band (setting)); + set_val_str (arr, 4, nmc_property_wireless_get_channel (setting)); + set_val_str (arr, 5, nmc_property_wireless_get_bssid (setting)); + set_val_str (arr, 6, nmc_property_wireless_get_rate (setting)); + set_val_str (arr, 7, nmc_property_wireless_get_tx_power (setting)); + set_val_str (arr, 8, nmc_property_wireless_get_mac_address (setting)); + set_val_str (arr, 9, nmc_property_wireless_get_cloned_mac_address (setting)); + set_val_str (arr, 10, nmc_property_wireless_get_mac_address_blacklist (setting)); + set_val_str (arr, 11, nmc_property_wireless_get_mtu (setting)); + set_val_str (arr, 12, nmc_property_wireless_get_seen_bssids (setting)); + set_val_str (arr, 13, nmc_property_wireless_get_hidden (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_wireless_security_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (s_wireless_sec), FALSE); + + tmpl = nmc_fields_setting_wireless_security; + tmpl_len = sizeof (nmc_fields_setting_wireless_security); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_WIRELESS_SECURITY_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_wifi_sec_get_key_mgmt (setting)); + set_val_str (arr, 2, nmc_property_wifi_sec_get_wep_tx_keyidx (setting)); + set_val_str (arr, 3, nmc_property_wifi_sec_get_auth_alg (setting)); + set_val_str (arr, 4, nmc_property_wifi_sec_get_proto (setting)); + set_val_str (arr, 5, nmc_property_wifi_sec_get_pairwise (setting)); + set_val_str (arr, 6, nmc_property_wifi_sec_get_group (setting)); + set_val_str (arr, 7, nmc_property_wifi_sec_get_leap_username (setting)); + set_val_str (arr, 8, GET_SECRET (secrets, setting, nmc_property_wifi_sec_get_wep_key0)); + set_val_str (arr, 9, GET_SECRET (secrets, setting, nmc_property_wifi_sec_get_wep_key1)); + set_val_str (arr, 10, GET_SECRET (secrets, setting, nmc_property_wifi_sec_get_wep_key2)); + set_val_str (arr, 11, GET_SECRET (secrets, setting, nmc_property_wifi_sec_get_wep_key3)); + set_val_str (arr, 12, nmc_property_wifi_sec_get_wep_key_flags (setting)); + set_val_str (arr, 13, nmc_property_wifi_sec_get_wep_key_type (setting)); + set_val_str (arr, 14, GET_SECRET (secrets, setting, nmc_property_wifi_sec_get_psk)); + set_val_str (arr, 15, nmc_property_wifi_sec_get_psk_flags (setting)); + set_val_str (arr, 16, GET_SECRET (secrets, setting, nmc_property_wifi_sec_get_leap_password)); + set_val_str (arr, 17, nmc_property_wifi_sec_get_leap_password_flags (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_ip4_config_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingIPConfig *s_ip4 = NM_SETTING_IP_CONFIG (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (s_ip4), FALSE); + + tmpl = nmc_fields_setting_ip4_config; + tmpl_len = sizeof (nmc_fields_setting_ip4_config); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_IP4_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_ipv4_get_method (setting)); + set_val_str (arr, 2, nmc_property_ipv4_get_dns (setting)); + set_val_str (arr, 3, nmc_property_ipv4_get_dns_search (setting)); + set_val_str (arr, 4, nmc_property_ip_get_addresses (setting)); + set_val_str (arr, 5, nmc_property_ipv4_get_gateway (setting)); + set_val_str (arr, 6, nmc_property_ipv4_get_routes (setting)); + set_val_str (arr, 7, nmc_property_ipv4_get_route_metric (setting)); + set_val_str (arr, 8, nmc_property_ipv4_get_ignore_auto_routes (setting)); + set_val_str (arr, 9, nmc_property_ipv4_get_ignore_auto_dns (setting)); + set_val_str (arr, 10, nmc_property_ipv4_get_dhcp_client_id (setting)); + set_val_str (arr, 11, nmc_property_ipv4_get_dhcp_send_hostname (setting)); + set_val_str (arr, 12, nmc_property_ipv4_get_dhcp_hostname (setting)); + set_val_str (arr, 13, nmc_property_ipv4_get_never_default (setting)); + set_val_str (arr, 14, nmc_property_ipv4_get_may_fail (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_ip6_config_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingIPConfig *s_ip6 = NM_SETTING_IP_CONFIG (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (s_ip6), FALSE); + + tmpl = nmc_fields_setting_ip6_config; + tmpl_len = sizeof (nmc_fields_setting_ip6_config); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_IP6_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_ipv6_get_method (setting)); + set_val_str (arr, 2, nmc_property_ipv6_get_dns (setting)); + set_val_str (arr, 3, nmc_property_ipv6_get_dns_search (setting)); + set_val_str (arr, 4, nmc_property_ip_get_addresses (setting)); + set_val_str (arr, 5, nmc_property_ipv6_get_gateway (setting)); + set_val_str (arr, 6, nmc_property_ipv6_get_routes (setting)); + set_val_str (arr, 7, nmc_property_ipv6_get_route_metric (setting)); + set_val_str (arr, 8, nmc_property_ipv6_get_ignore_auto_routes (setting)); + set_val_str (arr, 9, nmc_property_ipv6_get_ignore_auto_dns (setting)); + set_val_str (arr, 10, nmc_property_ipv6_get_never_default (setting)); + set_val_str (arr, 11, nmc_property_ipv6_get_may_fail (setting)); + set_val_str (arr, 12, nmc_property_ipv6_get_ip6_privacy (setting)); + set_val_str (arr, 13, nmc_property_ipv6_get_dhcp_send_hostname (setting)); + set_val_str (arr, 14, nmc_property_ipv6_get_dhcp_hostname (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_serial_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingSerial *s_serial = NM_SETTING_SERIAL (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_SERIAL (s_serial), FALSE); + + tmpl = nmc_fields_setting_serial; + tmpl_len = sizeof (nmc_fields_setting_serial); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_SERIAL_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_serial_get_baud (setting)); + set_val_str (arr, 2, nmc_property_serial_get_bits (setting)); + set_val_str (arr, 3, nmc_property_serial_get_parity (setting)); + set_val_str (arr, 4, nmc_property_serial_get_stopbits (setting)); + set_val_str (arr, 5, nmc_property_serial_get_send_delay (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_ppp_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingPpp *s_ppp = NM_SETTING_PPP (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_PPP (s_ppp), FALSE); + + tmpl = nmc_fields_setting_ppp; + tmpl_len = sizeof (nmc_fields_setting_ppp); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_PPP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_ppp_get_noauth (setting)); + set_val_str (arr, 2, nmc_property_ppp_get_refuse_eap (setting)); + set_val_str (arr, 3, nmc_property_ppp_get_refuse_pap (setting)); + set_val_str (arr, 4, nmc_property_ppp_get_refuse_chap (setting)); + set_val_str (arr, 5, nmc_property_ppp_get_refuse_mschap (setting)); + set_val_str (arr, 6, nmc_property_ppp_get_refuse_mschapv2 (setting)); + set_val_str (arr, 7, nmc_property_ppp_get_nobsdcomp (setting)); + set_val_str (arr, 8, nmc_property_ppp_get_nodeflate (setting)); + set_val_str (arr, 9, nmc_property_ppp_get_no_vj_comp (setting)); + set_val_str (arr, 10, nmc_property_ppp_get_require_mppe (setting)); + set_val_str (arr, 11, nmc_property_ppp_get_require_mppe_128 (setting)); + set_val_str (arr, 12, nmc_property_ppp_get_mppe_stateful (setting)); + set_val_str (arr, 13, nmc_property_ppp_get_crtscts (setting)); + set_val_str (arr, 14, nmc_property_ppp_get_baud (setting)); + set_val_str (arr, 15, nmc_property_ppp_get_mru (setting)); + set_val_str (arr, 16, nmc_property_ppp_get_mtu (setting)); + set_val_str (arr, 17, nmc_property_ppp_get_lcp_echo_failure (setting)); + set_val_str (arr, 18, nmc_property_ppp_get_lcp_echo_interval (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_pppoe_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingPppoe *s_pppoe = NM_SETTING_PPPOE (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_PPPOE (s_pppoe), FALSE); + + tmpl = nmc_fields_setting_pppoe; + tmpl_len = sizeof (nmc_fields_setting_pppoe); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_PPPOE_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_pppoe_get_service (setting)); + set_val_str (arr, 2, nmc_property_pppoe_get_username (setting)); + set_val_str (arr, 3, GET_SECRET (secrets, setting, nmc_property_pppoe_get_password)); + set_val_str (arr, 4, nmc_property_pppoe_get_password_flags (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_gsm_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingGsm *s_gsm = NM_SETTING_GSM (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_GSM (s_gsm), FALSE); + + tmpl = nmc_fields_setting_gsm; + tmpl_len = sizeof (nmc_fields_setting_gsm); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_GSM_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_gsm_get_number (setting)); + set_val_str (arr, 2, nmc_property_gsm_get_username (setting)); + set_val_str (arr, 3, GET_SECRET (secrets, setting, nmc_property_gsm_get_password)); + set_val_str (arr, 4, nmc_property_gsm_get_password_flags (setting)); + set_val_str (arr, 5, nmc_property_gsm_get_apn (setting)); + set_val_str (arr, 6, nmc_property_gsm_get_network_id (setting)); + set_val_str (arr, 7, GET_SECRET (secrets, setting, nmc_property_gsm_get_pin)); + set_val_str (arr, 8, nmc_property_gsm_get_pin_flags (setting)); + set_val_str (arr, 9, nmc_property_gsm_get_home_only (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_cdma_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingCdma *s_cdma = NM_SETTING_CDMA (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_CDMA (s_cdma), FALSE); + + tmpl = nmc_fields_setting_cdma; + tmpl_len = sizeof (nmc_fields_setting_cdma); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_CDMA_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_cdma_get_number (setting)); + set_val_str (arr, 2, nmc_property_cdma_get_username (setting)); + set_val_str (arr, 3, GET_SECRET (secrets, setting, nmc_property_cdma_get_password)); + set_val_str (arr, 4, nmc_property_cdma_get_password_flags (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_bluetooth_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingBluetooth *s_bluetooth = NM_SETTING_BLUETOOTH (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_BLUETOOTH (s_bluetooth), FALSE); + + tmpl = nmc_fields_setting_bluetooth; + tmpl_len = sizeof (nmc_fields_setting_bluetooth); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_BLUETOOTH_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_bluetooth_get_bdaddr (setting)); + set_val_str (arr, 2, nmc_property_bluetooth_get_type (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_olpc_mesh_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingOlpcMesh *s_olpc_mesh = NM_SETTING_OLPC_MESH (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (s_olpc_mesh), FALSE); + + tmpl = nmc_fields_setting_olpc_mesh; + tmpl_len = sizeof (nmc_fields_setting_olpc_mesh); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_OLPC_MESH_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_olpc_get_ssid (setting)); + set_val_str (arr, 2, nmc_property_olpc_get_channel (setting)); + set_val_str (arr, 3, nmc_property_olpc_get_anycast_address (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_vpn_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingVpn *s_vpn = NM_SETTING_VPN (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_VPN (s_vpn), FALSE); + + tmpl = nmc_fields_setting_vpn; + tmpl_len = sizeof (nmc_fields_setting_vpn); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_VPN_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_vpn_get_service_type (setting)); + set_val_str (arr, 2, nmc_property_vpn_get_user_name (setting)); + set_val_str (arr, 3, nmc_property_vpn_get_data (setting)); + set_val_str (arr, 4, GET_SECRET (secrets, setting, nmc_property_vpn_get_secrets)); + set_val_str (arr, 5, nmc_property_vpn_get_persistent (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_wimax_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingWimax *s_wimax = NM_SETTING_WIMAX (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_WIMAX (s_wimax), FALSE); + + tmpl = nmc_fields_setting_wimax; + tmpl_len = sizeof (nmc_fields_setting_wimax); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_WIMAX_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_wimax_get_mac_address (setting)); + set_val_str (arr, 2, nmc_property_wimax_get_network_name (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_infiniband_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingInfiniband *s_infiniband = NM_SETTING_INFINIBAND (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (s_infiniband), FALSE); + + tmpl = nmc_fields_setting_infiniband; + tmpl_len = sizeof (nmc_fields_setting_infiniband); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_INFINIBAND_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_ib_get_mac_address (setting)); + set_val_str (arr, 2, nmc_property_ib_get_mtu (setting)); + set_val_str (arr, 3, nmc_property_ib_get_transport_mode (setting)); + set_val_str (arr, 4, nmc_property_ib_get_p_key (setting)); + set_val_str (arr, 5, nmc_property_ib_get_parent (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_bond_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingBond *s_bond = NM_SETTING_BOND (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_BOND (s_bond), FALSE); + + tmpl = nmc_fields_setting_bond; + tmpl_len = sizeof (nmc_fields_setting_bond); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_BOND_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_bond_get_options (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_vlan_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_VLAN (s_vlan), FALSE); + + tmpl = nmc_fields_setting_vlan; + tmpl_len = sizeof (nmc_fields_setting_vlan); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_VLAN_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_vlan_get_parent (setting)); + set_val_str (arr, 2, nmc_property_vlan_get_id (setting)); + set_val_str (arr, 3, nmc_property_vlan_get_flags (setting)); + set_val_str (arr, 4, nmc_property_vlan_get_ingress_priority_map (setting)); + set_val_str (arr, 5, nmc_property_vlan_get_egress_priority_map (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_adsl_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingAdsl *s_adsl = NM_SETTING_ADSL (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_ADSL (s_adsl), FALSE); + + tmpl = nmc_fields_setting_adsl; + tmpl_len = sizeof (nmc_fields_setting_adsl); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_ADSL_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_adsl_get_username (setting)); + set_val_str (arr, 2, GET_SECRET (secrets, setting, nmc_property_adsl_get_password)); + set_val_str (arr, 3, nmc_property_adsl_get_password_flags (setting)); + set_val_str (arr, 4, nmc_property_adsl_get_protocol (setting)); + set_val_str (arr, 5, nmc_property_adsl_get_encapsulation (setting)); + set_val_str (arr, 6, nmc_property_adsl_get_vpi (setting)); + set_val_str (arr, 7, nmc_property_adsl_get_vci (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_bridge_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingBridge *s_bridge = NM_SETTING_BRIDGE (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (s_bridge), FALSE); + + tmpl = nmc_fields_setting_bridge; + tmpl_len = sizeof (nmc_fields_setting_bridge); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_BRIDGE_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_bridge_get_mac_address (setting)); + set_val_str (arr, 2, nmc_property_bridge_get_stp (setting)); + set_val_str (arr, 3, nmc_property_bridge_get_priority (setting)); + set_val_str (arr, 4, nmc_property_bridge_get_forward_delay (setting)); + set_val_str (arr, 5, nmc_property_bridge_get_hello_time (setting)); + set_val_str (arr, 6, nmc_property_bridge_get_max_age (setting)); + set_val_str (arr, 7, nmc_property_bridge_get_ageing_time (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_bridge_port_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingBridgePort *s_bridge_port = NM_SETTING_BRIDGE_PORT (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (s_bridge_port), FALSE); + + tmpl = nmc_fields_setting_bridge_port; + tmpl_len = sizeof (nmc_fields_setting_bridge_port); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_BRIDGE_PORT_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_bridge_port_get_priority (setting)); + set_val_str (arr, 2, nmc_property_bridge_port_get_path_cost (setting)); + set_val_str (arr, 3, nmc_property_bridge_port_get_hairpin_mode (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_team_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingTeam *s_team = NM_SETTING_TEAM (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_TEAM (s_team), FALSE); + + tmpl = nmc_fields_setting_team; + tmpl_len = sizeof (nmc_fields_setting_team); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_TEAM_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_team_get_config (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_team_port_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingTeamPort *s_team_port = NM_SETTING_TEAM_PORT (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (s_team_port), FALSE); + + tmpl = nmc_fields_setting_team_port; + tmpl_len = sizeof (nmc_fields_setting_team_port); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_TEAM_PORT_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_team_port_get_config (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_dcb_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + NMSettingDcb *s_dcb = NM_SETTING_DCB (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_DCB (s_dcb), FALSE); + + tmpl = nmc_fields_setting_dcb; + tmpl_len = sizeof (nmc_fields_setting_dcb); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_DCB_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_dcb_get_app_fcoe_flags (setting)); + set_val_str (arr, 2, nmc_property_dcb_get_app_fcoe_priority (setting)); + set_val_str (arr, 3, nmc_property_dcb_get_app_fcoe_mode (setting)); + set_val_str (arr, 4, nmc_property_dcb_get_app_iscsi_flags (setting)); + set_val_str (arr, 5, nmc_property_dcb_get_app_iscsi_priority (setting)); + set_val_str (arr, 6, nmc_property_dcb_get_app_fip_flags (setting)); + set_val_str (arr, 7, nmc_property_dcb_get_app_fip_priority (setting)); + set_val_str (arr, 8, nmc_property_dcb_get_pfc_flags (setting)); + set_val_str (arr, 9, nmc_property_dcb_get_pfc (setting)); + set_val_str (arr, 10, nmc_property_dcb_get_pg_flags (setting)); + set_val_str (arr, 11, nmc_property_dcb_get_pg_group_id (setting)); + set_val_str (arr, 12, nmc_property_dcb_get_pg_group_bandwidth (setting)); + set_val_str (arr, 13, nmc_property_dcb_get_pg_bandwidth (setting)); + set_val_str (arr, 14, nmc_property_dcb_get_pg_strict (setting)); + set_val_str (arr, 15, nmc_property_dcb_get_pg_traffic_class (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +typedef struct { + const char *sname; + gboolean (*func) (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets); +} SettingDetails; + +static const SettingDetails detail_printers[] = { + { NM_SETTING_CONNECTION_SETTING_NAME, setting_connection_details }, + { NM_SETTING_WIRED_SETTING_NAME, setting_wired_details }, + { NM_SETTING_802_1X_SETTING_NAME, setting_802_1X_details }, + { NM_SETTING_WIRELESS_SETTING_NAME, setting_wireless_details }, + { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, setting_wireless_security_details }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, setting_ip4_config_details }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, setting_ip6_config_details }, + { NM_SETTING_SERIAL_SETTING_NAME, setting_serial_details }, + { NM_SETTING_PPP_SETTING_NAME, setting_ppp_details }, + { NM_SETTING_PPPOE_SETTING_NAME, setting_pppoe_details }, + { NM_SETTING_GSM_SETTING_NAME, setting_gsm_details }, + { NM_SETTING_CDMA_SETTING_NAME, setting_cdma_details }, + { NM_SETTING_BLUETOOTH_SETTING_NAME, setting_bluetooth_details }, + { NM_SETTING_OLPC_MESH_SETTING_NAME, setting_olpc_mesh_details }, + { NM_SETTING_VPN_SETTING_NAME, setting_vpn_details }, + { NM_SETTING_WIMAX_SETTING_NAME, setting_wimax_details }, + { NM_SETTING_INFINIBAND_SETTING_NAME, setting_infiniband_details }, + { NM_SETTING_BOND_SETTING_NAME, setting_bond_details }, + { NM_SETTING_VLAN_SETTING_NAME, setting_vlan_details }, + { NM_SETTING_ADSL_SETTING_NAME, setting_adsl_details }, + { NM_SETTING_BRIDGE_SETTING_NAME, setting_bridge_details }, + { NM_SETTING_BRIDGE_PORT_SETTING_NAME, setting_bridge_port_details }, + { NM_SETTING_TEAM_SETTING_NAME, setting_team_details }, + { NM_SETTING_TEAM_PORT_SETTING_NAME, setting_team_port_details }, + { NM_SETTING_DCB_SETTING_NAME, setting_dcb_details }, + { NULL }, +}; + +gboolean +setting_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets) +{ + const SettingDetails *iter = &detail_printers[0]; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + + while (iter->sname) { + if (nm_setting_lookup_type (iter->sname) == G_OBJECT_TYPE (setting)) + return iter->func (setting, nmc, one_prop, secrets); + iter++; + } + + g_assert_not_reached (); + return FALSE; +} + diff --git a/clients/cli/settings.h b/clients/cli/settings.h new file mode 100644 index 00000000..3c4c814f --- /dev/null +++ b/clients/cli/settings.h @@ -0,0 +1,66 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + */ + +#ifndef NMC_SETTINGS_H +#define NMC_SETTINGS_H + +#include "nmcli.h" +#include "utils.h" + +/* --- Functions --- */ + +void nmc_properties_init (void); +void nmc_properties_cleanup (void); + +NMSetting *nmc_setting_new_for_name (const char *name); +void nmc_setting_custom_init (NMSetting *setting); +void nmc_setting_ip4_connect_handlers (NMSettingIPConfig *setting); +void nmc_setting_ip6_connect_handlers (NMSettingIPConfig *setting); +void nmc_setting_wireless_connect_handlers (NMSettingWireless *setting); +void nmc_setting_connection_connect_handlers (NMSettingConnection *setting, NMConnection *connection); + +char **nmc_setting_get_valid_properties (NMSetting *setting); +char *nmc_setting_get_property_desc (NMSetting *setting, const char *prop); +const char *nmc_setting_get_property_allowed_values (NMSetting *setting, const char *prop); +char *nmc_setting_get_property (NMSetting *setting, + const char *prop, + GError **error); +char *nmc_setting_get_property_out2in (NMSetting *setting, + const char *prop, + GError **error); +gboolean nmc_setting_set_property (NMSetting *setting, + const char *prop, + const char *val, + GError **error); +gboolean nmc_setting_reset_property (NMSetting *setting, + const char *prop, + GError **error); +gboolean nmc_setting_remove_property_option (NMSetting *setting, + const char *prop, + const char *option, + guint32 idx, + GError **error); +void nmc_property_set_default_value (NMSetting *setting, const char *prop); + +gboolean nmc_property_get_gvalue (NMSetting *setting, const char *prop, GValue *value); +gboolean nmc_property_set_gvalue (NMSetting *setting, const char *prop, GValue *value); + +gboolean setting_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets); + +#endif /* NMC_SETTINGS_H */ diff --git a/clients/cli/utils.c b/clients/cli/utils.c new file mode 100644 index 00000000..60cf9f93 --- /dev/null +++ b/clients/cli/utils.c @@ -0,0 +1,1151 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2010 - 2014 Red Hat, Inc. + */ + +/* Generated configuration file */ +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "utils.h" + +int +matches (const char *cmd, const char *pattern) +{ + size_t len = strlen (cmd); + if (!len || len > strlen (pattern)) + return -1; + return memcmp (pattern, cmd, len); +} + +int +next_arg (int *argc, char ***argv) +{ + int arg_num = *argc; + + if (arg_num > 0) { + (*argc)--; + (*argv)++; + } + if (arg_num <= 1) + return -1; + + return 0; +} + +gboolean +nmc_arg_is_help (const char *arg) +{ + if (!arg) + return FALSE; + if ( matches (arg, "help") == 0 + || (g_str_has_prefix (arg, "-") && matches (arg+1, "help") == 0) + || (g_str_has_prefix (arg, "--") && matches (arg+2, "help") == 0)) { + return TRUE; + } + return FALSE; +} + +gboolean +nmc_arg_is_option (const char *str, const char *opt_name) +{ + const char *p; + + if (!str || !*str) + return FALSE; + + if (str[0] != '-') + return FALSE; + + p = (str[1] == '-') ? str + 2 : str + 1; + + return (*p ? (matches (p, opt_name) == 0) : FALSE); +} + + +/* + * Helper function to parse command-line arguments. + * arg_arr: description of arguments to look for + * last: whether these are last expected arguments + * argc: command-line argument array + * argv: command-line argument array size + * error: error set on a failure (when FALSE is returned) + * Returns: TRUE on success, FALSE on an error and sets 'error' + */ +gboolean +nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error) +{ + nmc_arg_t *p; + gboolean found; + gboolean have_mandatory; + + g_return_val_if_fail (arg_arr != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + while (*argc > 0) { + found = FALSE; + + for (p = arg_arr; p->name; p++) { + if (strcmp (**argv, p->name) == 0) { + + if (p->found) { + /* Don't allow repeated arguments, because the argument of the same + * name could be used later on the line for another purpose. Assume + * that's the case and return. + */ + return TRUE; + } + + if (p->has_value) { + if (next_arg (argc, argv) != 0) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: value for '%s' argument is required."), *(*argv-1)); + return FALSE; + } + *(p->value) = **argv; + } + p->found = TRUE; + found = TRUE; + break; + } + } + + if (!found) { + have_mandatory = TRUE; + for (p = arg_arr; p->name; p++) { + if (p->mandatory && !p->found) { + have_mandatory = FALSE; + break; + } + } + + if (have_mandatory && !last) + return TRUE; + + if (p->name) + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: Argument '%s' was expected, but '%s' provided."), p->name, **argv); + else + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: Unexpected argument '%s'"), **argv); + return FALSE; + } + + next_arg (argc, argv); + } + + return TRUE; +} + +/* + * Convert SSID to a hex string representation. + * Caller has to free the returned string using g_free() + */ +char * +ssid_to_hex (const char *str, gsize len) +{ + GString *printable; + char *printable_str; + int i; + + if (str == NULL || len == 0) + return NULL; + + printable = g_string_new (NULL); + for (i = 0; i < len; i++) { + g_string_append_printf (printable, "%02X", (unsigned char) str[i]); + } + printable_str = g_string_free (printable, FALSE); + return printable_str; +} + +/* + * Converts IPv4 address from guint32 in network-byte order to text representation. + * Returns: text form of the IP or NULL (then error is set) + */ +char * +nmc_ip4_address_as_string (guint32 ip, GError **error) +{ + guint32 tmp_addr; + char buf[INET_ADDRSTRLEN]; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + memset (&buf, '\0', sizeof (buf)); + tmp_addr = ip; + + if (inet_ntop (AF_INET, &tmp_addr, buf, INET_ADDRSTRLEN)) { + return g_strdup (buf); + } else { + g_set_error (error, NMCLI_ERROR, 0, _("Error converting IP4 address '0x%X' to text form"), + ntohl (tmp_addr)); + return NULL; + } +} + +/* + * Converts IPv6 address in in6_addr structure to text representation. + * Returns: text form of the IP or NULL (then error is set) + */ +char * +nmc_ip6_address_as_string (const struct in6_addr *ip, GError **error) +{ + char buf[INET6_ADDRSTRLEN]; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + memset (&buf, '\0', sizeof (buf)); + + if (inet_ntop (AF_INET6, ip, buf, INET6_ADDRSTRLEN)) { + return g_strdup (buf); + } else { + if (error) { + int j; + GString *ip6_str = g_string_new (NULL); + g_string_append_printf (ip6_str, "%02X", ip->s6_addr[0]); + for (j = 1; j < 16; j++) + g_string_append_printf (ip6_str, " %02X", ip->s6_addr[j]); + g_set_error (error, NMCLI_ERROR, 0, _("Error converting IP6 address '%s' to text form"), + ip6_str->str); + g_string_free (ip6_str, TRUE); + } + return NULL; + } +} + +/* + * Erase terminal line using ANSI escape sequences. + * It prints <ESC>[2K sequence to erase the line and then \r to return back + * to the beginning of the line. + * + * http://www.termsys.demon.co.uk/vtansi.htm + */ +void +nmc_terminal_erase_line (void) +{ + /* We intentionally use printf(), not g_print() here, to ensure that + * GLib doesn't mistakenly try to convert the string. + */ + printf ("\33[2K\r"); + fflush (stdout); +} + +/* + * Print animated progress for an operation. + * Repeated calls of the function will show rotating slash in terminal followed + * by the string passed in 'str' argument. + */ +void +nmc_terminal_show_progress (const char *str) +{ + static int idx = 0; + const char slashes[4] = {'|', '/', '-', '\\'}; + + nmc_terminal_erase_line (); + g_print ("%c %s", slashes[idx++], str ? str : ""); + fflush (stdout); + if (idx == 4) + idx = 0; +} + +const char * +nmc_term_color_sequence (NmcTermColor color) +{ + switch (color) { + case NMC_TERM_COLOR_BLACK: + return "\33[30m"; + break; + case NMC_TERM_COLOR_RED: + return "\33[31m"; + break; + case NMC_TERM_COLOR_GREEN: + return "\33[32m"; + break; + case NMC_TERM_COLOR_YELLOW: + return "\33[33m"; + break; + case NMC_TERM_COLOR_BLUE: + return "\33[34m"; + break; + case NMC_TERM_COLOR_MAGENTA: + return "\33[35m"; + break; + case NMC_TERM_COLOR_CYAN: + return "\33[36m"; + break; + case NMC_TERM_COLOR_WHITE: + return "\33[37m"; + break; + default: + return ""; + break; + } +} + +char * +nmc_colorize (NmcTermColor color, const char *fmt, ...) +{ + va_list args; + char *str, *colored; + const char *ansi_color, *color_end; + + va_start (args, fmt); + str = g_strdup_vprintf (fmt, args); + va_end (args); + + ansi_color = nmc_term_color_sequence (color); + if (*ansi_color) + color_end = "\33[0m"; + else + color_end = ""; + + colored = g_strdup_printf ("%s%s%s", ansi_color, str, color_end); + g_free (str); + return colored; +} + +/* + * Convert string to signed integer. + * If required, the resulting number is checked to be in the <min,max> range. + */ +gboolean +nmc_string_to_int_base (const char *str, + int base, + gboolean range_check, + long int min, + long int max, + long int *value) +{ + char *end; + long int tmp; + + errno = 0; + tmp = strtol (str, &end, base); + if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { + return FALSE; + } + *value = tmp; + return TRUE; +} + +/* + * Convert string to unsigned integer. + * If required, the resulting number is checked to be in the <min,max> range. + */ +gboolean +nmc_string_to_uint_base (const char *str, + int base, + gboolean range_check, + unsigned long int min, + unsigned long int max, + unsigned long int *value) +{ + char *end; + unsigned long int tmp; + + errno = 0; + tmp = strtoul (str, &end, base); + if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { + return FALSE; + } + *value = tmp; + return TRUE; +} + +gboolean +nmc_string_to_int (const char *str, + gboolean range_check, + long int min, + long int max, + long int *value) +{ + return nmc_string_to_int_base (str, 10, range_check, min, max, value); +} + +gboolean +nmc_string_to_uint (const char *str, + gboolean range_check, + unsigned long int min, + unsigned long int max, + unsigned long int *value) +{ + return nmc_string_to_uint_base (str, 10, range_check, min, max, value); +} + +gboolean +nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error) +{ + const char *s_true[] = { "true", "yes", "on", NULL }; + const char *s_false[] = { "false", "no", "off", NULL }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (g_strcmp0 (str, "o") == 0) { + g_set_error (error, 1, 0, + _("'%s' is ambiguous (on x off)"), str); + return FALSE; + } + + if (nmc_string_is_valid (str, s_true, NULL)) + *val_bool = TRUE; + else if (nmc_string_is_valid (str, s_false, NULL)) + *val_bool = FALSE; + else { + g_set_error (error, 1, 0, + _("'%s' is not valid; use [%s] or [%s]"), + str, "true, yes, on", "false, no, off"); + return FALSE; + } + return TRUE; +} + +/* + * Ask user for input and return the string. + * The caller is responsible for freeing the returned string. + */ +char * +nmc_get_user_input (const char *ask_str) +{ + char *line = NULL; + size_t line_ln = 0; + ssize_t num; + + g_print ("%s", ask_str); + num = getline (&line, &line_ln, stdin); + + /* Remove newline from the string */ + if (num < 1 || (num == 1 && line[0] == '\n')) { + g_free (line); + line = NULL; + } else { + if (line[num-1] == '\n') + line[num-1] = '\0'; + } + + return line; +} + +/* + * Split string in 'line' according to 'delim' to (argument) array. + */ +int +nmc_string_to_arg_array (const char *line, const char *delim, char ***argv, int *argc) +{ + int i = 0; + char **arr; + + arr = g_strsplit_set (line ? line : "", delim ? delim : " \t", 0); + while (arr && arr[i]) + i++; + + *argc = i; + *argv = arr; + + return 0; +} + +/* + * Check whether 'input' is contained in 'allowed' array. It performs case + * insensitive comparison and supports shortcut strings if they are unique. + * Returns: a pointer to found string in allowed array on success or NULL. + * On failure: error->code : 0 - string not found; 1 - string is ambiguous + */ +const char * +nmc_string_is_valid (const char *input, const char **allowed, GError **error) +{ + const char **p; + size_t input_ln, p_len; + gboolean prev_match = FALSE; + const char *ret = NULL; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (!input || !*input) + goto finish; + + input_ln = strlen (input); + for (p = allowed; p && *p; p++) { + p_len = strlen (*p); + if (g_ascii_strncasecmp (input, *p, input_ln) == 0) { + if (input_ln == p_len) { + ret = *p; + break; + } + if (!prev_match) + ret = *p; + else { + g_set_error (error, 1, 1, _("'%s' is ambiguous (%s x %s)"), + input, ret, *p); + return NULL; + } + prev_match = TRUE; + } + } + +finish: + if (ret == NULL) { + char *valid_vals = g_strjoinv (", ", (char **) allowed); + if (!input || !*input) + g_set_error (error, 1, 0, _("missing name, try one of [%s]"), valid_vals); + else + g_set_error (error, 1, 0, _("'%s' not among [%s]"), input, valid_vals); + + g_free (valid_vals); + } + return ret; +} + +/* + * Convert string array (char **) to GSList. + * + * Returns: pointer to newly created GSList. Caller should free it. + */ +GSList * +nmc_util_strv_to_slist (char **strv) +{ + GSList *list = NULL; + guint i = 0; + + while (strv && strv[i]) + list = g_slist_prepend (list, g_strdup (strv[i++])); + + return g_slist_reverse (list); +} + +/* + * Convert string array (char **) to description string in the form of: + * "[string1, string2, ]" + * + * Returns: a newly allocated string. Caller must free it with g_free(). + */ +char * +nmc_util_strv_for_display (const char **strv) +{ + GString *result; + guint i = 0; + + result = g_string_sized_new (150); + g_string_append_c (result, '['); + while (strv && strv[i]) { + if (result->len > 1) + g_string_append (result, ", "); + g_string_append (result, strv[i]); + i++; + } + g_string_append_c (result, ']'); + + return g_string_free (result, FALSE); +} + +/* + * Wrapper function for g_strsplit_set() that removes empty strings + * from the vector as they are not useful in most cases. + */ +char ** +nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens) +{ + char **result; + uint i; + uint j; + + result = g_strsplit_set (str, delimiter, max_tokens); + + /* remove empty strings */ + for (i = 0; result && result[i]; i++) { + if (*(result[i]) == '\0') { + g_free (result[i]); + for (j = i; result[j]; j++) + result[j] = result[j + 1]; + i--; + } + } + return result; +} + +/* + * Find out how many columns an UTF-8 string occupies on the screen + */ +int +nmc_string_screen_width (const char *start, const char *end) +{ + int width = 0; + + if (end == NULL) + end = start + strlen (start); + + while (start < end) { + width += g_unichar_iswide (g_utf8_get_char (start)) ? 2 : g_unichar_iszerowidth (g_utf8_get_char (start)) ? 0 : 1; + start = g_utf8_next_char (start); + } + return width; +} + +void +set_val_str (NmcOutputField fields_array[], guint32 idx, char *value) +{ + fields_array[idx].value = value; + fields_array[idx].value_is_array = FALSE; + fields_array[idx].free_value = TRUE; +} + +void +set_val_strc (NmcOutputField fields_array[], guint32 idx, const char *value) +{ + fields_array[idx].value = (char *) value; + fields_array[idx].value_is_array = FALSE; + fields_array[idx].free_value = FALSE; +} + +void +set_val_arr (NmcOutputField fields_array[], guint32 idx, char **value) +{ + fields_array[idx].value = value; + fields_array[idx].value_is_array = TRUE; + fields_array[idx].free_value = TRUE; +} + +void +set_val_arrc (NmcOutputField fields_array[], guint32 idx, const char **value) +{ + fields_array[idx].value = (char **) value; + fields_array[idx].value_is_array = TRUE; + fields_array[idx].free_value = FALSE; +} + +/* + * Free 'value' members in array of NmcOutputField + */ +void +nmc_free_output_field_values (NmcOutputField fields_array[]) +{ + NmcOutputField *iter = fields_array; + + while (iter && iter->name) { + if (iter->free_value) { + if (iter->value_is_array) + g_strfreev ((char **) iter->value); + else + g_free ((char *) iter->value); + iter->value = NULL; + } + iter++; + } +} + +/** + * parse_output_fields: + * @field_str: comma-separated field names to parse + * @fields_array: array of allowed fields + * @parse_groups: whether the fields can contain group prefix (e.g. general.driver) + * @group_fields: (out) (allow-none): array of field names for particular groups + * @error: (out) (allow-none): location to store error, or %NULL + * + * Parses comma separated fields in @fields_str according to @fields_array. + * When @parse_groups is %TRUE, fields can be in the form 'group.field'. Then + * @group_fields will be filled with the required field for particular group. + * @group_fields array corresponds to the returned array. + * Examples: + * @field_str: "type,name,uuid" | "ip4,general.device" | "ip4.address,ip6" + * returned array: 2 0 1 | 7 0 | 7 9 + * @group_fields: NULL NULL NULL | NULL "device" | "address" NULL + * + * Returns: #GArray with indices representing fields in @fields_array. + * Caller is responsible for freeing the array. + */ +GArray * +parse_output_fields (const char *fields_str, + const NmcOutputField fields_array[], + gboolean parse_groups, + GPtrArray **group_fields, + GError **error) +{ + char **fields, **iter; + GArray *array; + int i, j; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + g_return_val_if_fail (group_fields == NULL || *group_fields == NULL, NULL); + + array = g_array_new (FALSE, FALSE, sizeof (int)); + if (parse_groups && group_fields) + *group_fields = g_ptr_array_new_full (20, (GDestroyNotify) g_free); + + /* Split supplied fields string */ + fields = g_strsplit_set (fields_str, ",", -1); + for (iter = fields; iter && *iter; iter++) { + int idx = -1; + + g_strstrip (*iter); + if (parse_groups) { + /* e.g. "general.device,general.driver,ip4,ip6" */ + gboolean found = FALSE; + char *left = *iter; + char *right = strchr (*iter, '.'); + + if (right) + *right++ = '\0'; + + for (i = 0; fields_array[i].name; i++) { + if (strcasecmp (left, fields_array[i].name) == 0) { + NmcOutputField *valid_names = fields_array[i].group; + idx = i; + if (!right && !valid_names) { + found = TRUE; + break; + } + for (j = 0; valid_names && valid_names[j].name; j++) { + if (!right || strcasecmp (right, valid_names[j].name) == 0) { + found = TRUE; + break; + } + } + if (found) + break; + } + } + if (found) { + /* Add index to array, and field name (or NULL) to group_fields array */ + g_array_append_val (array, idx); + if (group_fields && *group_fields) + g_ptr_array_add (*group_fields, g_strdup (right)); + } + if (right) + *(right-1) = '.'; /* Restore the original string */ + } else { + /* e.g. "general,ip4,ip6" */ + for (i = 0; fields_array[i].name; i++) { + if (strcasecmp (*iter, fields_array[i].name) == 0) { + g_array_append_val (array, i); + break; + } + } + } + + /* Field was not found - error case */ + if (fields_array[i].name == NULL) { + /* Set GError */ + if (!strcasecmp (*iter, "all") || !strcasecmp (*iter, "common")) + g_set_error (error, NMCLI_ERROR, 0, _("field '%s' has to be alone"), *iter); + else { + char *allowed_fields = nmc_get_allowed_fields (fields_array, idx); + g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s'; allowed fields: %s"), + *iter, allowed_fields); + g_free (allowed_fields); + } + + /* Free arrays on error */ + g_array_free (array, TRUE); + array = NULL; + if (group_fields && *group_fields) { + g_ptr_array_free (*group_fields, TRUE); + *group_fields = NULL; + } + goto done; + } + } +done: + if (fields) + g_strfreev (fields); + return array; +} + +/** +* nmc_get_allowed_fields: +* @fields_array: array of fields +* @group_idx: index to the array (for second-level array in 'group' member), +* or -1 +* +* Returns: string of allowed fields names. +* Caller is responsible for freeing the array. +*/ +char * +nmc_get_allowed_fields (const NmcOutputField fields_array[], int group_idx) +{ + GString *allowed_fields = g_string_sized_new (256); + int i; + + if (group_idx != -1 && fields_array[group_idx].group) { + NmcOutputField *second_level = fields_array[group_idx].group; + for (i = 0; second_level[i].name; i++) + g_string_append_printf (allowed_fields, "%s.%s,", + fields_array[group_idx].name, second_level[i].name); + } else { + for (i = 0; fields_array[i].name; i++) + g_string_append_printf (allowed_fields, "%s,", fields_array[i].name); + } + g_string_truncate (allowed_fields, allowed_fields->len - 1); + + return g_string_free (allowed_fields, FALSE); +} + +gboolean +nmc_terse_option_check (NMCPrintOutput print_output, const char *fields, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (print_output == NMC_PRINT_TERSE) { + if (!fields) { + g_set_error_literal (error, NMCLI_ERROR, 0, _("Option '--terse' requires specifying '--fields'")); + return FALSE; + } else if ( !strcasecmp (fields, "all") + || !strcasecmp (fields, "common")) { + g_set_error (error, NMCLI_ERROR, 0, _("Option '--terse' requires specific '--fields' option values , not '%s'"), fields); + return FALSE; + } + } + return TRUE; +} + +NmcOutputField * +nmc_dup_fields_array (NmcOutputField fields[], size_t size, guint32 flags) +{ + NmcOutputField *row; + + row = g_malloc0 (size); + memcpy (row, fields, size); + row[0].flags = flags; + + return row; +} + +void +nmc_empty_output_fields (NmCli *nmc) +{ + guint i; + + /* Free values in field structure */ + for (i = 0; i < nmc->output_data->len; i++) { + NmcOutputField *fld_arr = g_ptr_array_index (nmc->output_data, i); + nmc_free_output_field_values (fld_arr); + } + + /* Empty output_data array */ + if (nmc->output_data->len > 0) + g_ptr_array_remove_range (nmc->output_data, 0, nmc->output_data->len); + + if (nmc->print_fields.indices) { + g_array_free (nmc->print_fields.indices, TRUE); + nmc->print_fields.indices = NULL; + } +} + +static char * +get_value_to_print (NmcOutputField *fields, + gboolean field_name, + const char *not_set_str, + gboolean *dealloc) +{ + gboolean is_array = fields->value_is_array; + char *value; + + if (field_name) + value = _(fields->name_l10n); + else + value = fields->value ? + (is_array ? g_strjoinv (" | ", (char **) fields->value) : + (char *) fields->value) : + (char *) not_set_str; + *dealloc = fields->value && is_array && !field_name; + return value; +} + +/* + * Print both headers or values of 'field_values' array. + * Entries to print and their order are specified via indices in + * 'nmc->print_fields.indices' array. + * Various flags influencing the output of fields are set up in the first item + * of 'field_values' array. + */ +void +print_required_fields (NmCli *nmc, const NmcOutputField field_values[]) +{ + GString *str; + int width1, width2; + int table_width = 0; + char *line = NULL; + char *indent_str; + const char *not_set_str = "--"; + int i; + const NmcPrintFields fields = nmc->print_fields; + gboolean multiline = nmc->multiline_output; + gboolean terse = (nmc->print_output == NMC_PRINT_TERSE); + gboolean pretty = (nmc->print_output == NMC_PRINT_PRETTY); + gboolean escape = nmc->escape_values; + gboolean main_header_add = field_values[0].flags & NMC_OF_FLAG_MAIN_HEADER_ADD; + gboolean main_header_only = field_values[0].flags & NMC_OF_FLAG_MAIN_HEADER_ONLY; + gboolean field_names = field_values[0].flags & NMC_OF_FLAG_FIELD_NAMES; + gboolean section_prefix = field_values[0].flags & NMC_OF_FLAG_SECTION_PREFIX; + gboolean main_header = main_header_add || main_header_only; + + /* No headers are printed in terse mode: + * - neither main header nor field (column) names + */ + if ((main_header_only || field_names) && terse) + return; + + if (multiline) { + /* --- Multiline mode --- */ + enum { ML_HEADER_WIDTH = 79 }; + enum { ML_VALUE_INDENT = 40 }; + if (main_header && pretty) { + /* Print the main header */ + int header_width = nmc_string_screen_width (fields.header_name, NULL) + 4; + table_width = header_width < ML_HEADER_WIDTH ? ML_HEADER_WIDTH : header_width; + + line = g_strnfill (ML_HEADER_WIDTH, '='); + width1 = strlen (fields.header_name); + width2 = nmc_string_screen_width (fields.header_name, NULL); + g_print ("%s\n", line); + g_print ("%*s\n", (table_width + width2)/2 + width1 - width2, fields.header_name); + g_print ("%s\n", line); + g_free (line); + } + + /* Print values */ + if (!main_header_only && !field_names) { + for (i = 0; i < fields.indices->len; i++) { + char *tmp; + int idx = g_array_index (fields.indices, int, i); + gboolean is_array = field_values[idx].value_is_array; + + /* section prefix can't be an array */ + g_assert (!is_array || !section_prefix || idx != 0); + + if (section_prefix && idx == 0) /* The first field is section prefix */ + continue; + + if (is_array) { + /* value is a null-terminated string array */ + const char **p; + int j; + + for (p = (const char **) field_values[idx].value, j = 1; p && *p; p++, j++) { + tmp = g_strdup_printf ("%s%s%s[%d]:", + section_prefix ? (const char*) field_values[0].value : "", + section_prefix ? "." : "", + _(field_values[idx].name_l10n), + j); + width1 = strlen (tmp); + width2 = nmc_string_screen_width (tmp, NULL); + g_print ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp, + *p ? *p : not_set_str); + g_free (tmp); + } + } else { + /* value is a string */ + const char *hdr_name = (const char*) field_values[0].value; + const char *val = (const char*) field_values[idx].value; + + tmp = g_strdup_printf ("%s%s%s:", + section_prefix ? hdr_name : "", + section_prefix ? "." : "", + _(field_values[idx].name_l10n)); + width1 = strlen (tmp); + width2 = nmc_string_screen_width (tmp, NULL); + g_print ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp, + val ? val : not_set_str); + g_free (tmp); + } + } + if (pretty) { + line = g_strnfill (ML_HEADER_WIDTH, '-'); + g_print ("%s\n", line); + g_free (line); + } + } + return; + } + + /* --- Tabular mode: each line = one object --- */ + str = g_string_new (NULL); + + for (i = 0; i < fields.indices->len; i++) { + int idx = g_array_index (fields.indices, int, i); + gboolean dealloc; + char *value = get_value_to_print ((NmcOutputField *) field_values+idx, field_names, not_set_str, &dealloc); + + if (terse) { + if (escape) { + const char *p = value; + while (*p) { + if (*p == ':' || *p == '\\') + g_string_append_c (str, '\\'); /* Escaping by '\' */ + g_string_append_c (str, *p); + p++; + } + } + else + g_string_append_printf (str, "%s", value); + g_string_append_c (str, ':'); /* Column separator */ + } else { + width1 = strlen (value); + width2 = nmc_string_screen_width (value, NULL); /* Width of the string (in screen colums) */ + g_string_append_printf (str, "%-*s", field_values[idx].width + width1 - width2, strlen (value) > 0 ? value : "--"); + g_string_append_c (str, ' '); /* Column separator */ + table_width += field_values[idx].width + width1 - width2 + 1; + } + + if (dealloc) + g_free (value); + } + + /* Print the main table header */ + if (main_header && pretty) { + int header_width = nmc_string_screen_width (fields.header_name, NULL) + 4; + table_width = table_width < header_width ? header_width : table_width; + + line = g_strnfill (table_width, '='); + width1 = strlen (fields.header_name); + width2 = nmc_string_screen_width (fields.header_name, NULL); + g_print ("%s\n", line); + g_print ("%*s\n", (table_width + width2)/2 + width1 - width2, fields.header_name); + g_print ("%s\n", line); + g_free (line); + } + + /* Print actual values */ + if (!main_header_only && str->len > 0) { + g_string_truncate (str, str->len-1); /* Chop off last column separator */ + if (fields.indent > 0) { + indent_str = g_strnfill (fields.indent, ' '); + g_string_prepend (str, indent_str); + g_free (indent_str); + } + g_print ("%s\n", str->str); + } + + /* Print horizontal separator */ + if (!main_header_only && field_names && pretty) { + if (str->len > 0) { + line = g_strnfill (table_width, '-'); + g_print ("%s\n", line); + g_free (line); + } + } + + g_string_free (str, TRUE); +} + +/* + * Print nmc->output_data + * + * It first finds out maximal string length in columns and fill the value to + * 'width' member of NmcOutputField, so that columns in tabular output are + * properly aligned. Then each object (row in tabular) is printed using + * print_required_fields() function. + */ +void +print_data (NmCli *nmc) +{ + int i, j; + size_t len; + NmcOutputField *row; + int num_fields = 0; + + if (!nmc->output_data || nmc->output_data->len < 1) + return; + + /* How many fields? */ + row = g_ptr_array_index (nmc->output_data, 0); + while (row->name) { + num_fields++; + row++; + } + + /* Find out maximal string lengths */ + for (i = 0; i < num_fields; i++) { + size_t max_width = 0; + for (j = 0; j < nmc->output_data->len; j++) { + gboolean field_names, dealloc; + char *value; + row = g_ptr_array_index (nmc->output_data, j); + field_names = row[0].flags & NMC_OF_FLAG_FIELD_NAMES; + value = get_value_to_print (row+i, field_names, "--", &dealloc); + len = nmc_string_screen_width (value, NULL); + max_width = len > max_width ? len : max_width; + if (dealloc) + g_free (value); + } + for (j = 0; j < nmc->output_data->len; j++) { + row = g_ptr_array_index (nmc->output_data, j); + row[i].width = max_width + 1; + } + } + + /* Now we can print the data. */ + for (i = 0; i < nmc->output_data->len; i++) { + row = g_ptr_array_index (nmc->output_data, i); + print_required_fields (nmc, row); + } +} + +/* +* Compare versions of nmcli and NM daemon. +* Return: TRUE - the versions match (when only major and minor match, print a warning) +* FALSE - versions mismatch +*/ +gboolean +nmc_versions_match (NmCli *nmc) +{ + const char *nm_ver = NULL; + const char *dot; + gboolean match = FALSE; + + g_return_val_if_fail (nmc != NULL, FALSE); + + /* --nocheck option - don't compare the versions */ + if (nmc->nocheck_ver) + return TRUE; + + nmc->get_client (nmc); + nm_ver = nm_client_get_version (nmc->client); + if (nm_ver) { + if (!strcmp (nm_ver, VERSION)) + match = TRUE; + else { + dot = strchr (nm_ver, '.'); + if (dot) { + dot = strchr (dot + 1, '.'); + if (dot && !strncmp (nm_ver, VERSION, dot-nm_ver)) { + g_printerr (_("Warning: nmcli (%s) and NetworkManager (%s) versions don't match. Use --nocheck to suppress the warning.\n"), + VERSION, nm_ver); + match = TRUE; + } + } + } + } + + if (!match) { + g_string_printf (nmc->return_text, _("Error: nmcli (%s) and NetworkManager (%s) versions don't match. Force execution using --nocheck, but the results are unpredictable."), + VERSION, nm_ver ? nm_ver : _("unknown")); + nmc->return_value = NMC_RESULT_ERROR_VERSIONS_MISMATCH; + } + + return match; +} + diff --git a/clients/cli/utils.h b/clients/cli/utils.h new file mode 100644 index 00000000..fc43b0ed --- /dev/null +++ b/clients/cli/utils.h @@ -0,0 +1,96 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + */ + +#ifndef NMC_UTILS_H +#define NMC_UTILS_H + +#include "nmcli.h" + +/* === Types === */ + +typedef struct { + const char *name; + gboolean has_value; + const char **value; + gboolean mandatory; + gboolean found; +} nmc_arg_t; + +/* === Functions === */ +int matches (const char *cmd, const char *pattern); +int next_arg (int *argc, char ***argv); +gboolean nmc_arg_is_help (const char *arg); +gboolean nmc_arg_is_option (const char *arg, const char *opt_name); +gboolean nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error); +char *ssid_to_hex (const char *str, gsize len); +gboolean nmc_string_to_int_base (const char *str, + int base, + gboolean range_check, + long int min, + long int max, + long int *value); +gboolean nmc_string_to_uint_base (const char *str, + int base, + gboolean range_check, + unsigned long int min, + unsigned long int max, + unsigned long int *value); +gboolean nmc_string_to_int (const char *str, + gboolean range_check, + long int min, + long int max, + long int *value); +gboolean nmc_string_to_uint (const char *str, + gboolean range_check, + unsigned long int min, + unsigned long int max, + unsigned long int *value); +gboolean nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error); +char *nmc_ip4_address_as_string (guint32 ip, GError **error); +char *nmc_ip6_address_as_string (const struct in6_addr *ip, GError **error); +void nmc_terminal_erase_line (void); +void nmc_terminal_show_progress (const char *str); +const char *nmc_term_color_sequence (NmcTermColor color); +char *nmc_colorize (NmcTermColor color, const char * fmt, ...); +char *nmc_get_user_input (const char *ask_str); +int nmc_string_to_arg_array (const char *line, const char *delim, char ***argv, int *argc); +const char *nmc_string_is_valid (const char *input, const char **allowed, GError **error); +GSList *nmc_util_strv_to_slist (char **strv); +char * nmc_util_strv_for_display (const char **strv); +char **nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens); +int nmc_string_screen_width (const char *start, const char *end); +void set_val_str (NmcOutputField fields_array[], guint32 index, char *value); +void set_val_strc (NmcOutputField fields_array[], guint32 index, const char *value); +void set_val_arr (NmcOutputField fields_array[], guint32 index, char **value); +void set_val_arrc (NmcOutputField fields_array[], guint32 index, const char **value); +void nmc_free_output_field_values (NmcOutputField fields_array[]); +GArray *parse_output_fields (const char *fields_str, + const NmcOutputField fields_array[], + gboolean parse_groups, + GPtrArray **group_fields, + GError **error); +char *nmc_get_allowed_fields (const NmcOutputField fields_array[], int group_idx); +gboolean nmc_terse_option_check (NMCPrintOutput print_output, const char *fields, GError **error); +NmcOutputField *nmc_dup_fields_array (NmcOutputField fields[], size_t size, guint32 flags); +void nmc_empty_output_fields (NmCli *nmc); +void print_required_fields (NmCli *nmc, const NmcOutputField field_values[]); +void print_data (NmCli *nmc); +gboolean nmc_versions_match (NmCli *nmc); + +#endif /* NMC_UTILS_H */ diff --git a/clients/common/nm-polkit-listener.c b/clients/common/nm-polkit-listener.c new file mode 100644 index 00000000..82df1b2d --- /dev/null +++ b/clients/common/nm-polkit-listener.c @@ -0,0 +1,415 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2014 Red Hat, Inc. + */ + +/** + * SECTION:nm-polkit-listener + * @short_description: A polkit agent listener + * + * #NMPolkitListener is the polkit agent listener used by nmcli and nmtui. + * http://www.freedesktop.org/software/polkit/docs/latest/index.html + * + * For an example polkit agent you can look at polkit source tree: + * http://cgit.freedesktop.org/polkit/tree/src/polkitagent/polkitagenttextlistener.c + * http://cgit.freedesktop.org/polkit/tree/src/programs/pkttyagent.c + * or LXDE polkit agent: + * http://git.lxde.org/gitweb/?p=debian/lxpolkit.git;a=blob;f=src/lxpolkit-listener.c + * https://github.com/lxde/lxqt-policykit/tree/master/src + */ + +#include "config.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nm-glib-compat.h" +#include "nm-polkit-listener.h" + +G_DEFINE_TYPE (NMPolkitListener, nm_polkit_listener, POLKIT_AGENT_TYPE_LISTENER) + +#define NM_POLKIT_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerPrivate)) + +typedef struct { + gpointer reg_handle; /* handle of polkit agent registration */ + + GSimpleAsyncResult *simple; + PolkitAgentSession *active_session; + gulong cancel_id; + GCancellable *cancellable; + + char *action_id; + char *message; + char *icon_name; + char *identity; + + /* callbacks */ + NMPolkitListenerOnRequestFunc on_request_callback; + NMPolkitListenerOnShowInfoFunc on_show_info_callback; + NMPolkitListenerOnShowErrorFunc on_show_error_callback; + NMPolkitListenerOnCompletedFunc on_completed_callback; + gpointer request_callback_data; +} NMPolkitListenerPrivate; + + +static void +on_request (PolkitAgentSession *session, + const char *request, + gboolean echo_on, + gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (user_data); + char *response = NULL; + + if (priv->on_request_callback) { + response = priv->on_request_callback (request, priv->action_id, + priv->message, priv->icon_name, + priv->identity, echo_on, + priv->request_callback_data); + } + + if (response) { + polkit_agent_session_response (session, response); + g_free (response); + } else { + //FIXME: polkit_agent_session_cancel() should emit "completed", but it doesn't work for me ??? + //polkit_agent_session_cancel (session); + polkit_agent_session_response (session, ""); + } +} + +static void +on_show_info (PolkitAgentSession *session, + const char *text, + gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (user_data); + + if (priv->on_show_info_callback) + priv->on_show_info_callback (text); +} + +static void +on_show_error (PolkitAgentSession *session, + const char *text, + gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (user_data); + + if (priv->on_show_error_callback) + priv->on_show_error_callback (text); +} + +static void +on_completed (PolkitAgentSession *session, + gboolean gained_authorization, + gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (user_data); + + if (priv->on_completed_callback) + priv->on_completed_callback (gained_authorization); + + g_simple_async_result_complete_in_idle (priv->simple); + + g_object_unref (priv->simple); + g_object_unref (priv->active_session); + if (priv->cancellable) { + g_cancellable_disconnect (priv->cancellable, priv->cancel_id); + g_object_unref (priv->cancellable); + } + + priv->simple = NULL; + priv->active_session = NULL; + priv->cancel_id = 0; + + g_clear_pointer (&priv->action_id, g_free); + g_clear_pointer (&priv->message, g_free); + g_clear_pointer (&priv->icon_name, g_free); + g_clear_pointer (&priv->identity, g_free); +} + +static void +on_cancelled (GCancellable *cancellable, gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (user_data); + + polkit_agent_session_cancel (priv->active_session); +} + +static gint +compare_users (gconstpointer a, gconstpointer b) +{ + char *user; + int ret; + + if (POLKIT_IS_UNIX_USER (a)) + user = g_strdup (polkit_unix_user_get_name (POLKIT_UNIX_USER (a))); + else + user = polkit_identity_to_string (POLKIT_IDENTITY (a)); + + ret = g_strcmp0 ((const char *) user, (const char *) b); + g_free (user); + return ret; +} + +static PolkitIdentity * +choose_identity (GList *identities) +{ + const char *user; + GList *elem; + + /* Choose identity. First try current user, then root, and else + * take the firts one */ + user = getenv("USER"); + elem = g_list_find_custom (identities, user, (GCompareFunc) compare_users); + if (!elem) { + elem = g_list_find_custom (identities, "root", (GCompareFunc) compare_users); + if (!elem) + elem = identities; + } + + return elem->data; +} + +static void +initiate_authentication (PolkitAgentListener *listener, + const char *action_id, + const char *message, + const char *icon_name, + PolkitDetails *details, + const char *cookie, + GList *identities, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (listener); + GSimpleAsyncResult *simple; + PolkitIdentity *identity; + + simple = g_simple_async_result_new (G_OBJECT (listener), + callback, + user_data, + initiate_authentication); + if (priv->active_session != NULL) { + g_simple_async_result_set_error (simple, + POLKIT_ERROR, + POLKIT_ERROR_FAILED, + _("An authentication session is already underway.")); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } + + /* Choose identity */ + identity = choose_identity (identities); + + priv->active_session = polkit_agent_session_new (identity, cookie); + g_signal_connect (priv->active_session, + "completed", + G_CALLBACK (on_completed), + listener); + g_signal_connect (priv->active_session, + "request", + G_CALLBACK (on_request), + listener); + g_signal_connect (priv->active_session, + "show-info", + G_CALLBACK (on_show_info), + listener); + g_signal_connect (priv->active_session, + "show-error", + G_CALLBACK (on_show_error), + listener); + + priv->action_id = g_strdup (action_id); + priv->message = g_strdup (message); + priv->icon_name = g_strdup (icon_name); + if (POLKIT_IS_UNIX_USER (identity)) + priv->identity = g_strdup (polkit_unix_user_get_name (POLKIT_UNIX_USER (identity))); + else + priv->identity = polkit_identity_to_string (identity); + + priv->simple = simple; + priv->cancellable = g_object_ref (cancellable); + priv->cancel_id = g_cancellable_connect (cancellable, + G_CALLBACK (on_cancelled), + listener, + NULL); + + polkit_agent_session_initiate (priv->active_session); +} + +static gboolean +initiate_authentication_finish (PolkitAgentListener *listener, + GAsyncResult *result, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); +} + + +static void +nm_polkit_listener_init (NMPolkitListener *agent) +{ +} + +static void +nm_polkit_listener_finalize (GObject *object) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (object); + + if (priv->reg_handle) + polkit_agent_listener_unregister (priv->reg_handle); + + g_free (priv->action_id); + g_free (priv->message); + g_free (priv->icon_name); + g_free (priv->identity); + + G_OBJECT_CLASS (nm_polkit_listener_parent_class)->finalize (object); +} + +static void +nm_polkit_listener_class_init (NMPolkitListenerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + PolkitAgentListenerClass *pkal_class = POLKIT_AGENT_LISTENER_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMPolkitListenerPrivate)); + + gobject_class->finalize = nm_polkit_listener_finalize; + + pkal_class->initiate_authentication = initiate_authentication; + pkal_class->initiate_authentication_finish = initiate_authentication_finish; +} + +/** + * nm_polkit_listener_new: + * @for_session: %TRUE for registering the polkit agent for the user session, + * %FALSE for registering it for the running process + * @error: location to store error, or %NULL + * + * Creates a new #NMPolkitListener and registers it as a polkit agent. + * + * Returns: a new #NMPolkitListener + */ +PolkitAgentListener * +nm_polkit_listener_new (gboolean for_session, GError **error) +{ + PolkitAgentListener *listener; + PolkitSubject* session; + NMPolkitListenerPrivate *priv; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + listener = g_object_new (NM_TYPE_POLKIT_LISTENER, NULL); + priv = NM_POLKIT_LISTENER_GET_PRIVATE (listener); + + if (for_session) + session = polkit_unix_session_new_for_process_sync (getpid (), NULL, NULL); + else + session = polkit_unix_process_new_for_owner (getpid (), 0, getuid ()); + + priv->reg_handle = polkit_agent_listener_register (listener, POLKIT_AGENT_REGISTER_FLAGS_NONE, + session, NULL, NULL, error); + if (!priv->reg_handle) { + g_object_unref (listener); + g_object_unref (session); + return NULL; + } + + return listener; +} + +/** + * nm_polkit_listener_set_request_callback: + * @self: a #NMPolkitListener object + * @request_callback: callback to install for polkit requests + * @request_callback_data: usaer data passed to request_callback when it is called + * + * Set a callback for "request" signal. The callback will be invoked when polkit + * requests an authorization. + */ +void +nm_polkit_listener_set_request_callback (NMPolkitListener *self, + NMPolkitListenerOnRequestFunc request_callback, + gpointer request_callback_data) +{ + NMPolkitListenerPrivate *priv; + + g_return_if_fail (NM_IS_POLKIT_LISTENER (self)); + + priv = NM_POLKIT_LISTENER_GET_PRIVATE (self); + + priv->on_request_callback = request_callback; + priv->request_callback_data = request_callback_data; +} + +/** + * nm_polkit_listener_set_show_info_callback: + * @self: a #NMPolkitListener object + * @show_info_callback: callback to install for polkit show info trigger + * + * Set a callback for "show-info" signal. The callback will be invoked when polkit + * has an info text to display. + */ +void +nm_polkit_listener_set_show_info_callback (NMPolkitListener *self, + NMPolkitListenerOnShowInfoFunc show_info_callback) +{ + g_return_if_fail (NM_IS_POLKIT_LISTENER (self)); + + NM_POLKIT_LISTENER_GET_PRIVATE (self)->on_show_info_callback = show_info_callback; +} + +/** + * nm_polkit_listener_set_show_error_callback: + * @self: a #NMPolkitListener object + * @show_error_callback: callback to install for polkit show error trigger + * + * Set a callback for "show-error" signal. The callback will be invoked when polkit + * has an error text to display. + */ +void +nm_polkit_listener_set_show_error_callback (NMPolkitListener *self, + NMPolkitListenerOnShowErrorFunc show_error_callback) +{ + g_return_if_fail (NM_IS_POLKIT_LISTENER (self)); + + NM_POLKIT_LISTENER_GET_PRIVATE (self)->on_show_error_callback = show_error_callback; +} + +/** + * nm_polkit_listener_set_completed_callback: + * @self: a #NMPolkitListener object + * @completed_callback: callback to install for polkit completing authorization + * + * Set a callback for "completed" signal. The callback will be invoked when polkit + * completed the request. + */ +void +nm_polkit_listener_set_completed_callback (NMPolkitListener *self, + NMPolkitListenerOnCompletedFunc completed_callback) +{ + g_return_if_fail (NM_IS_POLKIT_LISTENER (self)); + + NM_POLKIT_LISTENER_GET_PRIVATE (self)->on_completed_callback = completed_callback; +} diff --git a/clients/common/nm-polkit-listener.h b/clients/common/nm-polkit-listener.h new file mode 100644 index 00000000..3cd75019 --- /dev/null +++ b/clients/common/nm-polkit-listener.h @@ -0,0 +1,104 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2014 Red Hat, Inc. + */ + +#ifndef __NM_POLKIT_LISTENER_H__ +#define __NM_POLKIT_LISTENER_H__ + +#include <glib.h> + +#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE +#include <polkitagent/polkitagent.h> + +G_BEGIN_DECLS + +#define NM_TYPE_POLKIT_LISTENER (nm_polkit_listener_get_type ()) +#define NM_POLKIT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_POLKIT_LISTENER, NMPolkitListener)) +#define NM_POLKIT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerClass)) +#define NM_IS_POLKIT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_POLKIT_LISTENER)) +#define NM_IS_POLKIT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_POLKIT_LISTENER)) +#define NM_POLKIT_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerClass)) + +/** + * NMPolkitListenerOnRequestFunc: + * @request: the request asked by polkit agent + * @action_id: the action_id of the polkit request + * @message: the message of the polkit request + * @icon_name: the icon name of the polkit request + * @user: user name + * @echo_on: whether the response to the request should be echoed to the screen + * @user_data: user data for the callback + * + * Called as a result of a request by polkit. The function should obtain response + * to the request from user, i.e. get the password required. + */ +typedef char * (*NMPolkitListenerOnRequestFunc) (const char *request, + const char *action_id, + const char *message, + const char *icon_name, + const char *user, + gboolean echo_on, + gpointer user_data); +/** + * NMPolkitListenerOnShowInfoFunc: + * @text: the info text from polkit + * + * Called as a result of show-info signal by polkit. + */ +typedef void (*NMPolkitListenerOnShowInfoFunc) (const char *text); +/** + * NMPolkitListenerOnShowErrorFunc: + * @text: the error text from polkit + * + * Called as a result of show-error signal by polkit. + */ +typedef void (*NMPolkitListenerOnShowErrorFunc) (const char *text); +/** + * NMPolkitListenerCompletedFunc: + * @gained_authorization: whether the autorization was successful + * + * Called as a result of completed signal by polkit. + */ +typedef void (*NMPolkitListenerOnCompletedFunc) (gboolean gained_authorization); + + +typedef struct { + PolkitAgentListener parent; + +} NMPolkitListener; + +typedef struct { + PolkitAgentListenerClass parent; + +} NMPolkitListenerClass; + +GType nm_polkit_listener_get_type (void); + +PolkitAgentListener* nm_polkit_listener_new (gboolean for_session, GError **error); +void nm_polkit_listener_set_request_callback (NMPolkitListener *self, + NMPolkitListenerOnRequestFunc request_callback, + gpointer request_callback_data); +void nm_polkit_listener_set_show_info_callback (NMPolkitListener *self, + NMPolkitListenerOnShowInfoFunc show_info_callback); +void nm_polkit_listener_set_show_error_callback (NMPolkitListener *self, + NMPolkitListenerOnShowErrorFunc show_error_callback); +void nm_polkit_listener_set_completed_callback (NMPolkitListener *self, + NMPolkitListenerOnCompletedFunc completed_callback); + +G_END_DECLS + +#endif /* __NM_POLKIT_LISTENER_H__ */ diff --git a/clients/common/nm-secret-agent-simple.c b/clients/common/nm-secret-agent-simple.c new file mode 100644 index 00000000..80022a4d --- /dev/null +++ b/clients/common/nm-secret-agent-simple.c @@ -0,0 +1,695 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2011-2013 Red Hat, Inc. + * Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com> + */ + +/** + * SECTION:nm-secret-agent-simple + * @short_description: A simple secret agent for NetworkManager + * + * #NMSecretAgentSimple is the secret agent used by nmtui-connect and nmcli. + * + * This is a stripped-down version of gnome-shell's ShellNetworkAgent, + * with bits of the corresponding JavaScript code squished down into + * it. It is intended to eventually be generic enough that it could + * replace ShellNetworkAgent. + */ + +#include "config.h" + +#include <string.h> +#include <glib/gi18n-lib.h> + +#include "nm-secret-agent-simple.h" + +G_DEFINE_TYPE (NMSecretAgentSimple, nm_secret_agent_simple, NM_TYPE_SECRET_AGENT_OLD) + +#define NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimplePrivate)) + +enum { + REQUEST_SECRETS, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + NMSecretAgentSimple *self; + + gchar *request_id; + NMConnection *connection; + gchar **hints; + NMSecretAgentOldGetSecretsFunc callback; + gpointer callback_data; +} NMSecretAgentSimpleRequest; + +typedef struct { + /* <char *request_id, NMSecretAgentSimpleRequest *request> */ + GHashTable *requests; + + char *path; + gboolean enabled; +} NMSecretAgentSimplePrivate; + +static void +nm_secret_agent_simple_request_free (gpointer data) +{ + NMSecretAgentSimpleRequest *request = data; + + g_object_unref (request->self); + g_object_unref (request->connection); + g_strfreev (request->hints); + + g_slice_free (NMSecretAgentSimpleRequest, request); +} + +static void +nm_secret_agent_simple_init (NMSecretAgentSimple *agent) +{ + NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (agent); + + priv->requests = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, nm_secret_agent_simple_request_free); +} + +static void +nm_secret_agent_simple_finalize (GObject *object) +{ + NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (object); + GError *error; + GHashTableIter iter; + gpointer key; + gpointer value; + + error = g_error_new (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_AGENT_CANCELED, + "The secret agent is going away"); + + g_hash_table_iter_init (&iter, priv->requests); + while (g_hash_table_iter_next (&iter, &key, &value)) { + NMSecretAgentSimpleRequest *request = value; + + request->callback (NM_SECRET_AGENT_OLD (object), + request->connection, + NULL, error, + request->callback_data); + } + + g_hash_table_destroy (priv->requests); + g_error_free (error); + + g_free (priv->path); + + G_OBJECT_CLASS (nm_secret_agent_simple_parent_class)->finalize (object); +} + +static gboolean +strv_has (gchar **haystack, + gchar *needle) +{ + gchar *iter; + + for (iter = *haystack; iter; iter++) { + if (g_strcmp0 (iter, needle) == 0) + return TRUE; + } + + return FALSE; +} + +/** + * NMSecretAgentSimpleSecret: + * @name: the user-visible name of the secret. Eg, "WEP Passphrase". + * @value: the value of the secret + * @password: %TRUE if this secret represents a password, %FALSE + * if it represents non-secret data. + * + * A single "secret" being requested. + */ + +typedef struct { + NMSecretAgentSimpleSecret base; + + NMSetting *setting; + char *property; +} NMSecretAgentSimpleSecretReal; + +static void +nm_secret_agent_simple_secret_free (NMSecretAgentSimpleSecret *secret) +{ + NMSecretAgentSimpleSecretReal *real = (NMSecretAgentSimpleSecretReal *)secret; + + g_free (secret->name); + g_free (secret->prop_name); + g_free (secret->value); + g_free (real->property); + g_clear_object (&real->setting); + + g_slice_free (NMSecretAgentSimpleSecretReal, real); +} + +static NMSecretAgentSimpleSecret * +nm_secret_agent_simple_secret_new (const char *name, + NMSetting *setting, + const char *property, + gboolean password) +{ + NMSecretAgentSimpleSecretReal *real; + + real = g_slice_new0 (NMSecretAgentSimpleSecretReal); + real->base.name = g_strdup (name); + real->base.prop_name = g_strdup_printf ("%s.%s", nm_setting_get_name (setting), property); + real->base.password = password; + + if (setting) { + real->setting = g_object_ref (setting); + real->property = g_strdup (property); + + g_object_get (setting, property, &real->base.value, NULL); + } + + return &real->base; +} + +static gboolean +add_8021x_secrets (NMSecretAgentSimpleRequest *request, + GPtrArray *secrets) +{ + NMSetting8021x *s_8021x = nm_connection_get_setting_802_1x (request->connection); + const char *eap_method; + NMSecretAgentSimpleSecret *secret; + + eap_method = nm_setting_802_1x_get_eap_method (s_8021x, 0); + if (!eap_method) + return FALSE; + + if ( !strcmp (eap_method, "md5") + || !strcmp (eap_method, "leap") + || !strcmp (eap_method, "ttls") + || !strcmp (eap_method, "peap")) { + /* TTLS and PEAP are actually much more complicated, but this complication + * is not visible here since we only care about phase2 authentication + * (and don't even care of which one) + */ + secret = nm_secret_agent_simple_secret_new (_("Username"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_IDENTITY, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } + + if (!strcmp (eap_method, "tls")) { + secret = nm_secret_agent_simple_secret_new (_("Identity"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_IDENTITY, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nm_secret_agent_simple_secret_new (_("Private key password"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } + + return FALSE; +} + +static gboolean +add_wireless_secrets (NMSecretAgentSimpleRequest *request, + GPtrArray *secrets) +{ + NMSettingWirelessSecurity *s_wsec = nm_connection_get_setting_wireless_security (request->connection); + const char *key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + NMSecretAgentSimpleSecret *secret; + + if (!key_mgmt) + return FALSE; + + if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) { + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_wsec), + NM_SETTING_WIRELESS_SECURITY_PSK, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } + + if (!strcmp (key_mgmt, "none")) { + int index; + char *key; + + index = nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec); + key = g_strdup_printf ("wep-key%d", index); + secret = nm_secret_agent_simple_secret_new (_("Key"), + NM_SETTING (s_wsec), + key, + TRUE); + g_free (key); + + g_ptr_array_add (secrets, secret); + return TRUE; + } + + if (!strcmp (key_mgmt, "iee8021x")) { + if (!g_strcmp0 (nm_setting_wireless_security_get_auth_alg (s_wsec), "leap")) { + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_wsec), + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } else + return add_8021x_secrets (request, secrets); + } + + if (!strcmp (key_mgmt, "wpa-eap")) + return add_8021x_secrets (request, secrets); + + return FALSE; +} + +static gboolean +add_pppoe_secrets (NMSecretAgentSimpleRequest *request, + GPtrArray *secrets) +{ + NMSettingPppoe *s_pppoe = nm_connection_get_setting_pppoe (request->connection); + NMSecretAgentSimpleSecret *secret; + + secret = nm_secret_agent_simple_secret_new (_("Username"), + NM_SETTING (s_pppoe), + NM_SETTING_PPPOE_USERNAME, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nm_secret_agent_simple_secret_new (_("Service"), + NM_SETTING (s_pppoe), + NM_SETTING_PPPOE_SERVICE, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_pppoe), + NM_SETTING_PPPOE_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; +} + +static void +request_secrets_from_ui (NMSecretAgentSimpleRequest *request) +{ + GPtrArray *secrets; + NMSecretAgentSimpleSecret *secret; + const char *title; + char *msg; + gboolean ok = TRUE; + + secrets = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_secret_agent_simple_secret_free); + + if (nm_connection_is_type (request->connection, NM_SETTING_WIRELESS_SETTING_NAME)) { + NMSettingWireless *s_wireless; + GBytes *ssid; + char *ssid_utf8; + + s_wireless = nm_connection_get_setting_wireless (request->connection); + ssid = nm_setting_wireless_get_ssid (s_wireless); + ssid_utf8 = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + + title = _("Authentication required by wireless network"); + msg = g_strdup_printf (_("Passwords or encryption keys are required to access the wireless network '%s'."), ssid_utf8); + + ok = add_wireless_secrets (request, secrets); + } else if (nm_connection_is_type (request->connection, NM_SETTING_WIRED_SETTING_NAME)) { + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (request->connection); + + title = _("Wired 802.1X authentication"); + msg = NULL; + + secret = nm_secret_agent_simple_secret_new (_("Network name"), + NM_SETTING (s_con), + NM_SETTING_CONNECTION_ID, + FALSE); + g_ptr_array_add (secrets, secret); + ok = add_8021x_secrets (request, secrets); + } else if (nm_connection_is_type (request->connection, NM_SETTING_PPPOE_SETTING_NAME)) { + title = _("DSL authentication"); + msg = NULL; + + ok = add_pppoe_secrets (request, secrets); + } else if (nm_connection_is_type (request->connection, NM_SETTING_GSM_SETTING_NAME)) { + NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (request->connection); + + if (strv_has (request->hints, "pin")) { + title = _("PIN code required"); + msg = g_strdup (_("PIN code is needed for the mobile broadband device")); + + secret = nm_secret_agent_simple_secret_new (_("PIN"), + NM_SETTING (s_gsm), + NM_SETTING_GSM_PIN, + FALSE); + g_ptr_array_add (secrets, secret); + } else { + title = _("Mobile broadband network password"); + msg = g_strdup_printf (_("A password is required to connect to '%s'."), + nm_connection_get_id (request->connection)); + + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_gsm), + NM_SETTING_GSM_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + } + } else if (nm_connection_is_type (request->connection, NM_SETTING_CDMA_SETTING_NAME)) { + NMSettingCdma *s_cdma = nm_connection_get_setting_cdma (request->connection); + + title = _("Mobile broadband network password"); + msg = g_strdup_printf (_("A password is required to connect to '%s'."), + nm_connection_get_id (request->connection)); + + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_cdma), + NM_SETTING_CDMA_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + } else if (nm_connection_is_type (request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) { + NMSetting *setting; + + setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_GSM_SETTING_NAME); + if (!setting) + setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_CDMA_SETTING_NAME); + + title = _("Mobile broadband network password"); + msg = g_strdup_printf (_("A password is required to connect to '%s'."), + nm_connection_get_id (request->connection)); + + secret = nm_secret_agent_simple_secret_new (_("Password"), + setting, + "password", + TRUE); + g_ptr_array_add (secrets, secret); + } else + ok = FALSE; + + if (!ok) { + g_ptr_array_unref (secrets); + return; + } + + g_signal_emit (request->self, signals[REQUEST_SECRETS], 0, + request->request_id, title, msg, secrets); +} + +static void +nm_secret_agent_simple_get_secrets (NMSecretAgentOld *agent, + NMConnection *connection, + const gchar *connection_path, + const gchar *setting_name, + const gchar **hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentOldGetSecretsFunc callback, + gpointer callback_data) +{ + NMSecretAgentSimple *self = NM_SECRET_AGENT_SIMPLE (agent); + NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self); + NMSecretAgentSimpleRequest *request; + NMSettingConnection *s_con; + const char *connection_type; + char *request_id; + GError *error; + + request_id = g_strdup_printf ("%s/%s", connection_path, setting_name); + if (g_hash_table_lookup (priv->requests, request_id) != NULL) { + /* We already have a request pending for this (connection, setting) */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED, + "Request for %s secrets already pending", request_id); + nope: + callback (agent, connection, NULL, error, callback_data); + g_error_free (error); + g_free (request_id); + return; + } + + s_con = nm_connection_get_setting_connection (connection); + connection_type = nm_setting_connection_get_connection_type (s_con); + + if (!strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME)) { + /* We don't support VPN secrets yet */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, + "VPN secrets not supported"); + goto nope; + } + + if (!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)) { + /* We don't do stored passwords */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, + "Stored passwords not supported"); + goto nope; + } + + request = g_slice_new (NMSecretAgentSimpleRequest); + request->self = g_object_ref (self); + request->connection = g_object_ref (connection); + request->hints = g_strdupv ((gchar **)hints); + request->callback = callback; + request->callback_data = callback_data; + request->request_id = request_id; + g_hash_table_replace (priv->requests, request->request_id, request); + + if (priv->enabled) + request_secrets_from_ui (request); +} + +/** + * nm_secret_agent_simple_response: + * @self: the #NMSecretAgentSimple + * @request_id: the request ID being responded to + * @secrets: (allow-none): the array of secrets, or %NULL + * + * Response to a #NMSecretAgentSimple::get-secrets signal. + * + * If the user provided secrets, the caller should set the + * corresponding <literal>value</literal> fields in the + * #NMSecretAgentSimpleSecrets (freeing any initial values they had), and + * pass the array to nm_secret_agent_simple_response(). If the user + * cancelled the request, @secrets should be NULL. + */ +void +nm_secret_agent_simple_response (NMSecretAgentSimple *self, + const char *request_id, + GPtrArray *secrets) +{ + NMSecretAgentSimplePrivate *priv; + NMSecretAgentSimpleRequest *request; + GVariant *dict = NULL; + GError *error = NULL; + int i; + + g_return_if_fail (NM_IS_SECRET_AGENT_SIMPLE (self)); + + priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self); + request = g_hash_table_lookup (priv->requests, request_id); + g_return_if_fail (request != NULL); + + if (secrets) { + GVariantBuilder conn_builder, *setting_builder; + GHashTable *settings; + GHashTableIter iter; + const char *name; + + settings = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < secrets->len; i++) { + NMSecretAgentSimpleSecretReal *secret = secrets->pdata[i]; + + setting_builder = g_hash_table_lookup (settings, nm_setting_get_name (secret->setting)); + if (!setting_builder) { + setting_builder = g_variant_builder_new (NM_VARIANT_TYPE_SETTING); + g_hash_table_insert (settings, (char *) nm_setting_get_name (secret->setting), + setting_builder); + } + + g_variant_builder_add (setting_builder, "{sv}", + secret->property, + g_variant_new_string (secret->base.value)); + } + + g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION); + g_hash_table_iter_init (&iter, settings); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &setting_builder)) + g_variant_builder_add (&conn_builder, "{sa{sv}}", name, setting_builder); + dict = g_variant_builder_end (&conn_builder); + g_hash_table_destroy (settings); + } else { + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED, + "User cancelled"); + } + + request->callback (NM_SECRET_AGENT_OLD (self), request->connection, dict, error, request->callback_data); + + g_clear_error (&error); + g_hash_table_remove (priv->requests, request_id); +} + +static void +nm_secret_agent_simple_cancel_get_secrets (NMSecretAgentOld *agent, + const gchar *connection_path, + const gchar *setting_name) +{ + /* We don't support cancellation. Sorry! */ +} + +static void +nm_secret_agent_simple_save_secrets (NMSecretAgentOld *agent, + NMConnection *connection, + const gchar *connection_path, + NMSecretAgentOldSaveSecretsFunc callback, + gpointer callback_data) +{ + /* We don't support secret storage */ + callback (agent, connection, NULL, callback_data); +} + +static void +nm_secret_agent_simple_delete_secrets (NMSecretAgentOld *agent, + NMConnection *connection, + const gchar *connection_path, + NMSecretAgentOldDeleteSecretsFunc callback, + gpointer callback_data) +{ + /* We don't support secret storage, so there's nothing to delete. */ + callback (agent, connection, NULL, callback_data); +} + +/** + * nm_secret_agent_simple_enable: + * @self: the #NMSecretAgentSimple + * @path: (allow-none): the path of the connection (if any) to handle secrets + * for. If %NULL, secrets for any connection will be handled. + * + * Enables servicing the requests including the already queued ones. If @path + * is given, the agent will only handle requests for connections that match + * @path. + */ +void +nm_secret_agent_simple_enable (NMSecretAgentSimple *self, const char *path) +{ + NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self); + GList *requests, *iter; + GError *error; + + if (g_strcmp0 (path, priv->path) != 0) { + g_free (priv->path); + priv->path = g_strdup (path); + } + + if (priv->enabled) + return; + priv->enabled = TRUE; + + /* Service pending secret requests. */ + requests = g_hash_table_get_values (priv->requests); + for (iter = requests; iter; iter = g_list_next (iter)) { + NMSecretAgentSimpleRequest *request = iter->data; + + if (g_str_has_prefix (request->request_id, priv->path)) { + request_secrets_from_ui (request); + } else { + /* We only handle requests for connection with @path if set. */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED, + "Request for %s secrets doesn't match path %s", + request->request_id, priv->path); + request->callback (NM_SECRET_AGENT_OLD (self), request->connection, NULL, error, request->callback_data); + g_hash_table_remove (priv->requests, request->request_id); + g_error_free (error); + } + } + g_list_free (requests); +} + +void +nm_secret_agent_simple_class_init (NMSecretAgentSimpleClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + NMSecretAgentOldClass *agent_class = NM_SECRET_AGENT_OLD_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMSecretAgentSimplePrivate)); + + gobject_class->finalize = nm_secret_agent_simple_finalize; + + agent_class->get_secrets = nm_secret_agent_simple_get_secrets; + agent_class->cancel_get_secrets = nm_secret_agent_simple_cancel_get_secrets; + agent_class->save_secrets = nm_secret_agent_simple_save_secrets; + agent_class->delete_secrets = nm_secret_agent_simple_delete_secrets; + + /** + * NMSecretAgentSimple::request-secrets: + * @agent: the #NMSecretAgentSimple + * @request_id: request ID, to eventually pass to + * nm_secret_agent_simple_response(). + * @title: a title for the password dialog + * @prompt: a prompt message for the password dialog + * @secrets: (element-type #NMSecretAgentSimpleSecret): array of secrets + * being requested. + * + * Emitted when the agent requires secrets from the user. + * + * The application should ask user for the secrets. For example, + * nmtui should create a password dialog (#NmtPasswordDialog) + * with the given title and prompt, and an entry for each + * element of @secrets. If any of the secrets already have a + * <literal>value</literal> filled in, the corresponding entry + * should be initialized to that value. + * + * When the dialog is complete, the app must call + * nm_secret_agent_simple_response() with the results. + */ + signals[REQUEST_SECRETS] = g_signal_new ("request-secrets", + G_TYPE_FROM_CLASS (klass), + 0, 0, NULL, NULL, NULL, + G_TYPE_NONE, + 4, + G_TYPE_STRING, /* request_id */ + G_TYPE_STRING, /* title */ + G_TYPE_STRING, /* prompt */ + G_TYPE_PTR_ARRAY); +} + +/** + * nm_secret_agent_simple_new: + * @name: the identifier of secret agent + * + * Creates a new #NMSecretAgentSimple. It does not serve any requests until + * nm_secret_agent_simple_enable() is called. + * + * Returns: a new #NMSecretAgentSimple if the agent creation is successful + * or %NULL in case of a failure. + */ +NMSecretAgentOld * +nm_secret_agent_simple_new (const char *name) +{ + return g_initable_new (NM_TYPE_SECRET_AGENT_SIMPLE, NULL, NULL, + NM_SECRET_AGENT_OLD_IDENTIFIER, name, + NULL); +} diff --git a/clients/common/nm-secret-agent-simple.h b/clients/common/nm-secret-agent-simple.h new file mode 100644 index 00000000..81fec651 --- /dev/null +++ b/clients/common/nm-secret-agent-simple.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 - 2014 Red Hat, Inc. + */ + +#ifndef __NM_SECRET_AGENT_SIMPLE_H__ +#define __NM_SECRET_AGENT_SIMPLE_H__ + +#include <NetworkManager.h> +#include <nm-secret-agent-old.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SECRET_AGENT_SIMPLE (nm_secret_agent_simple_get_type ()) +#define NM_SECRET_AGENT_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimple)) +#define NM_SECRET_AGENT_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimpleClass)) +#define NM_IS_SECRET_AGENT_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SECRET_AGENT_SIMPLE)) +#define NM_IS_SECRET_AGENT_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SECRET_AGENT_SIMPLE)) +#define NM_SECRET_AGENT_SIMPLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimpleClass)) + +typedef struct { + NMSecretAgentOld parent; + +} NMSecretAgentSimple; + +typedef struct { + NMSecretAgentOldClass parent; + +} NMSecretAgentSimpleClass; + +typedef struct { + char *name, *prop_name, *value; + gboolean password; +} NMSecretAgentSimpleSecret; + +GType nm_secret_agent_simple_get_type (void); + +NMSecretAgentOld *nm_secret_agent_simple_new (const char *name); + +void nm_secret_agent_simple_response (NMSecretAgentSimple *self, + const char *request_id, + GPtrArray *secrets); + +void nm_secret_agent_simple_enable (NMSecretAgentSimple *self, + const char *path); + +G_END_DECLS + +#endif /* __NM_SECRET_AGENT_SIMPLE_H__ */ diff --git a/clients/nm-online.c b/clients/nm-online.c new file mode 100644 index 00000000..bb0c9706 --- /dev/null +++ b/clients/nm-online.c @@ -0,0 +1,249 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Novell, Inc. + * Copyright (C) 2008 - 2014 Red Hat, Inc. + * + */ + +/* + * nm-online.c - Are we online? + * + * Return values: + * + * 0 : already online or connection established within given timeout + * 1 : offline or not online within given timeout + * 2 : unspecified error + * + * Robert Love <rml@novell.com> + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <getopt.h> +#include <locale.h> + +#include <glib/gi18n.h> + +#include <NetworkManager.h> + +#define PROGRESS_STEPS 15 +#define WAIT_STARTUP_TAG "wait-startup" + +typedef struct +{ + gint64 start_timestamp_ms; + gint64 end_timestamp_ms; + gint64 progress_step_duration; + gboolean quiet; +} Timeout; + +static void +client_properties_changed (GObject *object, + GParamSpec *pspec, + gpointer loop) +{ + NMClient *client = NM_CLIENT (object); + NMState state; + gboolean wait_startup = GPOINTER_TO_UINT (g_object_get_data (object, WAIT_STARTUP_TAG)); + + if (!nm_client_get_nm_running (client)) + return; + + if (wait_startup) { + if (!nm_client_get_startup (client)) + g_main_loop_quit (loop); + } else { + state = nm_client_get_state (client); + if ( state == NM_STATE_CONNECTED_LOCAL + || state == NM_STATE_CONNECTED_SITE + || state == NM_STATE_CONNECTED_GLOBAL) + g_main_loop_quit (loop); + } +} + +static gboolean +handle_timeout (gpointer data) +{ + const Timeout *timeout = data; + const gint64 now = g_get_monotonic_time () / (G_USEC_PER_SEC / 1000); + gint64 remaining_ms = timeout->end_timestamp_ms - now; + const gint64 elapsed_ms = now - timeout->start_timestamp_ms; + int progress_next_step_i = 0; + + if (!timeout->quiet) { + int i; + + /* calculate the next step (not the current): floor()+1 */ + progress_next_step_i = (elapsed_ms / timeout->progress_step_duration) + 1; + progress_next_step_i = MIN (progress_next_step_i, PROGRESS_STEPS); + + g_print (_("\rConnecting")); + for (i = 0; i < PROGRESS_STEPS; i++) + putchar (i < progress_next_step_i ? '.' : ' '); + g_print (" %4lds", (long) (MAX (0, remaining_ms) / 1000)); + fflush (stdout); + } + + if (remaining_ms <= 3) { + if (!timeout->quiet) + g_print ("\n"); + exit (1); + } + + if (!timeout->quiet) { + gint64 rem; + + /* synchronize the timeout with the ticking of the seconds. */ + rem = remaining_ms % 1000; + if (rem <= 3) + rem = rem + G_USEC_PER_SEC; + rem = rem + 10; /* add small offset to awake a bit after the second ticks */ + if (remaining_ms > rem) + remaining_ms = rem; + + /* synchronize the timeout with the steps of the progress bar. */ + rem = (progress_next_step_i * timeout->progress_step_duration) - elapsed_ms; + if (rem <= 3) + rem = rem + timeout->progress_step_duration; + rem = rem + 10; /* add small offset to awake a bit after the time out */ + if (remaining_ms > rem) + remaining_ms = rem; + } + + g_timeout_add (remaining_ms, handle_timeout, (void *) timeout); + return G_SOURCE_REMOVE; +} + +int +main (int argc, char *argv[]) +{ + int t_secs = 30; + gboolean exit_no_nm = FALSE; + gboolean wait_startup = FALSE; + Timeout timeout; + GOptionContext *opt_ctx = NULL; + gboolean success; + NMClient *client; + NMState state = NM_STATE_UNKNOWN; + GMainLoop *loop; + gint64 remaining_ms; + GError *error = NULL; + + GOptionEntry options[] = { + {"timeout", 't', 0, G_OPTION_ARG_INT, &t_secs, N_("Time to wait for a connection, in seconds (without the option, default value is 30)"), "<timeout>"}, + {"exit", 'x', 0, G_OPTION_ARG_NONE, &exit_no_nm, N_("Exit immediately if NetworkManager is not running or connecting"), NULL}, + {"quiet", 'q', 0, G_OPTION_ARG_NONE, &timeout.quiet, N_("Don't print anything"), NULL}, + {"wait-for-startup", 's', 0, G_OPTION_ARG_NONE, &wait_startup, N_("Wait for NetworkManager startup instead of a connection"), NULL}, + {NULL} + }; + + timeout.start_timestamp_ms = g_get_monotonic_time () / (G_USEC_PER_SEC / 1000); + timeout.quiet = FALSE; + + /* Set locale to be able to use environment variables */ + setlocale (LC_ALL, ""); + + bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + opt_ctx = g_option_context_new (NULL); + g_option_context_set_translation_domain (opt_ctx, GETTEXT_PACKAGE); + g_option_context_set_ignore_unknown_options (opt_ctx, FALSE); + g_option_context_set_help_enabled (opt_ctx, TRUE); + g_option_context_add_main_entries (opt_ctx, options, NULL); + + g_option_context_set_summary (opt_ctx, + _("Waits for NetworkManager to finish activating startup network connections.")); + + success = g_option_context_parse (opt_ctx, &argc, &argv, NULL); + g_option_context_free (opt_ctx); + + if (!success) { + g_printerr ("%s: %s\n", argv[0], + _("Invalid option. Please use --help to see a list of valid options.")); + return 2; + } + + if (t_secs < 0 || t_secs > 3600) { + g_printerr ("%s: %s\n", argv[0], + _("Invalid option. Please use --help to see a list of valid options.")); + return 2; + } + remaining_ms = t_secs * 1000; + +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + + client = nm_client_new (NULL, &error); + if (!client) { + g_printerr (_("Error: Could not create NMClient object: %s."), error->message); + g_error_free (error); + return 2; + } + + loop = g_main_loop_new (NULL, FALSE); + + g_object_set_data (G_OBJECT (client), WAIT_STARTUP_TAG, GUINT_TO_POINTER (wait_startup)); + state = nm_client_get_state (client); + if (!nm_client_get_nm_running (client)) { + if (exit_no_nm) { + g_object_unref (client); + return 1; + } + } else if (wait_startup) { + if (!nm_client_get_startup (client)) { + g_object_unref (client); + return 0; + } + } else { + if ( state == NM_STATE_CONNECTED_LOCAL + || state == NM_STATE_CONNECTED_SITE + || state == NM_STATE_CONNECTED_GLOBAL) { + g_object_unref (client); + return 0; + } + } + if (exit_no_nm && (state != NM_STATE_CONNECTING)) { + g_object_unref (client); + return 1; + } + + if (remaining_ms == 0) { + g_object_unref (client); + return 1; + } + + g_signal_connect (client, "notify", + G_CALLBACK (client_properties_changed), loop); + + timeout.end_timestamp_ms = timeout.start_timestamp_ms + remaining_ms; + timeout.progress_step_duration = (timeout.end_timestamp_ms - timeout.start_timestamp_ms + PROGRESS_STEPS/2) / PROGRESS_STEPS; + + g_timeout_add (timeout.quiet ? remaining_ms : 0, + handle_timeout, &timeout); + + g_main_loop_run (loop); + g_main_loop_unref (loop); + + g_object_unref (client); + + return 0; +} diff --git a/clients/tui/Makefile.am b/clients/tui/Makefile.am new file mode 100644 index 00000000..ad9335da --- /dev/null +++ b/clients/tui/Makefile.am @@ -0,0 +1,128 @@ +if BUILD_NMTUI + +SUBDIRS = newt . + +AM_CPPFLAGS= \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/libnm-core \ + -I$(top_builddir)/libnm-core \ + -I$(top_srcdir)/libnm \ + -I$(top_builddir)/libnm \ + -I$(srcdir)/newt \ + -I$(top_srcdir)/clients/common \ + $(GLIB_CFLAGS) \ + $(NEWT_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -DG_LOG_DOMAIN=\""nmtui"\" \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ + -DLOCALEDIR=\""$(localedir)"\" \ + $(NULL) + +bin_PROGRAMS = nmtui +links = nmtui-edit nmtui-connect nmtui-hostname + +install-exec-hook: + for link in $(links); do \ + ln -f $(DESTDIR)$(bindir)/nmtui $(DESTDIR)$(bindir)/$$link; \ + done + +uninstall-hook: + for link in $(links); do \ + rm -f $(DESTDIR)$(bindir)/$$link; \ + done + +nmtui_SOURCES = \ + nmtui.c \ + nmtui.h \ + nmtui-connect.c \ + nmtui-connect.h \ + nmtui-edit.c \ + nmtui-edit.h \ + nmtui-hostname.c \ + nmtui-hostname.h \ + \ + nm-editor-bindings.c \ + nm-editor-bindings.h \ + nm-editor-utils.c \ + nm-editor-utils.h \ + \ + nmt-address-list.c \ + nmt-address-list.h \ + nmt-connect-connection-list.c \ + nmt-connect-connection-list.h \ + nmt-device-entry.c \ + nmt-device-entry.h \ + nmt-edit-connection-list.c \ + nmt-edit-connection-list.h \ + nmt-editor-grid.c \ + nmt-editor-grid.h \ + nmt-editor-page.c \ + nmt-editor-page.h \ + nmt-editor-page-device.c \ + nmt-editor-page-device.h \ + nmt-editor-section.c \ + nmt-editor-section.h \ + nmt-editor.c \ + nmt-editor.h \ + nmt-ip-entry.c \ + nmt-ip-entry.h \ + nmt-mac-entry.c \ + nmt-mac-entry.h \ + nmt-mtu-entry.c \ + nmt-mtu-entry.h \ + nmt-page-bond.c \ + nmt-page-bond.h \ + nmt-page-bridge.c \ + nmt-page-bridge.h \ + nmt-page-bridge-port.c \ + nmt-page-bridge-port.h \ + nmt-page-dsl.c \ + nmt-page-dsl.h \ + nmt-page-ethernet.c \ + nmt-page-ethernet.h \ + nmt-page-infiniband.c \ + nmt-page-infiniband.h \ + nmt-page-ip4.c \ + nmt-page-ip4.h \ + nmt-page-ip6.c \ + nmt-page-ip6.h \ + nmt-page-ppp.c \ + nmt-page-ppp.h \ + nmt-page-team.c \ + nmt-page-team.h \ + nmt-page-team-port.c \ + nmt-page-team-port.h \ + nmt-page-vlan.c \ + nmt-page-vlan.h \ + nmt-page-wifi.c \ + nmt-page-wifi.h \ + nmt-password-dialog.c \ + nmt-password-dialog.h \ + nmt-password-fields.c \ + nmt-password-fields.h \ + nmt-route-editor.c \ + nmt-route-editor.h \ + nmt-route-entry.c \ + nmt-route-entry.h \ + nmt-route-table.c \ + nmt-route-table.h \ + nmt-slave-list.c \ + nmt-slave-list.h \ + nmt-utils.c \ + nmt-utils.h \ + nmt-widget-list.c \ + nmt-widget-list.h \ + $(srcdir)/../common/nm-secret-agent-simple.c \ + $(srcdir)/../common/nm-secret-agent-simple.h \ + $(NULL) + +nmtui_LDADD = \ + $(top_builddir)/libnm/libnm.la \ + $(builddir)/newt/libnmt-newt.a \ + $(GUDEV_LIBS) \ + $(NEWT_LIBS) \ + $(GLIB_LIBS) \ + $(NULL) + +endif diff --git a/clients/tui/Makefile.in b/clients/tui/Makefile.in new file mode 100644 index 00000000..86b2a3b9 --- /dev/null +++ b/clients/tui/Makefile.in @@ -0,0 +1,1131 @@ +# Makefile.in generated by automake 1.13.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@BUILD_NMTUI_TRUE@bin_PROGRAMS = nmtui$(EXEEXT) +subdir = clients/tui +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/build-aux/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gnome-code-coverage.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am__nmtui_SOURCES_DIST = nmtui.c nmtui.h nmtui-connect.c \ + nmtui-connect.h nmtui-edit.c nmtui-edit.h nmtui-hostname.c \ + nmtui-hostname.h nm-editor-bindings.c nm-editor-bindings.h \ + nm-editor-utils.c nm-editor-utils.h nmt-address-list.c \ + nmt-address-list.h nmt-connect-connection-list.c \ + nmt-connect-connection-list.h nmt-device-entry.c \ + nmt-device-entry.h nmt-edit-connection-list.c \ + nmt-edit-connection-list.h nmt-editor-grid.c nmt-editor-grid.h \ + nmt-editor-page.c nmt-editor-page.h nmt-editor-page-device.c \ + nmt-editor-page-device.h nmt-editor-section.c \ + nmt-editor-section.h nmt-editor.c nmt-editor.h nmt-ip-entry.c \ + nmt-ip-entry.h nmt-mac-entry.c nmt-mac-entry.h nmt-mtu-entry.c \ + nmt-mtu-entry.h nmt-page-bond.c nmt-page-bond.h \ + nmt-page-bridge.c nmt-page-bridge.h nmt-page-bridge-port.c \ + nmt-page-bridge-port.h nmt-page-dsl.c nmt-page-dsl.h \ + nmt-page-ethernet.c nmt-page-ethernet.h nmt-page-infiniband.c \ + nmt-page-infiniband.h nmt-page-ip4.c nmt-page-ip4.h \ + nmt-page-ip6.c nmt-page-ip6.h nmt-page-ppp.c nmt-page-ppp.h \ + nmt-page-team.c nmt-page-team.h nmt-page-team-port.c \ + nmt-page-team-port.h nmt-page-vlan.c nmt-page-vlan.h \ + nmt-page-wifi.c nmt-page-wifi.h nmt-password-dialog.c \ + nmt-password-dialog.h nmt-password-fields.c \ + nmt-password-fields.h nmt-route-editor.c nmt-route-editor.h \ + nmt-route-entry.c nmt-route-entry.h nmt-route-table.c \ + nmt-route-table.h nmt-slave-list.c nmt-slave-list.h \ + nmt-utils.c nmt-utils.h nmt-widget-list.c nmt-widget-list.h \ + $(srcdir)/../common/nm-secret-agent-simple.c \ + $(srcdir)/../common/nm-secret-agent-simple.h +@BUILD_NMTUI_TRUE@am_nmtui_OBJECTS = nmtui.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmtui-connect.$(OBJEXT) nmtui-edit.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmtui-hostname.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nm-editor-bindings.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nm-editor-utils.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-address-list.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-connect-connection-list.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-device-entry.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-edit-connection-list.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-editor-grid.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-editor-page.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-editor-page-device.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-editor-section.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-editor.$(OBJEXT) nmt-ip-entry.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-mac-entry.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-mtu-entry.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-bond.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-bridge.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-bridge-port.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-dsl.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-ethernet.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-infiniband.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-ip4.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-ip6.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-ppp.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-team.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-team-port.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-vlan.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-page-wifi.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-password-dialog.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-password-fields.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-route-editor.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-route-entry.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-route-table.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-slave-list.$(OBJEXT) nmt-utils.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nmt-widget-list.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nm-secret-agent-simple.$(OBJEXT) +nmtui_OBJECTS = $(am_nmtui_OBJECTS) +am__DEPENDENCIES_1 = +@BUILD_NMTUI_TRUE@nmtui_DEPENDENCIES = $(top_builddir)/libnm/libnm.la \ +@BUILD_NMTUI_TRUE@ $(builddir)/newt/libnmt-newt.a \ +@BUILD_NMTUI_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +@BUILD_NMTUI_TRUE@ $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(nmtui_SOURCES) +DIST_SOURCES = $(am__nmtui_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = newt . +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@ +BLUEZ5_LIBS = @BLUEZ5_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CKDB_PATH = @CKDB_PATH@ +CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ +CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ +CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ +DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ +DLLTOOL = @DLLTOOL@ +DNSMASQ_PATH = @DNSMASQ_PATH@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MAKEFILE = @GLIB_MAKEFILE@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@ +INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@ +INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@ +INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@ +INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ +INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@ +INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@ +INTROSPECTION_LIBS = @INTROSPECTION_LIBS@ +INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ +INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ +INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ +IPTABLES_PATH = @IPTABLES_PATH@ +IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@ +IWMX_SDK_LIBS = @IWMX_SDK_LIBS@ +KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBNDP_CFLAGS = @LIBNDP_CFLAGS@ +LIBNDP_LIBS = @LIBNDP_LIBS@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ +LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ +LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@ +MM_GLIB_LIBS = @MM_GLIB_LIBS@ +MOC = @MOC@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NEWT_CFLAGS = @NEWT_CFLAGS@ +NEWT_LIBS = @NEWT_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@ +NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ +NM_MICRO_VERSION = @NM_MICRO_VERSION@ +NM_MINOR_VERSION = @NM_MINOR_VERSION@ +NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@ +NM_VERSION = @NM_VERSION@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PPPD_PATH = @PPPD_PATH@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +PPPOE_PATH = @PPPOE_PATH@ +QT_CFLAGS = @QT_CFLAGS@ +QT_LIBS = @QT_LIBS@ +RANLIB = @RANLIB@ +READLINE_LIBS = @READLINE_LIBS@ +SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@ +SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@ +SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@ +SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@ +SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@ +SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_DIR = @UDEV_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VALGRIND_RULES = @VALGRIND_RULES@ +VAPIGEN = @VAPIGEN@ +VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@ +VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +intltool__v_merge_options_ = @intltool__v_merge_options_@ +intltool__v_merge_options_0 = @intltool__v_merge_options_0@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +nmbinary = @nmbinary@ +nmconfdir = @nmconfdir@ +nmdatadir = @nmdatadir@ +nmrundir = @nmrundir@ +nmstatedir = @nmstatedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_dhclient = @with_dhclient@ +with_dhcpcd = @with_dhcpcd@ +with_netconfig = @with_netconfig@ +with_resolvconf = @with_resolvconf@ +with_valgrind = @with_valgrind@ +@BUILD_NMTUI_TRUE@SUBDIRS = newt . +@BUILD_NMTUI_TRUE@AM_CPPFLAGS = \ +@BUILD_NMTUI_TRUE@ -I$(top_srcdir) \ +@BUILD_NMTUI_TRUE@ -I$(top_srcdir)/include \ +@BUILD_NMTUI_TRUE@ -I$(top_srcdir)/libnm-core \ +@BUILD_NMTUI_TRUE@ -I$(top_builddir)/libnm-core \ +@BUILD_NMTUI_TRUE@ -I$(top_srcdir)/libnm \ +@BUILD_NMTUI_TRUE@ -I$(top_builddir)/libnm \ +@BUILD_NMTUI_TRUE@ -I$(srcdir)/newt \ +@BUILD_NMTUI_TRUE@ -I$(top_srcdir)/clients/common \ +@BUILD_NMTUI_TRUE@ $(GLIB_CFLAGS) \ +@BUILD_NMTUI_TRUE@ $(NEWT_CFLAGS) \ +@BUILD_NMTUI_TRUE@ $(GUDEV_CFLAGS) \ +@BUILD_NMTUI_TRUE@ -DG_LOG_DOMAIN=\""nmtui"\" \ +@BUILD_NMTUI_TRUE@ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ +@BUILD_NMTUI_TRUE@ -DLOCALEDIR=\""$(localedir)"\" \ +@BUILD_NMTUI_TRUE@ $(NULL) + +@BUILD_NMTUI_TRUE@links = nmtui-edit nmtui-connect nmtui-hostname +@BUILD_NMTUI_TRUE@nmtui_SOURCES = \ +@BUILD_NMTUI_TRUE@ nmtui.c \ +@BUILD_NMTUI_TRUE@ nmtui.h \ +@BUILD_NMTUI_TRUE@ nmtui-connect.c \ +@BUILD_NMTUI_TRUE@ nmtui-connect.h \ +@BUILD_NMTUI_TRUE@ nmtui-edit.c \ +@BUILD_NMTUI_TRUE@ nmtui-edit.h \ +@BUILD_NMTUI_TRUE@ nmtui-hostname.c \ +@BUILD_NMTUI_TRUE@ nmtui-hostname.h \ +@BUILD_NMTUI_TRUE@ \ +@BUILD_NMTUI_TRUE@ nm-editor-bindings.c \ +@BUILD_NMTUI_TRUE@ nm-editor-bindings.h \ +@BUILD_NMTUI_TRUE@ nm-editor-utils.c \ +@BUILD_NMTUI_TRUE@ nm-editor-utils.h \ +@BUILD_NMTUI_TRUE@ \ +@BUILD_NMTUI_TRUE@ nmt-address-list.c \ +@BUILD_NMTUI_TRUE@ nmt-address-list.h \ +@BUILD_NMTUI_TRUE@ nmt-connect-connection-list.c \ +@BUILD_NMTUI_TRUE@ nmt-connect-connection-list.h \ +@BUILD_NMTUI_TRUE@ nmt-device-entry.c \ +@BUILD_NMTUI_TRUE@ nmt-device-entry.h \ +@BUILD_NMTUI_TRUE@ nmt-edit-connection-list.c \ +@BUILD_NMTUI_TRUE@ nmt-edit-connection-list.h \ +@BUILD_NMTUI_TRUE@ nmt-editor-grid.c \ +@BUILD_NMTUI_TRUE@ nmt-editor-grid.h \ +@BUILD_NMTUI_TRUE@ nmt-editor-page.c \ +@BUILD_NMTUI_TRUE@ nmt-editor-page.h \ +@BUILD_NMTUI_TRUE@ nmt-editor-page-device.c \ +@BUILD_NMTUI_TRUE@ nmt-editor-page-device.h \ +@BUILD_NMTUI_TRUE@ nmt-editor-section.c \ +@BUILD_NMTUI_TRUE@ nmt-editor-section.h \ +@BUILD_NMTUI_TRUE@ nmt-editor.c \ +@BUILD_NMTUI_TRUE@ nmt-editor.h \ +@BUILD_NMTUI_TRUE@ nmt-ip-entry.c \ +@BUILD_NMTUI_TRUE@ nmt-ip-entry.h \ +@BUILD_NMTUI_TRUE@ nmt-mac-entry.c \ +@BUILD_NMTUI_TRUE@ nmt-mac-entry.h \ +@BUILD_NMTUI_TRUE@ nmt-mtu-entry.c \ +@BUILD_NMTUI_TRUE@ nmt-mtu-entry.h \ +@BUILD_NMTUI_TRUE@ nmt-page-bond.c \ +@BUILD_NMTUI_TRUE@ nmt-page-bond.h \ +@BUILD_NMTUI_TRUE@ nmt-page-bridge.c \ +@BUILD_NMTUI_TRUE@ nmt-page-bridge.h \ +@BUILD_NMTUI_TRUE@ nmt-page-bridge-port.c \ +@BUILD_NMTUI_TRUE@ nmt-page-bridge-port.h \ +@BUILD_NMTUI_TRUE@ nmt-page-dsl.c \ +@BUILD_NMTUI_TRUE@ nmt-page-dsl.h \ +@BUILD_NMTUI_TRUE@ nmt-page-ethernet.c \ +@BUILD_NMTUI_TRUE@ nmt-page-ethernet.h \ +@BUILD_NMTUI_TRUE@ nmt-page-infiniband.c \ +@BUILD_NMTUI_TRUE@ nmt-page-infiniband.h \ +@BUILD_NMTUI_TRUE@ nmt-page-ip4.c \ +@BUILD_NMTUI_TRUE@ nmt-page-ip4.h \ +@BUILD_NMTUI_TRUE@ nmt-page-ip6.c \ +@BUILD_NMTUI_TRUE@ nmt-page-ip6.h \ +@BUILD_NMTUI_TRUE@ nmt-page-ppp.c \ +@BUILD_NMTUI_TRUE@ nmt-page-ppp.h \ +@BUILD_NMTUI_TRUE@ nmt-page-team.c \ +@BUILD_NMTUI_TRUE@ nmt-page-team.h \ +@BUILD_NMTUI_TRUE@ nmt-page-team-port.c \ +@BUILD_NMTUI_TRUE@ nmt-page-team-port.h \ +@BUILD_NMTUI_TRUE@ nmt-page-vlan.c \ +@BUILD_NMTUI_TRUE@ nmt-page-vlan.h \ +@BUILD_NMTUI_TRUE@ nmt-page-wifi.c \ +@BUILD_NMTUI_TRUE@ nmt-page-wifi.h \ +@BUILD_NMTUI_TRUE@ nmt-password-dialog.c \ +@BUILD_NMTUI_TRUE@ nmt-password-dialog.h \ +@BUILD_NMTUI_TRUE@ nmt-password-fields.c \ +@BUILD_NMTUI_TRUE@ nmt-password-fields.h \ +@BUILD_NMTUI_TRUE@ nmt-route-editor.c \ +@BUILD_NMTUI_TRUE@ nmt-route-editor.h \ +@BUILD_NMTUI_TRUE@ nmt-route-entry.c \ +@BUILD_NMTUI_TRUE@ nmt-route-entry.h \ +@BUILD_NMTUI_TRUE@ nmt-route-table.c \ +@BUILD_NMTUI_TRUE@ nmt-route-table.h \ +@BUILD_NMTUI_TRUE@ nmt-slave-list.c \ +@BUILD_NMTUI_TRUE@ nmt-slave-list.h \ +@BUILD_NMTUI_TRUE@ nmt-utils.c \ +@BUILD_NMTUI_TRUE@ nmt-utils.h \ +@BUILD_NMTUI_TRUE@ nmt-widget-list.c \ +@BUILD_NMTUI_TRUE@ nmt-widget-list.h \ +@BUILD_NMTUI_TRUE@ $(srcdir)/../common/nm-secret-agent-simple.c \ +@BUILD_NMTUI_TRUE@ $(srcdir)/../common/nm-secret-agent-simple.h \ +@BUILD_NMTUI_TRUE@ $(NULL) + +@BUILD_NMTUI_TRUE@nmtui_LDADD = \ +@BUILD_NMTUI_TRUE@ $(top_builddir)/libnm/libnm.la \ +@BUILD_NMTUI_TRUE@ $(builddir)/newt/libnmt-newt.a \ +@BUILD_NMTUI_TRUE@ $(GUDEV_LIBS) \ +@BUILD_NMTUI_TRUE@ $(NEWT_LIBS) \ +@BUILD_NMTUI_TRUE@ $(GLIB_LIBS) \ +@BUILD_NMTUI_TRUE@ $(NULL) + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu clients/tui/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu clients/tui/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +nmtui$(EXEEXT): $(nmtui_OBJECTS) $(nmtui_DEPENDENCIES) $(EXTRA_nmtui_DEPENDENCIES) + @rm -f nmtui$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(nmtui_OBJECTS) $(nmtui_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-editor-bindings.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-editor-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-secret-agent-simple.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-address-list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-connect-connection-list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-device-entry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-edit-connection-list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-editor-grid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-editor-page-device.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-editor-page.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-editor-section.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-editor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-ip-entry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-mac-entry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-mtu-entry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-bond.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-bridge-port.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-bridge.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-dsl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-ethernet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-infiniband.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-ip4.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-ip6.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-ppp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-team-port.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-team.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-vlan.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-wifi.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-password-dialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-password-fields.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-route-editor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-route-entry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-route-table.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-slave-list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-widget-list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmtui-connect.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmtui-edit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmtui-hostname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmtui.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +nm-secret-agent-simple.o: $(srcdir)/../common/nm-secret-agent-simple.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-secret-agent-simple.o -MD -MP -MF $(DEPDIR)/nm-secret-agent-simple.Tpo -c -o nm-secret-agent-simple.o `test -f '$(srcdir)/../common/nm-secret-agent-simple.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-secret-agent-simple.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-secret-agent-simple.Tpo $(DEPDIR)/nm-secret-agent-simple.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(srcdir)/../common/nm-secret-agent-simple.c' object='nm-secret-agent-simple.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-secret-agent-simple.o `test -f '$(srcdir)/../common/nm-secret-agent-simple.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-secret-agent-simple.c + +nm-secret-agent-simple.obj: $(srcdir)/../common/nm-secret-agent-simple.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-secret-agent-simple.obj -MD -MP -MF $(DEPDIR)/nm-secret-agent-simple.Tpo -c -o nm-secret-agent-simple.obj `if test -f '$(srcdir)/../common/nm-secret-agent-simple.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-secret-agent-simple.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-secret-agent-simple.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-secret-agent-simple.Tpo $(DEPDIR)/nm-secret-agent-simple.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(srcdir)/../common/nm-secret-agent-simple.c' object='nm-secret-agent-simple.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-secret-agent-simple.obj `if test -f '$(srcdir)/../common/nm-secret-agent-simple.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-secret-agent-simple.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-secret-agent-simple.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +@BUILD_NMTUI_FALSE@install-exec-hook: +@BUILD_NMTUI_FALSE@uninstall-hook: +clean: clean-recursive + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-hook +.MAKE: $(am__recursive_targets) install-am install-exec-am \ + install-strip uninstall-am + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-binPROGRAMS clean-generic clean-libtool \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-hook \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-hook + + +@BUILD_NMTUI_TRUE@install-exec-hook: +@BUILD_NMTUI_TRUE@ for link in $(links); do \ +@BUILD_NMTUI_TRUE@ ln -f $(DESTDIR)$(bindir)/nmtui $(DESTDIR)$(bindir)/$$link; \ +@BUILD_NMTUI_TRUE@ done + +@BUILD_NMTUI_TRUE@uninstall-hook: +@BUILD_NMTUI_TRUE@ for link in $(links); do \ +@BUILD_NMTUI_TRUE@ rm -f $(DESTDIR)$(bindir)/$$link; \ +@BUILD_NMTUI_TRUE@ done + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/clients/tui/newt/Makefile.am b/clients/tui/newt/Makefile.am new file mode 100644 index 00000000..e572238b --- /dev/null +++ b/clients/tui/newt/Makefile.am @@ -0,0 +1,53 @@ +AM_CPPFLAGS= \ + -I$(top_srcdir)/include \ + -DG_LOG_DOMAIN=\""nmtui"\" \ + $(GLIB_CFLAGS) \ + $(NEWT_CFLAGS) \ + $(NULL) + +noinst_LIBRARIES = libnmt-newt.a + +libnmt_newt_a_SOURCES = \ + nmt-newt.h \ + nmt-newt-types.h \ + nmt-newt-button.c \ + nmt-newt-button.h \ + nmt-newt-button-box.c \ + nmt-newt-button-box.h \ + nmt-newt-checkbox.c \ + nmt-newt-checkbox.h \ + nmt-newt-component.c \ + nmt-newt-component.h \ + nmt-newt-container.c \ + nmt-newt-container.h \ + nmt-newt-entry.c \ + nmt-newt-entry.h \ + nmt-newt-entry-numeric.c \ + nmt-newt-entry-numeric.h \ + nmt-newt-form.c \ + nmt-newt-form.h \ + nmt-newt-grid.c \ + nmt-newt-grid.h \ + nmt-newt-hacks.c \ + nmt-newt-hacks.h \ + nmt-newt-label.c \ + nmt-newt-label.h \ + nmt-newt-listbox.c \ + nmt-newt-listbox.h \ + nmt-newt-popup.c \ + nmt-newt-popup.h \ + nmt-newt-section.c \ + nmt-newt-section.h \ + nmt-newt-separator.c \ + nmt-newt-separator.h \ + nmt-newt-stack.c \ + nmt-newt-stack.h \ + nmt-newt-textbox.c \ + nmt-newt-textbox.h \ + nmt-newt-toggle-button.c \ + nmt-newt-toggle-button.h \ + nmt-newt-utils.c \ + nmt-newt-utils.h \ + nmt-newt-widget.c \ + nmt-newt-widget.h \ + $(NULL) diff --git a/clients/tui/newt/Makefile.in b/clients/tui/newt/Makefile.in new file mode 100644 index 00000000..64abce69 --- /dev/null +++ b/clients/tui/newt/Makefile.in @@ -0,0 +1,805 @@ +# Makefile.in generated by automake 1.13.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = clients/tui/newt +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/build-aux/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gnome-code-coverage.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libnmt_newt_a_AR = $(AR) $(ARFLAGS) +libnmt_newt_a_LIBADD = +am_libnmt_newt_a_OBJECTS = nmt-newt-button.$(OBJEXT) \ + nmt-newt-button-box.$(OBJEXT) nmt-newt-checkbox.$(OBJEXT) \ + nmt-newt-component.$(OBJEXT) nmt-newt-container.$(OBJEXT) \ + nmt-newt-entry.$(OBJEXT) nmt-newt-entry-numeric.$(OBJEXT) \ + nmt-newt-form.$(OBJEXT) nmt-newt-grid.$(OBJEXT) \ + nmt-newt-hacks.$(OBJEXT) nmt-newt-label.$(OBJEXT) \ + nmt-newt-listbox.$(OBJEXT) nmt-newt-popup.$(OBJEXT) \ + nmt-newt-section.$(OBJEXT) nmt-newt-separator.$(OBJEXT) \ + nmt-newt-stack.$(OBJEXT) nmt-newt-textbox.$(OBJEXT) \ + nmt-newt-toggle-button.$(OBJEXT) nmt-newt-utils.$(OBJEXT) \ + nmt-newt-widget.$(OBJEXT) +libnmt_newt_a_OBJECTS = $(am_libnmt_newt_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libnmt_newt_a_SOURCES) +DIST_SOURCES = $(libnmt_newt_a_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@ +BLUEZ5_LIBS = @BLUEZ5_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CKDB_PATH = @CKDB_PATH@ +CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ +CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ +CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ +DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ +DLLTOOL = @DLLTOOL@ +DNSMASQ_PATH = @DNSMASQ_PATH@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MAKEFILE = @GLIB_MAKEFILE@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@ +INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@ +INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@ +INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@ +INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ +INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@ +INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@ +INTROSPECTION_LIBS = @INTROSPECTION_LIBS@ +INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ +INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ +INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ +IPTABLES_PATH = @IPTABLES_PATH@ +IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@ +IWMX_SDK_LIBS = @IWMX_SDK_LIBS@ +KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBNDP_CFLAGS = @LIBNDP_CFLAGS@ +LIBNDP_LIBS = @LIBNDP_LIBS@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ +LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ +LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@ +MM_GLIB_LIBS = @MM_GLIB_LIBS@ +MOC = @MOC@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NEWT_CFLAGS = @NEWT_CFLAGS@ +NEWT_LIBS = @NEWT_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@ +NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ +NM_MICRO_VERSION = @NM_MICRO_VERSION@ +NM_MINOR_VERSION = @NM_MINOR_VERSION@ +NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@ +NM_VERSION = @NM_VERSION@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PPPD_PATH = @PPPD_PATH@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +PPPOE_PATH = @PPPOE_PATH@ +QT_CFLAGS = @QT_CFLAGS@ +QT_LIBS = @QT_LIBS@ +RANLIB = @RANLIB@ +READLINE_LIBS = @READLINE_LIBS@ +SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@ +SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@ +SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@ +SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@ +SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@ +SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_DIR = @UDEV_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VALGRIND_RULES = @VALGRIND_RULES@ +VAPIGEN = @VAPIGEN@ +VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@ +VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +intltool__v_merge_options_ = @intltool__v_merge_options_@ +intltool__v_merge_options_0 = @intltool__v_merge_options_0@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +nmbinary = @nmbinary@ +nmconfdir = @nmconfdir@ +nmdatadir = @nmdatadir@ +nmrundir = @nmrundir@ +nmstatedir = @nmstatedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_dhclient = @with_dhclient@ +with_dhcpcd = @with_dhcpcd@ +with_netconfig = @with_netconfig@ +with_resolvconf = @with_resolvconf@ +with_valgrind = @with_valgrind@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -DG_LOG_DOMAIN=\""nmtui"\" \ + $(GLIB_CFLAGS) \ + $(NEWT_CFLAGS) \ + $(NULL) + +noinst_LIBRARIES = libnmt-newt.a +libnmt_newt_a_SOURCES = \ + nmt-newt.h \ + nmt-newt-types.h \ + nmt-newt-button.c \ + nmt-newt-button.h \ + nmt-newt-button-box.c \ + nmt-newt-button-box.h \ + nmt-newt-checkbox.c \ + nmt-newt-checkbox.h \ + nmt-newt-component.c \ + nmt-newt-component.h \ + nmt-newt-container.c \ + nmt-newt-container.h \ + nmt-newt-entry.c \ + nmt-newt-entry.h \ + nmt-newt-entry-numeric.c \ + nmt-newt-entry-numeric.h \ + nmt-newt-form.c \ + nmt-newt-form.h \ + nmt-newt-grid.c \ + nmt-newt-grid.h \ + nmt-newt-hacks.c \ + nmt-newt-hacks.h \ + nmt-newt-label.c \ + nmt-newt-label.h \ + nmt-newt-listbox.c \ + nmt-newt-listbox.h \ + nmt-newt-popup.c \ + nmt-newt-popup.h \ + nmt-newt-section.c \ + nmt-newt-section.h \ + nmt-newt-separator.c \ + nmt-newt-separator.h \ + nmt-newt-stack.c \ + nmt-newt-stack.h \ + nmt-newt-textbox.c \ + nmt-newt-textbox.h \ + nmt-newt-toggle-button.c \ + nmt-newt-toggle-button.h \ + nmt-newt-utils.c \ + nmt-newt-utils.h \ + nmt-newt-widget.c \ + nmt-newt-widget.h \ + $(NULL) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu clients/tui/newt/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu clients/tui/newt/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libnmt-newt.a: $(libnmt_newt_a_OBJECTS) $(libnmt_newt_a_DEPENDENCIES) $(EXTRA_libnmt_newt_a_DEPENDENCIES) + $(AM_V_at)-rm -f libnmt-newt.a + $(AM_V_AR)$(libnmt_newt_a_AR) libnmt-newt.a $(libnmt_newt_a_OBJECTS) $(libnmt_newt_a_LIBADD) + $(AM_V_at)$(RANLIB) libnmt-newt.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-button-box.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-button.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-checkbox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-component.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-container.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-entry-numeric.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-entry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-form.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-grid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-hacks.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-label.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-listbox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-popup.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-section.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-separator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-stack.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-textbox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-toggle-button.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-widget.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/clients/tui/newt/nmt-newt-button-box.c b/clients/tui/newt/nmt-newt-button-box.c new file mode 100644 index 00000000..4fba1daf --- /dev/null +++ b/clients/tui/newt/nmt-newt-button-box.c @@ -0,0 +1,386 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-button-box + * @short_description: A container for #NmtNewtButtons + * + * #NmtNewtButtonBox is a container for creating and holding + * #NmtNewtButtons. + * + * A button box can be either horizontally or vertically laid out, and + * has two sections within it: the "start" (left or top) and "end" + * (right or bottom). Buttons are added from left to right or top to bottom + * within each of the two sections. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-button-box.h" +#include "nmt-newt-button.h" + +G_DEFINE_TYPE (NmtNewtButtonBox, nmt_newt_button_box, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_BUTTON_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxPrivate)) + +typedef struct { + NmtNewtButtonBoxOrientation orientation; + GPtrArray *start_buttons, *end_buttons; +} NmtNewtButtonBoxPrivate; + +enum { + PROP_0, + + PROP_ORIENTATION, + + LAST_PROP +}; + +/** + * NmtNewtButtonBoxOrientation: + * @NMT_NEWT_BUTTON_BOX_HORIZONTAL: horizontal + * @NMT_NEWT_BUTTON_BOX_VERTICAL: vertical + * + * The orientation of an #NmtNewtButtonBox + */ + +/** + * nmt_newt_button_box_new: + * @orientation: the orientation + * + * Creates a new #NmtNewtButtonBox + * + * Returns: a new #NmtNewtButtonBox + */ +NmtNewtWidget * +nmt_newt_button_box_new (NmtNewtButtonBoxOrientation orientation) +{ + return g_object_new (NMT_TYPE_NEWT_BUTTON_BOX, + "orientation", orientation, + NULL); +} + +static void +nmt_newt_button_box_init (NmtNewtButtonBox *bbox) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox); + + priv->start_buttons = g_ptr_array_new (); + priv->end_buttons = g_ptr_array_new (); +} + +/** + * nmt_newt_button_box_add_start: + * @bbox: an #NmtNewtButtonBox + * @label: the label for the newt button + * + * Creates a new #NmtNewtButton with the given @label, adds it + * to the "start" section of @bbox, and returns the newly-created + * button. + * + * Returns: the newly-created button, already added to @bbox + */ +NmtNewtWidget * +nmt_newt_button_box_add_start (NmtNewtButtonBox *bbox, + const char *label) +{ + NmtNewtWidget *button; + + button = nmt_newt_button_new (label); + nmt_newt_button_box_add_widget_start (bbox, button); + return button; +} + +/** + * nmt_newt_button_box_add_widget_start: + * @bbox: an #NmtNewtButtonBox + * @widget: the #NmtNewtWidget to add + * + * Adds the given widget to the "start" section of @bbox. + */ +void +nmt_newt_button_box_add_widget_start (NmtNewtButtonBox *bbox, + NmtNewtWidget *widget) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox); + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)-> + add (NMT_NEWT_CONTAINER (bbox), widget); + g_ptr_array_add (priv->start_buttons, widget); +} + +/** + * nmt_newt_button_box_add_end: + * @bbox: an #NmtNewtButtonBox + * @label: the label for the newt button + * + * Creates a new #NmtNewtButton with the given @label, adds it + * to the "end" section of @bbox, and returns the newly-created + * button. + * + * Returns: the newly-created button, already added to @bbox + */ +NmtNewtWidget * +nmt_newt_button_box_add_end (NmtNewtButtonBox *bbox, + const char *label) +{ + NmtNewtWidget *button; + + button = nmt_newt_button_new (label); + nmt_newt_button_box_add_widget_end (bbox, button); + return button; +} + +/** + * nmt_newt_button_box_add_widget_end: + * @bbox: an #NmtNewtButtonBox + * @widget: the #NmtNewtWidget to add + * + * Adds the given widget to the "end" section of @bbox. + */ +void +nmt_newt_button_box_add_widget_end (NmtNewtButtonBox *bbox, + NmtNewtWidget *widget) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox); + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)-> + add (NMT_NEWT_CONTAINER (bbox), widget); + g_ptr_array_add (priv->end_buttons, widget); +} + +static void +nmt_newt_button_box_remove (NmtNewtContainer *container, + NmtNewtWidget *child) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (container); + int i; + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)-> + remove (container, child); + + for (i = 0; i < priv->start_buttons->len; i++) { + if (priv->start_buttons->pdata[i] == (gpointer) child) { + g_ptr_array_remove_index (priv->start_buttons, i); + return; + } + } + for (i = 0; i < priv->end_buttons->len; i++) { + if (priv->end_buttons->pdata[i] == (gpointer) child) { + g_ptr_array_remove_index (priv->end_buttons, i); + return; + } + } +} + +static void +add_buttons (GPtrArray *buttons, GPtrArray *cos) +{ + NmtNewtWidget *child; + newtComponent *child_cos; + int i, c; + + for (i = 0; i < buttons->len; i++) { + child = buttons->pdata[i]; + + if (!nmt_newt_widget_get_visible (child)) + continue; + + child_cos = nmt_newt_widget_get_components (child); + for (c = 0; child_cos[c]; c++) + g_ptr_array_add (cos, child_cos[c]); + g_free (child_cos); + } +} + +static newtComponent * +nmt_newt_button_box_get_components (NmtNewtWidget *widget) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget); + GPtrArray *cos; + + cos = g_ptr_array_new (); + add_buttons (priv->start_buttons, cos); + add_buttons (priv->end_buttons, cos); + g_ptr_array_add (cos, NULL); + + return (newtComponent *) g_ptr_array_free (cos, FALSE); +} + +static void +size_request_buttons (NmtNewtButtonBox *bbox, + GPtrArray *buttons, + int *width, + int *height) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox); + int child_width, child_height; + int i; + + for (i = 0; i < buttons->len; i++) { + NmtNewtWidget *child = buttons->pdata[i]; + + nmt_newt_widget_size_request (child, &child_width, &child_height); + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) { + *width += child_width; + if (i > 0) + *width += 1; + *height = MAX (*height, child_height); + } else { + *height += child_height; + if (i > 0) + *height += 1; + *width = MAX (*width, child_width); + } + } +} + +static void +nmt_newt_button_box_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtButtonBox *bbox = NMT_NEWT_BUTTON_BOX (widget); + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget); + + *width = *height = 0; + size_request_buttons (bbox, priv->start_buttons, width, height); + size_request_buttons (bbox, priv->end_buttons, width, height); + + if (priv->start_buttons && priv->end_buttons) { + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) + *width += 1; + else + *height += 1; + } +} + +static void +nmt_newt_button_box_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget); + NmtNewtWidget *child; + int child_x, child_y, child_width, child_height; + int i; + + child_x = x; + child_y = y; + for (i = 0; i < priv->start_buttons->len; i++) { + child = priv->start_buttons->pdata[i]; + nmt_newt_widget_size_request (child, &child_width, &child_height); + + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) { + nmt_newt_widget_size_allocate (child, child_x, child_y, child_width, child_height); + child_x += child_width + 1; + } else { + nmt_newt_widget_size_allocate (child, child_x, child_y, child_width, child_height); + child_y += child_height + 1; + } + } + + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) + child_x = x + width; + else + child_y = y + height; + + for (i = priv->end_buttons->len - 1; i >= 0; i--) { + child = priv->end_buttons->pdata[i]; + nmt_newt_widget_size_request (child, &child_width, &child_height); + + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) { + nmt_newt_widget_size_allocate (child, + child_x - child_width, child_y, + child_width, child_height); + child_x -= child_width + 1; + } else { + nmt_newt_widget_size_allocate (child, + child_x, child_y - child_height, + child_width, child_height); + child_y -= child_height + 1; + } + } +} + +static void +nmt_newt_button_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ORIENTATION: + priv->orientation = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ORIENTATION: + g_value_set_int (value, priv->orientation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_box_class_init (NmtNewtButtonBoxClass *bbox_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (bbox_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (bbox_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (bbox_class); + + g_type_class_add_private (bbox_class, sizeof (NmtNewtButtonBoxPrivate)); + + object_class->get_property = nmt_newt_button_box_get_property; + object_class->set_property = nmt_newt_button_box_set_property; + + widget_class->get_components = nmt_newt_button_box_get_components; + widget_class->size_request = nmt_newt_button_box_size_request; + widget_class->size_allocate = nmt_newt_button_box_size_allocate; + + container_class->remove = nmt_newt_button_box_remove; + + g_object_class_install_property + (object_class, PROP_ORIENTATION, + g_param_spec_int ("orientation", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-button-box.h b/clients/tui/newt/nmt-newt-button-box.h new file mode 100644 index 00000000..970588a4 --- /dev/null +++ b/clients/tui/newt/nmt-newt-button-box.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_BUTTON_BOX_H +#define NMT_NEWT_BUTTON_BOX_H + +#include "nmt-newt-grid.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_BUTTON_BOX (nmt_newt_button_box_get_type ()) +#define NMT_NEWT_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBox)) +#define NMT_NEWT_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxClass)) +#define NMT_IS_NEWT_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_BUTTON_BOX)) +#define NMT_IS_NEWT_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_BUTTON_BOX)) +#define NMT_NEWT_BUTTON_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxClass)) + +struct _NmtNewtButtonBox { + NmtNewtContainer parent; + +}; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtNewtButtonBoxClass; + +GType nmt_newt_button_box_get_type (void); + +typedef enum { + NMT_NEWT_BUTTON_BOX_HORIZONTAL, + NMT_NEWT_BUTTON_BOX_VERTICAL +} NmtNewtButtonBoxOrientation; + +NmtNewtWidget *nmt_newt_button_box_new (NmtNewtButtonBoxOrientation orientation); + +NmtNewtWidget *nmt_newt_button_box_add_start (NmtNewtButtonBox *bbox, + const char *label); +NmtNewtWidget *nmt_newt_button_box_add_end (NmtNewtButtonBox *bbox, + const char *label); + +void nmt_newt_button_box_add_widget_start (NmtNewtButtonBox *bbox, + NmtNewtWidget *widget); +void nmt_newt_button_box_add_widget_end (NmtNewtButtonBox *bbox, + NmtNewtWidget *widget); + + +G_END_DECLS + +#endif /* NMT_NEWT_BUTTON_BOX_H */ diff --git a/clients/tui/newt/nmt-newt-button.c b/clients/tui/newt/nmt-newt-button.c new file mode 100644 index 00000000..00dbcd79 --- /dev/null +++ b/clients/tui/newt/nmt-newt-button.c @@ -0,0 +1,261 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-button + * @short_description: Push buttons + * + * #NmtNewtButton implements a button widget. + */ + +#include "config.h" + +#include "nmt-newt-button.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtButton, nmt_newt_button, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonPrivate)) + +typedef struct { + char *label; +} NmtNewtButtonPrivate; + +enum { + PROP_0, + PROP_LABEL, + + LAST_PROP +}; + +enum { + CLICKED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/** + * nmt_newt_button_new: + * @label: the (initial) button label + * + * Creates a new button. + * + * Returns: a new #NmtNewtButton + */ +NmtNewtWidget * +nmt_newt_button_new (const char *label) +{ + return g_object_new (NMT_TYPE_NEWT_BUTTON, + "label", label, + NULL); +} + +/** + * nmt_newt_button_set_label: + * @button: an #NmtNewtButton + * @label: the new label + * + * Updates @button's label. + */ +void +nmt_newt_button_set_label (NmtNewtButton *button, + const char *label) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (button); + + if (!g_strcmp0 (priv->label, label)) + return; + + g_free (priv->label); + priv->label = g_strdup (label); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (button)); +} + +/** + * nmt_newt_button_get_label: + * @button: an #NmtNewtButton + * + * Gets @button's label. + * + * Returns: @button's label. + */ +const char * +nmt_newt_button_get_label (NmtNewtButton *button) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (button); + + return priv->label; +} + +static void +nmt_newt_button_init (NmtNewtButton *button) +{ +} + +static void +nmt_newt_button_finalize (GObject *object) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (object); + + g_free (priv->label); + + G_OBJECT_CLASS (nmt_newt_button_parent_class)->finalize (object); +} + +static newtComponent +nmt_newt_button_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (component); + newtComponent co; + char *label = NULL, *label_lc; + + if (sensitive) { + label_lc = nmt_newt_locale_from_utf8 (priv->label); + co = newtCompactButton (-1, -1, label_lc); + g_free (label_lc); + } else { + label = g_strdup_printf (" <%s>", priv->label); + label_lc = nmt_newt_locale_from_utf8 (label); + co = newtLabel (-1, -1, label_lc); + g_free (label_lc); + newtLabelSetColors (co, NMT_NEWT_COLORSET_DISABLED_BUTTON); + } + + return co; +} + +static void +nmt_newt_button_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->size_request (widget, width, height); + + /* remove the automatically-added left padding */ + (*width)--; +} + +static void +nmt_newt_button_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + /* account for the automatically-added left padding */ + x--; + width++; + + NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->size_allocate (widget, x, y, width, height); +} + +static void +nmt_newt_button_activated (NmtNewtWidget *widget) +{ + g_signal_emit (widget, signals[CLICKED], 0); + + NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->activated (widget); +} + +static void +nmt_newt_button_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_LABEL: + nmt_newt_button_set_label (NMT_NEWT_BUTTON (object), + g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LABEL: + g_value_set_string (value, priv->label); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_class_init (NmtNewtButtonClass *button_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (button_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (button_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (button_class); + + g_type_class_add_private (button_class, sizeof (NmtNewtButtonPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_button_set_property; + object_class->get_property = nmt_newt_button_get_property; + object_class->finalize = nmt_newt_button_finalize; + + widget_class->size_request = nmt_newt_button_size_request; + widget_class->size_allocate = nmt_newt_button_size_allocate; + widget_class->activated = nmt_newt_button_activated; + + component_class->build_component = nmt_newt_button_build_component; + + /* signals */ + + /** + * NmtNewtButton::clicked: + * @button: the #NmtNewtButton + * + * Emitted when the button is clicked. + */ + signals[CLICKED] = + g_signal_new ("clicked", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /* properties */ + + /** + * NmtNewtButton:label: + * + * The button's label + */ + g_object_class_install_property + (object_class, PROP_LABEL, + g_param_spec_string ("label", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-button.h b/clients/tui/newt/nmt-newt-button.h new file mode 100644 index 00000000..52e95c5d --- /dev/null +++ b/clients/tui/newt/nmt-newt-button.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_BUTTON_H +#define NMT_NEWT_BUTTON_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_BUTTON (nmt_newt_button_get_type ()) +#define NMT_NEWT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButton)) +#define NMT_NEWT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonClass)) +#define NMT_IS_NEWT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_BUTTON)) +#define NMT_IS_NEWT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_BUTTON)) +#define NMT_NEWT_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonClass)) + +struct _NmtNewtButton { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtButtonClass; + +GType nmt_newt_button_get_type (void); + +NmtNewtWidget *nmt_newt_button_new (const char *label); + +void nmt_newt_button_set_label (NmtNewtButton *button, + const char *label); +const char *nmt_newt_button_get_label (NmtNewtButton *button); + +G_END_DECLS + +#endif /* NMT_NEWT_BUTTON_H */ diff --git a/clients/tui/newt/nmt-newt-checkbox.c b/clients/tui/newt/nmt-newt-checkbox.c new file mode 100644 index 00000000..69d9920a --- /dev/null +++ b/clients/tui/newt/nmt-newt-checkbox.c @@ -0,0 +1,237 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-checkbox + * @short_description: Checkboxes + * + * #NmtNewtCheckbox implements a checkbox widget. + */ + +#include "config.h" + +#include "nmt-newt-checkbox.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtCheckbox, nmt_newt_checkbox, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_CHECKBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxPrivate)) + +typedef struct { + char *label_lc; + gboolean active; +} NmtNewtCheckboxPrivate; + +enum { + PROP_0, + PROP_LABEL, + PROP_ACTIVE, + + LAST_PROP +}; + +#define CHECKBOX_INACTIVE ' ' +#define CHECKBOX_ACTIVE 'X' +#define CHECKBOX_STATES " X" + +/** + * nmt_newt_checkbox_new: + * @label: the (initial) checkbox label + * + * Creates a new checkbox. + * + * Returns: a new #NmtNewtCheckbox + */ +NmtNewtWidget * +nmt_newt_checkbox_new (const char *label) +{ + return g_object_new (NMT_TYPE_NEWT_CHECKBOX, + "label", label, + NULL); +} + +/** + * nmt_newt_checkbox_set_active: + * @checkbox: an #NmtNewtCheckbox + * @active: whether @checkbox should be checked + * + * Updates @checkbox's checked state + */ +void +nmt_newt_checkbox_set_active (NmtNewtCheckbox *checkbox, + gboolean active) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox); + newtComponent co; + + active = !!active; + if (active == priv->active) + return; + + priv->active = active; + + co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (checkbox)); + if (co) + newtCheckboxSetValue (co, priv->active ? CHECKBOX_ACTIVE : CHECKBOX_INACTIVE); + + g_object_notify (G_OBJECT (checkbox), "active"); +} + +/** + * nmt_newt_checkbox_get_active: + * @checkbox: an #NmtNewtCheckbox + * + * Gets @checkbox's checked state + * + * Returns: @checkbox's checked state + */ +gboolean +nmt_newt_checkbox_get_active (NmtNewtCheckbox *checkbox) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox); + + return priv->active; +} + +static void +nmt_newt_checkbox_init (NmtNewtCheckbox *checkbox) +{ +} + +static void +nmt_newt_checkbox_finalize (GObject *object) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object); + + g_free (priv->label_lc); + + G_OBJECT_CLASS (nmt_newt_checkbox_parent_class)->finalize (object); +} + +static void +checkbox_toggled_callback (newtComponent co, + void *checkbox) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox); + gboolean active; + + active = (newtCheckboxGetValue (co) == CHECKBOX_ACTIVE); + if (active != priv->active) { + priv->active = active; + g_object_notify (checkbox, "active"); + } +} + +static newtComponent +nmt_newt_checkbox_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (component); + newtComponent co; + + co = newtCheckbox (-1, -1, priv->label_lc, + priv->active ? CHECKBOX_ACTIVE : CHECKBOX_INACTIVE, + CHECKBOX_STATES, NULL); + if (!sensitive) + newtCheckboxSetFlags (co, NEWT_FLAG_DISABLED, NEWT_FLAGS_SET); + newtComponentAddCallback (co, checkbox_toggled_callback, component); + return co; +} + +static void +nmt_newt_checkbox_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtCheckbox *checkbox = NMT_NEWT_CHECKBOX (object); + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LABEL: + g_free (priv->label_lc); + priv->label_lc = nmt_newt_locale_from_utf8 (g_value_get_string (value)); + break; + case PROP_ACTIVE: + nmt_newt_checkbox_set_active (checkbox, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_checkbox_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LABEL: + g_value_take_string (value, nmt_newt_locale_to_utf8 (priv->label_lc)); + break; + case PROP_ACTIVE: + g_value_set_boolean (value, priv->active); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_checkbox_class_init (NmtNewtCheckboxClass *checkbox_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (checkbox_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (checkbox_class); + + g_type_class_add_private (checkbox_class, sizeof (NmtNewtCheckboxPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_checkbox_set_property; + object_class->get_property = nmt_newt_checkbox_get_property; + object_class->finalize = nmt_newt_checkbox_finalize; + + component_class->build_component = nmt_newt_checkbox_build_component; + + /** + * NmtNewtCheckbox:label: + * + * The checkbox's label + */ + g_object_class_install_property + (object_class, PROP_LABEL, + g_param_spec_string ("label", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtCheckbox:active: + * + * The checkbox's checked state + */ + g_object_class_install_property + (object_class, PROP_ACTIVE, + g_param_spec_boolean ("active", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-checkbox.h b/clients/tui/newt/nmt-newt-checkbox.h new file mode 100644 index 00000000..c277386b --- /dev/null +++ b/clients/tui/newt/nmt-newt-checkbox.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_CHECKBOX_H +#define NMT_NEWT_CHECKBOX_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_CHECKBOX (nmt_newt_checkbox_get_type ()) +#define NMT_NEWT_CHECKBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckbox)) +#define NMT_NEWT_CHECKBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxClass)) +#define NMT_IS_NEWT_CHECKBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_CHECKBOX)) +#define NMT_IS_NEWT_CHECKBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_CHECKBOX)) +#define NMT_NEWT_CHECKBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxClass)) + +struct _NmtNewtCheckbox { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtCheckboxClass; + +GType nmt_newt_checkbox_get_type (void); + +NmtNewtWidget *nmt_newt_checkbox_new (const char *label); + +void nmt_newt_checkbox_set_active (NmtNewtCheckbox *checkbox, + gboolean active); +gboolean nmt_newt_checkbox_get_active (NmtNewtCheckbox *checkbox); + +G_END_DECLS + +#endif /* NMT_NEWT_CHECKBOX_H */ diff --git a/clients/tui/newt/nmt-newt-component.c b/clients/tui/newt/nmt-newt-component.c new file mode 100644 index 00000000..98f6df63 --- /dev/null +++ b/clients/tui/newt/nmt-newt-component.c @@ -0,0 +1,309 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-component + * @short_description: Base class for widgets that wrap #newtComponents + * + * #NmtNewtComponent is the abstract class for #NmtNewtWidgets that + * wrap a (single) #newtComponent. + */ + +#include "config.h" + +#include "nmt-newt-component.h" +#include "nmt-newt-form.h" +#include "nmt-newt-hacks.h" + +G_DEFINE_ABSTRACT_TYPE (NmtNewtComponent, nmt_newt_component, NMT_TYPE_NEWT_WIDGET) + +#define NMT_NEWT_COMPONENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentPrivate)) + +typedef struct { + newtComponent co; + gboolean own_component; + gboolean sensitive; +} NmtNewtComponentPrivate; + +enum { + PROP_0, + + PROP_COMPONENT, + PROP_SENSITIVE, + + LAST_PROP +}; + +static void +nmt_newt_component_init (NmtNewtComponent *component) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component); + + priv->sensitive = TRUE; +} + +static void +nmt_newt_component_unrealize (NmtNewtWidget *widget) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + + if (!priv->co) + return; + + newtComponentAddCallback (priv->co, NULL, NULL); + newtComponentAddDestroyCallback (priv->co, NULL, NULL); + + if (priv->own_component) + newtComponentDestroy (priv->co); + priv->co = NULL; +} + +static void +component_destroy_callback (newtComponent co, + void *component) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component); + + priv->own_component = FALSE; + nmt_newt_widget_unrealize (component); + nmt_newt_widget_needs_rebuild (component); +} + +static void +nmt_newt_component_realize (NmtNewtWidget *widget) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + + nmt_newt_component_unrealize (widget); + priv->co = NMT_NEWT_COMPONENT_GET_CLASS (widget)-> + build_component (NMT_NEWT_COMPONENT (widget), priv->sensitive); + priv->own_component = TRUE; + if (!priv->sensitive) + newtComponentTakesFocus (priv->co, FALSE); + newtComponentAddDestroyCallback (priv->co, component_destroy_callback, widget); +} + +static newtComponent * +nmt_newt_component_get_components (NmtNewtWidget *widget) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + newtComponent *cos; + + priv->own_component = FALSE; + cos = g_new0 (newtComponent, 2); + cos[0] = priv->co; + return cos; +} + +static NmtNewtWidget * +nmt_newt_component_find_component (NmtNewtWidget *widget, + newtComponent co) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + + if (co == priv->co) + return widget; + else + return NULL; +} + +static void +nmt_newt_component_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + + newtComponentGetSize (priv->co, width, height); +} + +static void +nmt_newt_component_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + newtGrid grid; + + /* You can't directly place a newtComponent, so we create a newtGrid, + * position the component within that, and then place the grid. + */ + grid = newtCreateGrid (1, 1); + newtGridSetField (grid, 0, 0, + NEWT_GRID_COMPONENT, priv->co, + x, y, 0, 0, + NEWT_ANCHOR_LEFT | NEWT_ANCHOR_TOP, 0); + newtGridPlace (grid, 0, 0); + newtGridFree (grid, FALSE); +} + +static newtComponent +nmt_newt_component_get_focus_component (NmtNewtWidget *widget) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + + return priv->co; +} + +/** + * nmt_newt_component_get_component: + * @component: an #NmtNewtComponent + * + * A simpler version of nmt_newt_widget_get_components() for the + * single-component case. Also, unlike + * nmt_newt_widget_get_component(), this does not realize the widget + * if it isn't already realized. FIXME: why? + * + * Returns: @component's #newtComponent + */ +newtComponent +nmt_newt_component_get_component (NmtNewtComponent *component) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component); + + return priv->co; +} + +/** + * nmt_newt_component_get_sensitive: + * @component: an #NmtNewtComponent + * + * Gets @component's #NmtNewtComponent:sensitive property, indicating + * whether the widget is available for manipulation. Insensitive + * components will be skipped over in the keyboard tab chain, and may + * be displayed differently. + * + * Returns: @component's #NmtNewtComponent:sensitive property + */ +gboolean +nmt_newt_component_get_sensitive (NmtNewtComponent *component) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component); + + return priv->sensitive; +} + +/** + * nmt_newt_component_set_sensitive: + * @component: an #NmtNewtComponent + * @sensitive: whether @component should be sensitive + * + * Sets @component's #NmtNewtComponent:sensitive property. + */ +void +nmt_newt_component_set_sensitive (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component); + + sensitive = !!sensitive; + if (priv->sensitive == sensitive) + return; + + priv->sensitive = sensitive; + g_object_notify (G_OBJECT (component), "sensitive"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (component)); +} + +static void +nmt_newt_component_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtComponent *component = NMT_NEWT_COMPONENT (object); + + switch (prop_id) { + case PROP_SENSITIVE: + nmt_newt_component_set_sensitive (component, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_component_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtComponent *component = NMT_NEWT_COMPONENT (object); + + switch (prop_id) { + case PROP_COMPONENT: + g_value_set_pointer (value, nmt_newt_component_get_component (component)); + break; + case PROP_SENSITIVE: + g_value_set_boolean (value, nmt_newt_component_get_sensitive (component)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_component_class_init (NmtNewtComponentClass *component_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (component_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (component_class); + + g_type_class_add_private (component_class, sizeof (NmtNewtComponentPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_component_set_property; + object_class->get_property = nmt_newt_component_get_property; + + widget_class->realize = nmt_newt_component_realize; + widget_class->unrealize = nmt_newt_component_unrealize; + widget_class->get_components = nmt_newt_component_get_components; + widget_class->find_component = nmt_newt_component_find_component; + widget_class->size_request = nmt_newt_component_size_request; + widget_class->size_allocate = nmt_newt_component_size_allocate; + widget_class->get_focus_component = nmt_newt_component_get_focus_component; + + /* properties */ + + /** + * NmtNewtComponent:component: + * + * The component's #newtComponent + */ + g_object_class_install_property + (object_class, PROP_COMPONENT, + g_param_spec_pointer ("component", "", "", + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtComponent:sensitive: + * + * Whether the component is sensitive. Insensitive components will + * be skipped over in the keyboard tab chain, and may be displayed + * differently. + */ + g_object_class_install_property + (object_class, PROP_SENSITIVE, + g_param_spec_boolean ("sensitive", "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-component.h b/clients/tui/newt/nmt-newt-component.h new file mode 100644 index 00000000..2ffa018e --- /dev/null +++ b/clients/tui/newt/nmt-newt-component.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_COMPONENT_H +#define NMT_NEWT_COMPONENT_H + +#include "nmt-newt-widget.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_COMPONENT (nmt_newt_component_get_type ()) +#define NMT_NEWT_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponent)) +#define NMT_NEWT_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentClass)) +#define NMT_IS_NEWT_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_COMPONENT)) +#define NMT_IS_NEWT_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_COMPONENT)) +#define NMT_NEWT_COMPONENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentClass)) + +struct _NmtNewtComponent { + NmtNewtWidget parent; + +}; + +typedef struct { + NmtNewtWidgetClass parent; + + /* methods */ + newtComponent (*build_component) (NmtNewtComponent *component, + gboolean sensitive); + +} NmtNewtComponentClass; + +GType nmt_newt_component_get_type (void); + +newtComponent nmt_newt_component_get_component (NmtNewtComponent *component); + +gboolean nmt_newt_component_get_sensitive (NmtNewtComponent *component); +void nmt_newt_component_set_sensitive (NmtNewtComponent *component, + gboolean sensitive); + +G_END_DECLS + +#endif /* NMT_NEWT_COMPONENT_H */ diff --git a/clients/tui/newt/nmt-newt-container.c b/clients/tui/newt/nmt-newt-container.c new file mode 100644 index 00000000..03f677fb --- /dev/null +++ b/clients/tui/newt/nmt-newt-container.c @@ -0,0 +1,252 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-container + * @short_description: Base class for containers + * + * #NmtNewtContainer is the base class for #NmtNewtWidgets that + * contain other widgets. + * + * #NmtNewtGrid is the most generic container type. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-container.h" +#include "nmt-newt-component.h" + +G_DEFINE_ABSTRACT_TYPE (NmtNewtContainer, nmt_newt_container, NMT_TYPE_NEWT_WIDGET) + +#define NMT_NEWT_CONTAINER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerPrivate)) + +typedef struct { + GPtrArray *children; + +} NmtNewtContainerPrivate; + +static void child_needs_rebuild (NmtNewtWidget *widget, gpointer user_data); + +static void +nmt_newt_container_init (NmtNewtContainer *container) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container); + + priv->children = g_ptr_array_new (); +} + +static void +nmt_newt_container_finalize (GObject *object) +{ + NmtNewtContainer *container = NMT_NEWT_CONTAINER (object); + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (object); + + while (priv->children->len) + nmt_newt_container_remove (container, priv->children->pdata[0]); + + G_OBJECT_CLASS (nmt_newt_container_parent_class)->finalize (object); +} + +static void +nmt_newt_container_realize (NmtNewtWidget *widget) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget); + int i; + + for (i = 0; i < priv->children->len; i++) + nmt_newt_widget_realize (priv->children->pdata[i]); +} + +static void +nmt_newt_container_unrealize (NmtNewtWidget *widget) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget); + int i; + + for (i = 0; i < priv->children->len; i++) + nmt_newt_widget_unrealize (priv->children->pdata[i]); +} + +static void +child_needs_rebuild (NmtNewtWidget *widget, + gpointer user_data) +{ + NmtNewtWidget *container = user_data; + + nmt_newt_widget_needs_rebuild (container); +} + +static void +nmt_newt_container_real_child_validity_changed (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtContainerPrivate *priv; + int i; + + if (widget) { + if (!nmt_newt_widget_get_visible (widget)) + return; + if (!nmt_newt_widget_get_valid (widget)) { + nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), FALSE); + return; + } + } + + priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container); + for (i = 0; i < priv->children->len; i++) { + widget = priv->children->pdata[i]; + if ( nmt_newt_widget_get_visible (widget) + && !nmt_newt_widget_get_valid (widget)) { + nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), FALSE); + return; + } + } + + nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), TRUE); +} + +static void +nmt_newt_container_child_validity_changed (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NMT_NEWT_CONTAINER_GET_CLASS (container)->child_validity_changed (container, widget); +} + +static void +child_validity_notify (GObject *object, + GParamSpec *pspec, + gpointer container) +{ + nmt_newt_container_child_validity_changed (container, NMT_NEWT_WIDGET (object)); +} + +static void +nmt_newt_container_real_add (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container); + + g_signal_connect (widget, "needs-rebuild", G_CALLBACK (child_needs_rebuild), container); + g_signal_connect (widget, "notify::valid", G_CALLBACK (child_validity_notify), container); + g_ptr_array_add (priv->children, g_object_ref_sink (widget)); + nmt_newt_widget_set_parent (widget, NMT_NEWT_WIDGET (container)); + + nmt_newt_container_child_validity_changed (container, widget); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (container)); +} + +static void +nmt_newt_container_real_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container); + int i; + + for (i = 0; i < priv->children->len; i++) { + if (widget == priv->children->pdata[i]) { + g_ptr_array_remove_index (priv->children, i); + g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (child_needs_rebuild), container); + g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (child_validity_notify), container); + nmt_newt_widget_set_parent (widget, NULL); + g_object_unref (widget); + + nmt_newt_container_child_validity_changed (container, NULL); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (container)); + return; + } + } +} + +/** + * nmt_newt_container_remove: + * @container: the #NmtNewtContainer + * @widget: the child to remove + * + * Removes @widget from @container. + * + * Note that there is not a corresponding + * <literal>nmt_newt_container_add ()</literal>; you must use + * container-type-specific methods to add widgets to containers. + */ +void +nmt_newt_container_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NMT_NEWT_CONTAINER_GET_CLASS (container)->remove (container, widget); +} + +static NmtNewtWidget * +nmt_newt_container_find_component (NmtNewtWidget *widget, + newtComponent co) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget); + NmtNewtWidget *found, *child; + int i; + + for (i = 0; i < priv->children->len; i++) { + child = priv->children->pdata[i]; + + found = nmt_newt_widget_find_component (child, co); + if (found) + return found; + } + + return NULL; +} + +/** + * nmt_newt_container_get_children: + * @container: an #NmtNewtContainer + * + * Gets a list of @container's children. + * + * Returns: (transfer full): a list of @container's children. + */ +GSList * +nmt_newt_container_get_children (NmtNewtContainer *container) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container); + GSList *ret; + int i; + + for (i = 0, ret = NULL; i < priv->children->len; i++) + ret = g_slist_prepend (ret, g_object_ref (priv->children->pdata[i])); + return g_slist_reverse (ret); +} + +static void +nmt_newt_container_class_init (NmtNewtContainerClass *container_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (container_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (container_class); + + g_type_class_add_private (container_class, sizeof (NmtNewtContainerPrivate)); + + /* virtual methods */ + object_class->finalize = nmt_newt_container_finalize; + + widget_class->realize = nmt_newt_container_realize; + widget_class->unrealize = nmt_newt_container_unrealize; + widget_class->find_component = nmt_newt_container_find_component; + + container_class->add = nmt_newt_container_real_add; + container_class->remove = nmt_newt_container_real_remove; + container_class->child_validity_changed = nmt_newt_container_real_child_validity_changed; +} diff --git a/clients/tui/newt/nmt-newt-container.h b/clients/tui/newt/nmt-newt-container.h new file mode 100644 index 00000000..6f182925 --- /dev/null +++ b/clients/tui/newt/nmt-newt-container.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_CONTAINER_H +#define NMT_NEWT_CONTAINER_H + +#include "nmt-newt-widget.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_CONTAINER (nmt_newt_container_get_type ()) +#define NMT_NEWT_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainer)) +#define NMT_NEWT_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerClass)) +#define NMT_IS_NEWT_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_CONTAINER)) +#define NMT_IS_NEWT_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_CONTAINER)) +#define NMT_NEWT_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerClass)) + +struct _NmtNewtContainer { + NmtNewtWidget parent; + +}; + +typedef struct { + NmtNewtWidgetClass parent; + + /* methods */ + void (*add) (NmtNewtContainer *container, + NmtNewtWidget *child); + void (*remove) (NmtNewtContainer *container, + NmtNewtWidget *child); + + void (*child_validity_changed) (NmtNewtContainer *container, + NmtNewtWidget *child); + +} NmtNewtContainerClass; + +GType nmt_newt_container_get_type (void); + +void nmt_newt_container_remove (NmtNewtContainer *container, + NmtNewtWidget *widget); + +GSList *nmt_newt_container_get_children (NmtNewtContainer *container); + +G_END_DECLS + +#endif /* NMT_NEWT_CONTAINER_H */ diff --git a/clients/tui/newt/nmt-newt-entry-numeric.c b/clients/tui/newt/nmt-newt-entry-numeric.c new file mode 100644 index 00000000..416b4b79 --- /dev/null +++ b/clients/tui/newt/nmt-newt-entry-numeric.c @@ -0,0 +1,215 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-entry-numeric + * @short_description: Numeric text entry + * + * #NmtNewtEntryNumeric implements a numeric-only #NmtNewtEntry. + * + * #NmtNewtEntryNumeric provides its own #NmtNewtEntryFilter and + * #NmtNewtEntryValidator functions, so you should not set your own. + */ + +#include "config.h" + +#include <stdlib.h> + +#include "nmt-newt-entry-numeric.h" + +G_DEFINE_TYPE (NmtNewtEntryNumeric, nmt_newt_entry_numeric, NMT_TYPE_NEWT_ENTRY) + +#define NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericPrivate)) + +typedef struct { + int min, max; +} NmtNewtEntryNumericPrivate; + +enum { + PROP_0, + PROP_MINIMUM, + PROP_MAXIMUM, + + LAST_PROP +}; + +/** + * nmt_newt_entry_numeric_new: + * @width: the entry's width in characters + * @min: the minimum valid value + * @max: the maximum valid value + * + * Creates a new #NmtNewtEntryNumeric, accepting values in the + * indicated range. + * + * Returns: a new #NmtNewtEntryNumeric + */ +NmtNewtWidget * +nmt_newt_entry_numeric_new (int width, + int min, + int max) +{ + return g_object_new (NMT_TYPE_NEWT_ENTRY_NUMERIC, + "width", width, + "minimum", min, + "maximum", max, + NULL); +} + +static gboolean +newt_entry_numeric_filter (NmtNewtEntry *entry, + const char *text, + int ch, + int position, + gpointer user_data) +{ + NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (entry); + + if (g_ascii_isdigit (ch)) + return TRUE; + + if (ch == '-' && position == 0 && priv->min < 0) + return TRUE; + + return FALSE; +} + +static gboolean +newt_entry_numeric_validate (NmtNewtEntry *entry, + const char *text, + gpointer user_data) +{ + NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (entry); + int val; + char *end; + + if (!*text) + return FALSE; + + val = strtoul (text, &end, 10); + if (*end) + return FALSE; + if (val < priv->min || val > priv->max) + return FALSE; + + return TRUE; +} + +static void +nmt_newt_entry_numeric_init (NmtNewtEntryNumeric *entry) +{ + nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), newt_entry_numeric_filter, NULL); + nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), newt_entry_numeric_validate, NULL); +} + +static void +nmt_newt_entry_numeric_constructed (GObject *object) +{ + NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object); + + if (!*nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object))) { + char buf[32]; + + g_snprintf (buf, sizeof (buf), "%d", priv->min); + nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), buf); + } + + G_OBJECT_CLASS (nmt_newt_entry_numeric_parent_class)->constructed (object); +} + +static void +nmt_newt_entry_numeric_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MINIMUM: + priv->min = g_value_get_int (value); + break; + case PROP_MAXIMUM: + priv->max = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_entry_numeric_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MINIMUM: + g_value_set_int (value, priv->min); + break; + case PROP_MAXIMUM: + g_value_set_int (value, priv->max); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_entry_numeric_class_init (NmtNewtEntryNumericClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtNewtEntryNumericPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_newt_entry_numeric_constructed; + object_class->set_property = nmt_newt_entry_numeric_set_property; + object_class->get_property = nmt_newt_entry_numeric_get_property; + + /** + * NmtNewtEntryNumeric:minimum: + * + * The minimum #NmtNewtWidget:valid value for the entry. If this + * is non-negative, then the entry will not allow negative numbers + * to be entered. + */ + g_object_class_install_property + (object_class, PROP_MINIMUM, + g_param_spec_int ("minimum", "", "", + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtEntryNumeric:maximum: + * + * The maximum #NmtNewtWidget:valid value for the entry. + */ + g_object_class_install_property + (object_class, PROP_MAXIMUM, + g_param_spec_int ("maximum", "", "", + G_MININT, G_MAXINT, G_MAXINT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-entry-numeric.h b/clients/tui/newt/nmt-newt-entry-numeric.h new file mode 100644 index 00000000..43ac3449 --- /dev/null +++ b/clients/tui/newt/nmt-newt-entry-numeric.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_ENTRY_NUMERIC_H +#define NMT_NEWT_ENTRY_NUMERIC_H + +#include "nmt-newt-entry.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_ENTRY_NUMERIC (nmt_newt_entry_numeric_get_type ()) +#define NMT_NEWT_ENTRY_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumeric)) +#define NMT_NEWT_ENTRY_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericClass)) +#define NMT_IS_NEWT_ENTRY_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC)) +#define NMT_IS_NEWT_ENTRY_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_ENTRY_NUMERIC)) +#define NMT_NEWT_ENTRY_NUMERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericClass)) + +struct _NmtNewtEntryNumeric { + NmtNewtEntry parent; + +}; + +typedef struct { + NmtNewtEntryClass parent; + +} NmtNewtEntryNumericClass; + +GType nmt_newt_entry_numeric_get_type (void); + +NmtNewtWidget *nmt_newt_entry_numeric_new (int width, + int min, + int max); + +G_END_DECLS + +#endif /* NMT_NEWT_ENTRY_NUMERIC_H */ diff --git a/clients/tui/newt/nmt-newt-entry.c b/clients/tui/newt/nmt-newt-entry.c new file mode 100644 index 00000000..7c92d00a --- /dev/null +++ b/clients/tui/newt/nmt-newt-entry.c @@ -0,0 +1,540 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-entry + * @short_description: Text entries + * + * #NmtNewtEntry implements entry widgets, with optional filtering and + * validation. + * + * See also #NmtNewtEntryNumeric, for numeric-only entries. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-entry.h" +#include "nmt-newt-form.h" +#include "nmt-newt-hacks.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtEntry, nmt_newt_entry, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryPrivate)) + +typedef struct { + int width; + NmtNewtEntryFlags flags; + char *text; + int last_cursor_pos; + guint idle_update; + + NmtNewtEntryFilter filter; + gpointer filter_data; + + NmtNewtEntryValidator validator; + gpointer validator_data; +} NmtNewtEntryPrivate; + +enum { + PROP_0, + PROP_TEXT, + PROP_WIDTH, + PROP_FLAGS, + PROP_PASSWORD, + + LAST_PROP +}; + +/** + * NmtNewtEntryFlags: + * @NMT_NEWT_ENTRY_NOSCROLL: the entry content should not scroll left + * and right + * @NMT_NEWT_ENTRY_PASSWORD: the entry should show '*'s instead of its + * actual contents + * @NMT_NEWT_ENTRY_NONEMPTY: the entry should be considered not + * #NmtNewtWidget:valid if it is empty. + * + * Flags describing an #NmtNewtEntry + */ + +/** + * nmt_newt_entry_new: + * @width: the width in characters for the entry + * @flags: flags describing the entry + * + * Creates a new #NmtNewtEntry. + * + * Returns: a new #NmtNewtEntry + */ +NmtNewtWidget * +nmt_newt_entry_new (int width, + NmtNewtEntryFlags flags) +{ + return g_object_new (NMT_TYPE_NEWT_ENTRY, + "width", width, + "flags", flags, + NULL); +} + +/** + * NmtNewtEntryFilter: + * @entry: the #NmtNewtEntry + * @text: the current contents of @entry + * @ch: the character just typed + * @position: the position of the cursor in @entry + * @user_data: the data passed to nmt_newt_entry_set_filter() + * + * Callback function used to filter the contents of an entry. + * + * Returns: %TRUE if @ch should be accepted, %FALSE if not + */ + +/** + * nmt_newt_entry_set_filter: + * @entry: an #NmtNewtEntry + * @filter: the function to use to filter the entry + * @user_data: data for @filter + * + * Sets a #NmtNewtEntryFilter on @entry, to allow filtering out + * certain characters from the entry. + * + * Note that @filter will only be called for printable characters (eg, + * not for cursor-control characters or the like), and that it will + * only be called for user input, not, eg, for + * nmt_newt_entry_set_text(). + */ +void +nmt_newt_entry_set_filter (NmtNewtEntry *entry, + NmtNewtEntryFilter filter, + gpointer user_data) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + priv->filter = filter; + priv->filter_data = user_data; +} + +static void +nmt_newt_entry_check_valid (NmtNewtEntry *entry) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + gboolean valid; + + if ( (priv->flags & NMT_NEWT_ENTRY_NONEMPTY) + && *priv->text == '\0') + valid = FALSE; + else if (priv->validator) + valid = !!priv->validator (entry, priv->text, priv->validator_data); + else + valid = TRUE; + + nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (entry), valid); +} + +/** + * NmtNewtEntryValidator: + * @entry: the #NmtNewtEntry + * @text: the current contents of @entry + * @user_data: the data passed to nmt_newt_entry_set_validator() + * + * Callback function used to validate the contents of an entry. + * + * Returns: whether the entry is #NmtNewtWidget:valid + */ + +/** + * nmt_newt_entry_set_validator: + * @entry: an #NmtNewtEntry + * @validator: the function to use to validate the entry + * @user_data: data for @validator + * + * Sets a #NmtNewtEntryValidator on @entry, to allow validation of + * the entry contents. If @validator returns %FALSE, then the entry + * will not be considered #NmtNewtWidget:valid. + */ +void +nmt_newt_entry_set_validator (NmtNewtEntry *entry, + NmtNewtEntryValidator validator, + gpointer user_data) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + priv->validator = validator; + priv->validator_data = user_data; + + nmt_newt_entry_check_valid (entry); +} + +static void +nmt_newt_entry_set_text_internal (NmtNewtEntry *entry, + const char *text, + newtComponent co) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + if (!text) + text = ""; + + if (!strcmp (priv->text, text)) + return; + + g_free (priv->text); + priv->text = g_strdup (text); + + if (co) { + char *text_lc; + + text_lc = priv->text ? nmt_newt_locale_from_utf8 (priv->text) : NULL; + newtEntrySet (co, text_lc, TRUE); + g_free (text_lc); + priv->last_cursor_pos = -1; + } + + g_object_freeze_notify (G_OBJECT (entry)); + nmt_newt_entry_check_valid (entry); + g_object_notify (G_OBJECT (entry), "text"); + g_object_thaw_notify (G_OBJECT (entry)); +} + +/** + * nmt_newt_entry_set_text: + * @entry: an #NmtNewtEntry + * @text: the new text + * + * Updates @entry's text. Note that this skips the entry's + * #NmtNewtEntryFilter, but will cause its #NmtNewtEntryValidator to + * be re-run. + */ +void +nmt_newt_entry_set_text (NmtNewtEntry *entry, + const char *text) +{ + newtComponent co; + + co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (entry)); + nmt_newt_entry_set_text_internal (entry, text, co); +} + +/** + * nmt_newt_entry_get_text: + * @entry: an #NmtNewtEntry + * + * Gets @entry's text + * + * Returns: @entry's text + */ +const char * +nmt_newt_entry_get_text (NmtNewtEntry *entry) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + return priv->text; +} + +/** + * nmt_newt_entry_set_width: + * @entry: an #NmtNewtEntpry + * @widget: the new width + * + * Updates @entry's width + */ +void +nmt_newt_entry_set_width (NmtNewtEntry *entry, + int width) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + if (priv->width == width) + return; + + priv->width = width; + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry)); + + g_object_notify (G_OBJECT (entry), "width"); +} + +/** + * nmt_newt_entry_get_width: + * @entry: an #NmtNewtEntry + * + * Gets @entry's width + * + * Returns: @entry's width + */ +int +nmt_newt_entry_get_width (NmtNewtEntry *entry) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + return priv->width; +} + +static void +nmt_newt_entry_init (NmtNewtEntry *entry) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + priv->text = g_strdup (""); + priv->last_cursor_pos = -1; +} + +static void +nmt_newt_entry_constructed (GObject *object) +{ + nmt_newt_entry_check_valid (NMT_NEWT_ENTRY (object)); + + G_OBJECT_CLASS (nmt_newt_entry_parent_class)->constructed (object); +} + +static void +nmt_newt_entry_finalize (GObject *object) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (object); + + g_free (priv->text); + if (priv->idle_update) + g_source_remove (priv->idle_update); + + G_OBJECT_CLASS (nmt_newt_entry_parent_class)->finalize (object); +} + +static gboolean +idle_update_entry (gpointer entry) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + newtComponent co = nmt_newt_component_get_component (entry); + char *text; + + priv->idle_update = 0; + if (!co) + return FALSE; + + priv->last_cursor_pos = newtEntryGetCursorPosition (co); + + text = nmt_newt_locale_to_utf8 (newtEntryGetValue (co)); + nmt_newt_entry_set_text_internal (entry, text, NULL); + g_free (text); + + return FALSE; +} + +static int +entry_filter (newtComponent entry, + void *self, + int ch, + int cursor) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (self); + + if (g_ascii_isprint (ch)) { + if (priv->filter) { + char *text = nmt_newt_locale_to_utf8 (newtEntryGetValue (entry)); + + if (!priv->filter (self, text, ch, cursor, priv->filter_data)) { + g_free (text); + return 0; + } + g_free (text); + } + } + + if (!priv->idle_update) + priv->idle_update = g_idle_add (idle_update_entry, self); + return ch; +} + +static guint +convert_flags (NmtNewtEntryFlags flags) +{ + guint newt_flags = NEWT_FLAG_RETURNEXIT; + + if (!(flags & NMT_NEWT_ENTRY_NOSCROLL)) + newt_flags |= NEWT_FLAG_SCROLL; + if (flags & NMT_NEWT_ENTRY_PASSWORD) + newt_flags |= NEWT_FLAG_PASSWORD; + + return newt_flags; +} + +static newtComponent +nmt_newt_entry_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (component); + newtComponent co; + char *text_lc; + int flags; + + flags = convert_flags (priv->flags); + if (!sensitive) + flags |= NEWT_FLAG_DISABLED; + + text_lc = priv->text ? nmt_newt_locale_from_utf8 (priv->text) : NULL; + co = newtEntry (-1, -1, text_lc, priv->width, NULL, flags); + g_free (text_lc); + + if (priv->last_cursor_pos != -1) + newtEntrySetCursorPosition (co, priv->last_cursor_pos); + + newtEntrySetFilter (co, entry_filter, component); + return co; +} + +static void +nmt_newt_entry_activated (NmtNewtWidget *widget) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (widget); + + if (priv->idle_update) { + g_source_remove (priv->idle_update); + idle_update_entry (widget); + } + + NMT_NEWT_WIDGET_CLASS (nmt_newt_entry_parent_class)->activated (widget); +} + +static void +nmt_newt_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtEntry *entry = NMT_NEWT_ENTRY (object); + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + switch (prop_id) { + case PROP_TEXT: + nmt_newt_entry_set_text (entry, g_value_get_string (value)); + break; + case PROP_WIDTH: + nmt_newt_entry_set_width (entry, g_value_get_int (value)); + break; + case PROP_FLAGS: + priv->flags = g_value_get_uint (value); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry)); + break; + case PROP_PASSWORD: + if (g_value_get_boolean (value)) + priv->flags |= NMT_NEWT_ENTRY_PASSWORD; + else + priv->flags &= ~NMT_NEWT_ENTRY_PASSWORD; + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtEntry *entry = NMT_NEWT_ENTRY (object); + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + switch (prop_id) { + case PROP_TEXT: + g_value_set_string (value, nmt_newt_entry_get_text (entry)); + break; + case PROP_WIDTH: + g_value_set_int (value, priv->width); + break; + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_PASSWORD: + g_value_set_boolean (value, (priv->flags & NMT_NEWT_ENTRY_PASSWORD) != 0); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_entry_class_init (NmtNewtEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (entry_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtNewtEntryPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_newt_entry_constructed; + object_class->set_property = nmt_newt_entry_set_property; + object_class->get_property = nmt_newt_entry_get_property; + object_class->finalize = nmt_newt_entry_finalize; + + widget_class->activated = nmt_newt_entry_activated; + + component_class->build_component = nmt_newt_entry_build_component; + + /** + * NmtNewtEntry:text + * + * The entry's text + */ + g_object_class_install_property + (object_class, PROP_TEXT, + g_param_spec_string ("text", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtEntry:width + * + * The entry's width in characters + */ + g_object_class_install_property + (object_class, PROP_WIDTH, + g_param_spec_int ("width", "", "", + -1, 80, -1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtEntry:flags + * + * The entry's #NmtNewtEntryFlags + */ + g_object_class_install_property + (object_class, PROP_FLAGS, + g_param_spec_uint ("flags", "", "", + 0, 0xFFFF, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtEntry:password + * + * %TRUE if #NmtNewtEntry:flags contains %NMT_NEWT_ENTRY_PASSWORD, + * %FALSE if not. + */ + g_object_class_install_property + (object_class, PROP_PASSWORD, + g_param_spec_boolean ("password", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-entry.h b/clients/tui/newt/nmt-newt-entry.h new file mode 100644 index 00000000..8df0b137 --- /dev/null +++ b/clients/tui/newt/nmt-newt-entry.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_ENTRY_H +#define NMT_NEWT_ENTRY_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_ENTRY (nmt_newt_entry_get_type ()) +#define NMT_NEWT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntry)) +#define NMT_NEWT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryClass)) +#define NMT_IS_NEWT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_ENTRY)) +#define NMT_IS_NEWT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_ENTRY)) +#define NMT_NEWT_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryClass)) + +struct _NmtNewtEntry { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtEntryClass; + +GType nmt_newt_entry_get_type (void); + +typedef gboolean (*NmtNewtEntryFilter) (NmtNewtEntry *, const char *text, int ch, int position, gpointer); +typedef gboolean (*NmtNewtEntryValidator) (NmtNewtEntry *, const char *text, gpointer); + +typedef enum { + NMT_NEWT_ENTRY_NOSCROLL = (1 << 0), + NMT_NEWT_ENTRY_PASSWORD = (1 << 1), + NMT_NEWT_ENTRY_NONEMPTY = (1 << 2) +} NmtNewtEntryFlags; + +NmtNewtWidget *nmt_newt_entry_new (int width, + NmtNewtEntryFlags flags); + +void nmt_newt_entry_set_filter (NmtNewtEntry *entry, + NmtNewtEntryFilter filter, + gpointer user_data); +void nmt_newt_entry_set_validator (NmtNewtEntry *entry, + NmtNewtEntryValidator validator, + gpointer user_data); + +void nmt_newt_entry_set_text (NmtNewtEntry *entry, + const char *text); +const char *nmt_newt_entry_get_text (NmtNewtEntry *entry); + +void nmt_newt_entry_set_width (NmtNewtEntry *entry, + int width); +int nmt_newt_entry_get_width (NmtNewtEntry *entry); + +G_END_DECLS + +#endif /* NMT_NEWT_ENTRY_H */ diff --git a/clients/tui/newt/nmt-newt-form.c b/clients/tui/newt/nmt-newt-form.c new file mode 100644 index 00000000..19bd3998 --- /dev/null +++ b/clients/tui/newt/nmt-newt-form.c @@ -0,0 +1,753 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-form + * @short_description: The top-level NmtNewt widget + * + * #NmtNewtForm is the top-level widget that contains and presents a + * "form" (aka dialog) to the user. + */ + +#include "config.h" + +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include <glib/gi18n-lib.h> + +#include "nmt-newt-form.h" +#include "nmt-newt-button.h" +#include "nmt-newt-grid.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtForm, nmt_newt_form, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_FORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_FORM, NmtNewtFormPrivate)) + +typedef struct { + newtComponent form; + NmtNewtWidget *content; + + guint x, y, width, height; + guint padding; + gboolean fixed_x, fixed_y; + gboolean fixed_width, fixed_height; + char *title_lc; + + gboolean dirty, escape_exits; + NmtNewtWidget *focus; +#ifdef HAVE_NEWTFORMGETSCROLLPOSITION + int scroll_position = 0; +#endif +} NmtNewtFormPrivate; + +enum { + PROP_0, + PROP_TITLE, + PROP_FULLSCREEN, + PROP_FULLSCREEN_VERTICAL, + PROP_FULLSCREEN_HORIZONTAL, + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT, + PROP_PADDING, + PROP_ESCAPE_EXITS, + + LAST_PROP +}; + +enum { + QUIT, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void nmt_newt_form_redraw (NmtNewtForm *form); + +/** + * nmt_newt_form_new: + * @title: (allow-none): the form title + * + * Creates a new form, which will be shown centered on the screen. + * Compare nmt_newt_form_new_fullscreen(). You can also position a + * form manually by setting its #NmtNewtForm:x and #NmtNewtForm:y + * properties at construct time, and/or by setting + * #NmtNewtForm:fullscreen, #NmtNewtform:fullscreen-horizontal, or + * #NmtNewtForm:fullscreen-vertical. + * + * If @title is NULL, the form will have no title. + * + * Returns: a new #NmtNewtForm + */ +NmtNewtForm * +nmt_newt_form_new (const char *title) +{ + return g_object_new (NMT_TYPE_NEWT_FORM, + "title", title, + NULL); +} + +/** + * nmt_newt_form_new_fullscreen: + * @title: (allow-none): the form title + * + * Creates a new fullscreen form. Compare nmt_newt_form_new(). + * + * If @title is NULL, the form will have no title. + * + * Returns: a new #NmtNewtForm + */ +NmtNewtForm * +nmt_newt_form_new_fullscreen (const char *title) +{ + return g_object_new (NMT_TYPE_NEWT_FORM, + "title", title, + "fullscreen", TRUE, + NULL); +} + +static void +nmt_newt_form_init (NmtNewtForm *form) +{ + g_object_ref_sink (form); +} + +static void +nmt_newt_form_finalize (GObject *object) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object); + + g_free (priv->title_lc); + g_clear_object (&priv->focus); + + G_OBJECT_CLASS (nmt_newt_form_parent_class)->finalize (object); +} + +static void +nmt_newt_form_needs_rebuild (NmtNewtWidget *widget) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (widget); + + if (!priv->dirty) { + priv->dirty = TRUE; + nmt_newt_form_redraw (NMT_NEWT_FORM (widget)); + } +} + +static void +nmt_newt_form_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (container); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_form_parent_class); + + g_return_if_fail (widget == priv->content); + + parent_class->remove (container, widget); + priv->content = NULL; +} + +/** + * nmt_newt_form_set_content: + * @form: the #NmtNewtForm + * @content: the form's content + * + * Sets @form's content to be @content. + */ +void +nmt_newt_form_set_content (NmtNewtForm *form, + NmtNewtWidget *content) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_form_parent_class); + + if (priv->content) + nmt_newt_form_remove (NMT_NEWT_CONTAINER (form), priv->content); + + priv->content = content; + + if (priv->content) + parent_class->add (NMT_NEWT_CONTAINER (form), content); +} + +static void +nmt_newt_form_build (NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + int screen_height, screen_width, form_height, form_width; + newtComponent *cos; + int i; + + priv->dirty = FALSE; + nmt_newt_widget_realize (NMT_NEWT_WIDGET (form)); + + nmt_newt_widget_size_request (priv->content, &form_width, &form_height); + newtGetScreenSize (&screen_width, &screen_height); + + if (!priv->fixed_width) + priv->width = MIN (form_width + 2 * priv->padding, screen_width - 2); + if (!priv->fixed_height) + priv->height = MIN (form_height + 2 * priv->padding, screen_height - 2); + + if (!priv->fixed_x) + priv->x = (screen_width - form_width) / 2; + if (!priv->fixed_y) + priv->y = (screen_height - form_height) / 2; + + nmt_newt_widget_size_allocate (priv->content, + priv->padding, + priv->padding, + priv->width - 2 * priv->padding, + priv->height - 2 * priv->padding); + + if (priv->height - 2 * priv->padding < form_height) { + newtComponent scroll_bar = + newtVerticalScrollbar (priv->width - 1, 0, priv->height, + NEWT_COLORSET_WINDOW, + NEWT_COLORSET_ACTCHECKBOX); + + priv->form = newtForm (scroll_bar, NULL, NEWT_FLAG_NOF12); + newtFormAddComponent (priv->form, scroll_bar); + newtFormSetHeight (priv->form, priv->height - 2); + } else + priv->form = newtForm (NULL, NULL, NEWT_FLAG_NOF12); + + if (priv->escape_exits) + newtFormAddHotKey (priv->form, NEWT_KEY_ESCAPE); + + cos = nmt_newt_widget_get_components (priv->content); + for (i = 0; cos[i]; i++) + newtFormAddComponent (priv->form, cos[i]); + g_free (cos); + + if (priv->focus) { + newtComponent fco; + + fco = nmt_newt_widget_get_focus_component (priv->focus); + if (fco) + newtFormSetCurrent (priv->form, fco); + } +#ifdef HAVE_NEWTFORMGETSCROLLPOSITION + if (priv->scroll_position) + newtFormSetScrollPosition (priv->form, priv->scroll_position); +#endif + + newtOpenWindow (priv->x, priv->y, priv->width, priv->height, priv->title_lc); +} + +static void +nmt_newt_form_destroy (NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + +#ifdef HAVE_NEWTFORMGETSCROLLPOSITION + priv->scroll_position = newtFormGetScrollPosition (priv->form); +#endif + + newtFormDestroy (priv->form); + priv->form = NULL; + newtPopWindowNoRefresh (); + + nmt_newt_widget_unrealize (NMT_NEWT_WIDGET (form)); +} + +/* A "normal" newt program would call newtFormRun() to run newt's main loop + * and process events. But we want to let GLib's main loop control the program + * (eg, so libnm can process D-Bus notifications). So we call this function + * to run a single iteration of newt's main loop (or rather, to run newt's + * main loop for 1ms) whenever there are events for newt to process (redrawing + * or keypresses). + */ +static void +nmt_newt_form_iterate (NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + NmtNewtWidget *focus; + struct newtExitStruct es; + + if (priv->dirty) { + nmt_newt_form_destroy (form); + nmt_newt_form_build (form); + } + + newtFormSetTimer (priv->form, 1); + newtFormRun (priv->form, &es); + + if ( es.reason == NEWT_EXIT_HOTKEY + || es.reason == NEWT_EXIT_ERROR) { + /* The user hit Esc or there was an error. */ + g_clear_object (&priv->focus); + nmt_newt_form_quit (form); + return; + } + + if (es.reason == NEWT_EXIT_COMPONENT) { + /* The user hit Return/Space on a component; update the form focus + * to point that that component, and activate it. + */ + focus = nmt_newt_widget_find_component (priv->content, es.u.co); + if (focus) { + nmt_newt_form_set_focus (form, focus); + nmt_newt_widget_activated (focus); + } + } else { + /* The 1ms timer ran out. Update focus but don't do anything else. */ + focus = nmt_newt_widget_find_component (priv->content, + newtFormGetCurrent (priv->form)); + if (focus) + nmt_newt_form_set_focus (form, focus); + } +} + +/* @form_stack keeps track of all currently-displayed forms, from top to bottom. + * @keypress_source is the global stdin-monitoring GSource. When it triggers, + * nmt_newt_form_keypress_callback() iterates the top-most form, so it can + * process the keypress. + */ +static GSList *form_stack; +static GSource *keypress_source; + +static gboolean +nmt_newt_form_keypress_callback (int fd, + GIOCondition condition, + gpointer user_data) +{ + g_return_val_if_fail (form_stack != NULL, FALSE); + + nmt_newt_form_iterate (form_stack->data); + return TRUE; +} + +static gboolean +nmt_newt_form_timeout_callback (gpointer user_data) +{ + if (form_stack) + nmt_newt_form_iterate (form_stack->data); + return FALSE; +} + +static void +nmt_newt_form_redraw (NmtNewtForm *form) +{ + g_timeout_add (0, nmt_newt_form_timeout_callback, NULL); +} + +static void +nmt_newt_form_real_show (NmtNewtForm *form) +{ + if (!keypress_source) { + GIOChannel *io; + + io = g_io_channel_unix_new (STDIN_FILENO); + keypress_source = g_io_create_watch (io, G_IO_IN); + g_source_set_can_recurse (keypress_source, TRUE); + g_source_set_callback (keypress_source, + (GSourceFunc) nmt_newt_form_keypress_callback, + NULL, NULL); + g_source_attach (keypress_source, NULL); + g_io_channel_unref (io); + } + + nmt_newt_form_build (form); + form_stack = g_slist_prepend (form_stack, g_object_ref (form)); + nmt_newt_form_redraw (form); +} + +/** + * nmt_newt_form_show: + * @form: an #NmtNewtForm + * + * Displays @form and begins running it asynchronously in the default + * #GMainContext. If another form is currently running, it will remain + * visible in the background, but will not be able to receive keyboard + * input until @form exits. + * + * Call nmt_newt_form_quit() to quit the form. + */ +void +nmt_newt_form_show (NmtNewtForm *form) +{ + NMT_NEWT_FORM_GET_CLASS (form)->show (form); +} + +/** + * nmt_newt_form_run_sync: + * @form: an #NmtNewtForm + * + * Displays @form as with nmt_newt_form_show(), but then iterates the + * #GMainContext internally until @form exits. + * + * Returns: the widget whose activation caused @form to exit, or + * %NULL if it was not caused by a widget. FIXME: this exit value is + * sort of weird and may not be 100% accurate anyway. + */ +NmtNewtWidget * +nmt_newt_form_run_sync (NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + + nmt_newt_form_show (form); + while (priv->form) + g_main_context_iteration (NULL, TRUE); + + return priv->focus; +} + +/** + * nmt_newt_form_quit: + * @form: an #NmtNewtForm + * + * Causes @form to exit. + */ +void +nmt_newt_form_quit (NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + + g_return_if_fail (priv->form != NULL); + + nmt_newt_form_destroy (form); + + form_stack = g_slist_remove (form_stack, form); + + if (form_stack) + nmt_newt_form_iterate (form_stack->data); + else if (keypress_source) { + g_source_destroy (keypress_source); + g_clear_pointer (&keypress_source, g_source_unref); + } + + g_signal_emit (form, signals[QUIT], 0); + g_object_unref (form); +} + +/** + * nmt_newt_form_set_focus: + * @form: an #NmtNewtForm + * @widget: the widget to focus + * + * Focuses @widget in @form. + */ +void +nmt_newt_form_set_focus (NmtNewtForm *form, + NmtNewtWidget *widget) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + + g_return_if_fail (priv->form != NULL); + + if (priv->focus == widget) + return; + + if (priv->focus) + g_object_unref (priv->focus); + priv->focus = widget; + if (priv->focus) + g_object_ref (priv->focus); +} + +static void +nmt_newt_form_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object); + int screen_width, screen_height; + + switch (prop_id) { + case PROP_TITLE: + if (g_value_get_string (value)) { + priv->title_lc = nmt_newt_locale_from_utf8 (g_value_get_string (value)); + } else + priv->title_lc = NULL; + break; + case PROP_FULLSCREEN: + if (g_value_get_boolean (value)) { + newtGetScreenSize (&screen_width, &screen_height); + priv->x = priv->y = 2; + priv->fixed_x = priv->fixed_y = TRUE; + priv->width = screen_width - 4; + priv->height = screen_height - 4; + priv->fixed_width = priv->fixed_height = TRUE; + } + break; + case PROP_FULLSCREEN_VERTICAL: + if (g_value_get_boolean (value)) { + newtGetScreenSize (&screen_width, &screen_height); + priv->y = 2; + priv->fixed_y = TRUE; + priv->height = screen_height - 4; + priv->fixed_height = TRUE; + } + break; + case PROP_FULLSCREEN_HORIZONTAL: + if (g_value_get_boolean (value)) { + newtGetScreenSize (&screen_width, &screen_height); + priv->x = 2; + priv->fixed_x = TRUE; + priv->width = screen_width - 4; + priv->fixed_width = TRUE; + } + break; + case PROP_X: + if (g_value_get_uint (value)) { + priv->x = g_value_get_uint (value); + priv->fixed_x = TRUE; + } + break; + case PROP_Y: + if (g_value_get_uint (value)) { + priv->y = g_value_get_uint (value); + priv->fixed_y = TRUE; + } + break; + case PROP_WIDTH: + if (g_value_get_uint (value)) { + priv->width = g_value_get_uint (value); + priv->fixed_width = TRUE; + } + break; + case PROP_HEIGHT: + if (g_value_get_uint (value)) { + priv->height = g_value_get_uint (value); + priv->fixed_height = TRUE; + } + break; + case PROP_PADDING: + priv->padding = g_value_get_uint (value); + break; + case PROP_ESCAPE_EXITS: + priv->escape_exits = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_form_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_TITLE: + if (priv->title_lc) { + g_value_take_string (value, nmt_newt_locale_to_utf8 (priv->title_lc)); + } else + g_value_set_string (value, NULL); + break; + case PROP_X: + g_value_set_uint (value, priv->x); + break; + case PROP_Y: + g_value_set_uint (value, priv->y); + break; + case PROP_WIDTH: + g_value_set_uint (value, priv->width); + break; + case PROP_HEIGHT: + g_value_set_uint (value, priv->height); + break; + case PROP_PADDING: + g_value_set_uint (value, priv->padding); + break; + case PROP_ESCAPE_EXITS: + g_value_set_boolean (value, priv->escape_exits); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_form_class_init (NmtNewtFormClass *form_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (form_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (form_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (form_class); + + g_type_class_add_private (form_class, sizeof (NmtNewtFormPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_form_set_property; + object_class->get_property = nmt_newt_form_get_property; + object_class->finalize = nmt_newt_form_finalize; + + widget_class->needs_rebuild = nmt_newt_form_needs_rebuild; + + container_class->remove = nmt_newt_form_remove; + + form_class->show = nmt_newt_form_real_show; + + /* signals */ + + /** + * NmtNewtForm::quit: + * @form: the #NmtNewtForm + * + * Emitted when the form quits. + */ + signals[QUIT] = + g_signal_new ("quit", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NmtNewtFormClass, quit), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /** + * NmtNewtForm:title: + * + * The form's title. If non-%NULL, this will be displayed above + * the form in its border. + */ + g_object_class_install_property + (object_class, PROP_TITLE, + g_param_spec_string ("title", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:fullscreen: + * + * If %TRUE, the form will fill the entire "screen" (ie, terminal + * window). + */ + g_object_class_install_property + (object_class, PROP_FULLSCREEN, + g_param_spec_boolean ("fullscreen", "", "", + FALSE, + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:fullscreen-vertical: + * + * If %TRUE, the form will fill the entire "screen" (ie, terminal + * window) vertically, but not necessarily horizontally. + */ + g_object_class_install_property + (object_class, PROP_FULLSCREEN_VERTICAL, + g_param_spec_boolean ("fullscreen-vertical", "", "", + FALSE, + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:fullscreen-horizontal: + * + * If %TRUE, the form will fill the entire "screen" (ie, terminal + * window) horizontally, but not necessarily vertically. + */ + g_object_class_install_property + (object_class, PROP_FULLSCREEN_HORIZONTAL, + g_param_spec_boolean ("fullscreen-horizontal", "", "", + FALSE, + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:x: + * + * The form's x coordinate. By default, the form will be centered + * on the screen. + */ + g_object_class_install_property + (object_class, PROP_X, + g_param_spec_uint ("x", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:y: + * + * The form's y coordinate. By default, the form will be centered + * on the screen. + */ + g_object_class_install_property + (object_class, PROP_Y, + g_param_spec_uint ("y", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:width: + * + * The form's width. By default, this will be determined by the + * width of the form's content. + */ + g_object_class_install_property + (object_class, PROP_WIDTH, + g_param_spec_uint ("width", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:height: + * + * The form's height. By default, this will be determined by the + * height of the form's content. + */ + g_object_class_install_property + (object_class, PROP_HEIGHT, + g_param_spec_uint ("height", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:padding: + * + * The padding between the form's content and its border. + */ + g_object_class_install_property + (object_class, PROP_PADDING, + g_param_spec_uint ("padding", "", "", + 0, G_MAXUINT, 1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:escape-exits: + * + * If %TRUE, then hitting the Escape key will cause the form to + * exit. + */ + g_object_class_install_property + (object_class, PROP_ESCAPE_EXITS, + g_param_spec_boolean ("escape-exits", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); +} diff --git a/clients/tui/newt/nmt-newt-form.h b/clients/tui/newt/nmt-newt-form.h new file mode 100644 index 00000000..be95eb68 --- /dev/null +++ b/clients/tui/newt/nmt-newt-form.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_FORM_H +#define NMT_NEWT_FORM_H + +#include "nmt-newt-container.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_FORM (nmt_newt_form_get_type ()) +#define NMT_NEWT_FORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_FORM, NmtNewtForm)) +#define NMT_NEWT_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_FORM, NmtNewtFormClass)) +#define NMT_IS_NEWT_FORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_FORM)) +#define NMT_IS_NEWT_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_FORM)) +#define NMT_NEWT_FORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_FORM, NmtNewtFormClass)) + +struct _NmtNewtForm { + NmtNewtContainer parent; + +}; + +typedef struct { + NmtNewtContainerClass parent; + + /* signals */ + void (*quit) (NmtNewtForm *form); + + /* methods */ + void (*show) (NmtNewtForm *form); + +} NmtNewtFormClass; + +GType nmt_newt_form_get_type (void); + +NmtNewtForm *nmt_newt_form_new (const char *title); +NmtNewtForm *nmt_newt_form_new_fullscreen (const char *title); + +void nmt_newt_form_set_content (NmtNewtForm *form, + NmtNewtWidget *content); + +void nmt_newt_form_show (NmtNewtForm *form); +NmtNewtWidget *nmt_newt_form_run_sync (NmtNewtForm *form); +void nmt_newt_form_quit (NmtNewtForm *form); + +void nmt_newt_form_set_focus (NmtNewtForm *form, + NmtNewtWidget *widget); + +G_END_DECLS + +#endif /* NMT_NEWT_FORM_H */ diff --git a/clients/tui/newt/nmt-newt-grid.c b/clients/tui/newt/nmt-newt-grid.c new file mode 100644 index 00000000..ac967334 --- /dev/null +++ b/clients/tui/newt/nmt-newt-grid.c @@ -0,0 +1,472 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-grid + * @short_description: Grid container + * + * #NmtNewtGrid is the most general-purpose container widget in NmtNewt. + * + * An #NmtNewtGrid consists of a number of rows and columns. There is + * no pre-established maximum row or columns. Rather, rows and columns + * exist if and only if there are widgets in them. + * + * The width of each column is the width of the widest widget in that + * column, and the height of each row is the height of the tallest + * widget in that row. Empty rows and empty columns take up no space, + * so a grid with a single widget at 0,0 would look exactly the same + * if the widget was at 5,10 instead. + * + * If a widget's cell ends up being larger than the widget's requested + * size, then by default the widget will be centered in its cell. + * However, this can be modified by changing its #NmtNewtGridFlags. + * FIXME: the FILL/ANCHOR flags can be implemented in #NmtNewtWidget + * and so should move there. Less clear about the EXPAND flags, which + * must be implemented by the container... + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-grid.h" + +G_DEFINE_TYPE (NmtNewtGrid, nmt_newt_grid, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_GRID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_GRID, NmtNewtGridPrivate)) + +typedef struct { + NmtNewtWidget *widget; + int x, y; + NmtNewtGridFlags flags; + int req_height, req_width; +} NmtNewtGridChild; + +typedef struct { + GArray *children; + int max_x, max_y; + int *row_heights, *col_widths; + gboolean *expand_rows, *expand_cols; + int n_expand_rows, n_expand_cols; + int req_height, req_width; +} NmtNewtGridPrivate; + +/** + * nmt_newt_grid_new: + * + * Creates a new #NmtNewtGrid + * + * Returns: a new #NmtNewtGrid + */ +NmtNewtWidget * +nmt_newt_grid_new (void) +{ + return g_object_new (NMT_TYPE_NEWT_GRID, NULL); +} + +static void +nmt_newt_grid_init (NmtNewtGrid *grid) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + + priv->children = g_array_new (FALSE, FALSE, sizeof (NmtNewtGridChild)); +} + +static void +nmt_newt_grid_finalize (GObject *object) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (object); + + g_array_unref (priv->children); + g_clear_pointer (&priv->row_heights, g_free); + g_clear_pointer (&priv->col_widths, g_free); + g_clear_pointer (&priv->expand_rows, g_free); + g_clear_pointer (&priv->expand_cols, g_free); + + G_OBJECT_CLASS (nmt_newt_grid_parent_class)->finalize (object); +} + +static int +child_sort_func (gconstpointer a, + gconstpointer b) +{ + NmtNewtGridChild *child_a = (NmtNewtGridChild *)a; + NmtNewtGridChild *child_b = (NmtNewtGridChild *)b; + + if (child_a->y != child_b->y) + return child_a->y - child_b->y; + else + return child_a->x - child_b->x; +} + +static newtComponent * +nmt_newt_grid_get_components (NmtNewtWidget *widget) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (widget); + NmtNewtGridChild *children; + GPtrArray *cos; + newtComponent *child_cos; + int i, c; + + g_array_sort (priv->children, child_sort_func); + children = (NmtNewtGridChild *)priv->children->data; + + cos = g_ptr_array_new (); + + for (i = 0; i < priv->children->len; i++) { + if (!nmt_newt_widget_get_visible (children[i].widget)) + continue; + + child_cos = nmt_newt_widget_get_components (children[i].widget); + for (c = 0; child_cos[c]; c++) + g_ptr_array_add (cos, child_cos[c]); + g_free (child_cos); + } + g_ptr_array_add (cos, NULL); + + return (newtComponent *) g_ptr_array_free (cos, FALSE); +} + +static void +nmt_newt_grid_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtGrid *grid = NMT_NEWT_GRID (widget); + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data; + int row, col, i; + + g_free (priv->row_heights); + g_free (priv->col_widths); + g_free (priv->expand_rows); + g_free (priv->expand_cols); + + priv->row_heights = g_new0 (int, priv->max_y + 1); + priv->col_widths = g_new0 (int, priv->max_x + 1); + priv->expand_rows = g_new0 (gboolean, priv->max_y + 1); + priv->expand_cols = g_new0 (gboolean, priv->max_x + 1); + priv->n_expand_rows = priv->n_expand_cols = 0; + + for (row = 0; row < priv->max_y + 1; row++) { + for (col = 0; col < priv->max_x + 1; col++) { + for (i = 0; i < priv->children->len; i++) { + if (children[i].x != col || children[i].y != row) + continue; + if (!nmt_newt_widget_get_visible (children[i].widget)) + continue; + + nmt_newt_widget_size_request (children[i].widget, + &children[i].req_width, + &children[i].req_height); + if (children[i].req_height > priv->row_heights[row]) + priv->row_heights[row] = children[i].req_height; + if (children[i].req_width > priv->col_widths[col]) + priv->col_widths[col] = children[i].req_width; + + if ( (children[i].flags & NMT_NEWT_GRID_EXPAND_X) + && !priv->expand_cols[children[i].x]) { + priv->expand_cols[children[i].x] = TRUE; + priv->n_expand_cols++; + } + if ( (children[i].flags & NMT_NEWT_GRID_EXPAND_Y) + && !priv->expand_rows[children[i].y]) { + priv->expand_rows[children[i].y] = TRUE; + priv->n_expand_rows++; + } + } + } + } + + priv->req_height = priv->req_width = 0; + for (row = 0; row < priv->max_y + 1; row++) + priv->req_height += priv->row_heights[row]; + for (col = 0; col < priv->max_x + 1; col++) + priv->req_width += priv->col_widths[col]; + + *height = priv->req_height; + *width = priv->req_width; +} + +static void +nmt_newt_grid_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (widget); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data, *child; + int i, row, col; + int child_x, child_y, child_width, child_height; + int extra, extra_all, extra_some; + + extra = width - priv->req_width; + if (extra > 0 && priv->n_expand_cols) { + extra_all = extra / priv->n_expand_cols; + extra_some = extra % priv->n_expand_cols; + + for (col = 0; col < priv->max_x + 1; col++) { + if (!priv->expand_cols[col]) + continue; + priv->col_widths[col] += extra_all; + if (extra_some) { + priv->col_widths[col]++; + extra_some--; + } + } + } + + extra = height - priv->req_height; + if (extra > 0 && priv->n_expand_rows) { + extra_all = extra / priv->n_expand_rows; + extra_some = extra % priv->n_expand_rows; + + for (row = 0; row < priv->max_y + 1; row++) { + if (!priv->expand_rows[row]) + continue; + priv->row_heights[row] += extra_all; + if (extra_some) { + priv->row_heights[row]++; + extra_some--; + } + } + } + + for (i = 0; i < priv->children->len; i++) { + child = &children[i]; + if (!nmt_newt_widget_get_visible (child->widget)) + continue; + + child_x = x; + for (col = 0; col < child->x; col++) + child_x += priv->col_widths[col]; + + if ((child->flags & NMT_NEWT_GRID_FILL_X) == NMT_NEWT_GRID_FILL_X) { + child_width = priv->col_widths[child->x]; + } else { + child_width = child->req_width; + if (child->flags & NMT_NEWT_GRID_ANCHOR_RIGHT) + child_x += priv->col_widths[child->x] - child->req_width; + else if (!(child->flags & NMT_NEWT_GRID_ANCHOR_LEFT)) + child_x += (priv->col_widths[child->x] - child->req_width) / 2; + } + + child_y = y; + for (row = 0; row < child->y; row++) + child_y += priv->row_heights[row]; + + if ((child->flags & NMT_NEWT_GRID_FILL_Y) == NMT_NEWT_GRID_FILL_Y) { + child_height = priv->row_heights[child->y]; + } else { + child_height = child->req_height; + if (child->flags & NMT_NEWT_GRID_ANCHOR_BOTTOM) + child_y += priv->row_heights[child->y] - child->req_height; + else if (!(child->flags & NMT_NEWT_GRID_ANCHOR_TOP)) + child_y += (priv->row_heights[child->y] - child->req_height) / 2; + } + + nmt_newt_widget_size_allocate (child->widget, + child_x, child_y, + child_width, child_height); + } +} + +static void +nmt_newt_grid_find_size (NmtNewtGrid *grid) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data; + int i; + + priv->max_x = priv->max_y = 0; + for (i = 0; i < priv->children->len; i++) { + if (children[i].x > priv->max_x) + priv->max_x = children[i].x; + if (children[i].y > priv->max_y) + priv->max_y = children[i].y; + } +} + +/** + * nmt_newt_grid_add: + * @grid: an #NmtNewtGrid + * @widget: the widget to add + * @x: x coordinate + * @y: y coordinate + * + * Adds @widget to @grid at @x, @y. See the discussion above for more + * details of exactly how this works. + */ +void +nmt_newt_grid_add (NmtNewtGrid *grid, + NmtNewtWidget *widget, + int x, + int y) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild child; + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_grid_parent_class)->add (NMT_NEWT_CONTAINER (grid), widget); + + memset (&child, 0, sizeof (child)); + child.widget = widget; + child.x = x; + child.y = y; + child.flags = NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y; + g_array_append_val (priv->children, child); + + if (x > priv->max_x) + priv->max_x = x; + if (y > priv->max_y) + priv->max_y = y; +} + +static int +find_child (NmtNewtGrid *grid, + NmtNewtWidget *widget) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data; + int i; + + for (i = 0; i < priv->children->len; i++) { + if (children[i].widget == widget) + return i; + } + + return -1; +} + +static void +nmt_newt_grid_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtGrid *grid = NMT_NEWT_GRID (container); + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + int i; + + i = find_child (grid, widget); + if (i != -1) { + g_array_remove_index (priv->children, i); + nmt_newt_grid_find_size (grid); + } + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_grid_parent_class)->remove (container, widget); +} + +/** + * nmt_newt_grid_move: + * @grid: an #NmtNewtGrid + * @widget: a child of @grid + * @x: x coordinate + * @y: y coordinate + * + * Moves @widget to the given new coordinates. + */ +void +nmt_newt_grid_move (NmtNewtGrid *grid, + NmtNewtWidget *widget, + int x, + int y) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data; + int i; + + i = find_child (grid, widget); + if (i != -1 && (children[i].x != x || children[i].y != y)) { + children[i].x = x; + children[i].y = y; + nmt_newt_grid_find_size (grid); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (grid)); + } +} + +/** + * NmtNewtGridFlags: + * @NMT_NEWT_GRID_EXPAND_X: The widget's cell should expand + * horizontally if the grid as a whole is given more width than + * it requested. + * @NMT_NEWT_GRID_EXPAND_Y: The widget's cell should expand + * vertically if the grid as a whole is given more height than + * it requested. + * @NMT_NEWT_GRID_ANCHOR_LEFT: If the widget's cell is wider than + * the widget requested, the widget should be anchored to the + * left of its cell rather than being centered. + * @NMT_NEWT_GRID_ANCHOR_RIGHT: If the widget's cell is wider than + * the widget requested, the widget should be anchored to the + * right of its cell rather than being centered. + * @NMT_NEWT_GRID_FILL_X: If the widget's cell is wider than + * the widget requested, the widget should be allocated the + * full width of the cell; this is equivalent to specifying + * both %NMT_NEWT_GRID_ANCHOR_LEFT and %NMT_NEWT_GRID_ANCHOR_RIGHT. + * @NMT_NEWT_GRID_ANCHOR_TOP: If the widget's cell is taller than + * the widget requested, the widget should be anchored to the + * top of its cell rather than being centered. + * @NMT_NEWT_GRID_ANCHOR_BOTTOM: If the widget's cell is taller than + * the widget requested, the widget should be anchored to the + * bottom of its cell rather than being centered. + * @NMT_NEWT_GRID_FILL_Y: If the widget's cell is taller than + * the widget requested, the widget should be allocated the + * full height of the cell; this is equivalent to specifying + * both %NMT_NEWT_GRID_ANCHOR_TOP and %NMT_NEWT_GRID_ANCHOR_BOTTOM. + * + * Flags describing how a widget is placed within its grid cell. + */ + +/** + * nmt_newt_grid_set_flags: + * @grid: an #NmtNewtGrid + * @widget: a child of @grid + * @flags: #NmtNewtGridFlags for @widget + * + * Sets the #NmtNewtGridFlags on @widget + */ +void +nmt_newt_grid_set_flags (NmtNewtGrid *grid, + NmtNewtWidget *widget, + NmtNewtGridFlags flags) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data; + int i; + + i = find_child (grid, widget); + if (i != -1) + children[i].flags = flags; +} + +static void +nmt_newt_grid_class_init (NmtNewtGridClass *grid_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (grid_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (grid_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (grid_class); + + g_type_class_add_private (grid_class, sizeof (NmtNewtGridPrivate)); + + /* virtual methods */ + object_class->finalize = nmt_newt_grid_finalize; + + widget_class->get_components = nmt_newt_grid_get_components; + widget_class->size_request = nmt_newt_grid_size_request; + widget_class->size_allocate = nmt_newt_grid_size_allocate; + + container_class->remove = nmt_newt_grid_remove; +} diff --git a/clients/tui/newt/nmt-newt-grid.h b/clients/tui/newt/nmt-newt-grid.h new file mode 100644 index 00000000..f36a38ee --- /dev/null +++ b/clients/tui/newt/nmt-newt-grid.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_GRID_H +#define NMT_NEWT_GRID_H + +#include "nmt-newt-container.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_GRID (nmt_newt_grid_get_type ()) +#define NMT_NEWT_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_GRID, NmtNewtGrid)) +#define NMT_NEWT_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_GRID, NmtNewtGridClass)) +#define NMT_IS_NEWT_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_GRID)) +#define NMT_IS_NEWT_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_GRID)) +#define NMT_NEWT_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_GRID, NmtNewtGridClass)) + +struct _NmtNewtGrid { + NmtNewtContainer parent; + +}; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtNewtGridClass; + +GType nmt_newt_grid_get_type (void); + +typedef enum { + NMT_NEWT_GRID_EXPAND_X = (1 << 0), + NMT_NEWT_GRID_EXPAND_Y = (1 << 1), + NMT_NEWT_GRID_ANCHOR_LEFT = (1 << 2), + NMT_NEWT_GRID_ANCHOR_RIGHT = (1 << 3), + NMT_NEWT_GRID_FILL_X = NMT_NEWT_GRID_ANCHOR_LEFT | NMT_NEWT_GRID_ANCHOR_RIGHT, + NMT_NEWT_GRID_ANCHOR_TOP = (1 << 4), + NMT_NEWT_GRID_ANCHOR_BOTTOM = (1 << 5), + NMT_NEWT_GRID_FILL_Y = NMT_NEWT_GRID_ANCHOR_TOP | NMT_NEWT_GRID_ANCHOR_BOTTOM, +} NmtNewtGridFlags; + +NmtNewtWidget *nmt_newt_grid_new (void); + +void nmt_newt_grid_add (NmtNewtGrid *grid, + NmtNewtWidget *widget, + int x, + int y); +void nmt_newt_grid_move (NmtNewtGrid *grid, + NmtNewtWidget *widget, + int x, + int y); +void nmt_newt_grid_set_flags (NmtNewtGrid *grid, + NmtNewtWidget *widget, + NmtNewtGridFlags flags); + +G_END_DECLS + +#endif /* NMT_NEWT_GRID_H */ diff --git a/clients/tui/newt/nmt-newt-hacks.c b/clients/tui/newt/nmt-newt-hacks.c new file mode 100644 index 00000000..2d9b1725 --- /dev/null +++ b/clients/tui/newt/nmt-newt-hacks.c @@ -0,0 +1,103 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-hacks + * @short_description: Hacks! + * + * This contains hacky cheating implementations of certain newt + * functions that were added after 0.52.15. + */ + +#include "config.h" + +#include "nmt-newt-hacks.h" + +#if !defined (HAVE_NEWTCOMPONENTGETSIZE) || !defined (HAVE_NEWTENTRYGETCURSORPOSITION) +struct newtComponent_0_52_15_struct_hack { + int height, width; + int top, left; + int takesFocus; + int isMapped; + + struct componentOps *ops; + + newtCallback callback; + void *callbackData; + + newtCallback destroyCallback; + void *destroyCallbackData; + + void *data; +}; +#endif + +#ifndef HAVE_NEWTCOMPONENTGETSIZE +void +newtComponentGetSize (newtComponent component, + int *width, + int *height) +{ + struct newtComponent_0_52_15_struct_hack *hack = (void *) component; + + *width = hack->width; + *height = hack->height; +} + +void +newtComponentGetPosition (newtComponent component, + int *left, + int *top) +{ + struct newtComponent_0_52_15_struct_hack *hack = (void *) component; + + *left = hack->left; + *top = hack->top; +} +#endif + +#ifndef HAVE_NEWTENTRYGETCURSORPOSITION +struct newtEntry_0_52_15_struct_hack { + int flags; + char *buf; + const char **resultPtr; + int bufAlloced; + int bufUsed; + int cursorPosition; + /* ... */ +}; + +int +newtEntryGetCursorPosition (newtComponent component) +{ + struct newtComponent_0_52_15_struct_hack *co_hack = (void *) component; + struct newtEntry_0_52_15_struct_hack *entry_hack = co_hack->data; + + return entry_hack->cursorPosition; +} + +void +newtEntrySetCursorPosition (newtComponent component, + int position) +{ + struct newtComponent_0_52_15_struct_hack *co_hack = (void *) component; + struct newtEntry_0_52_15_struct_hack *entry_hack = co_hack->data; + + entry_hack->cursorPosition = position; +} +#endif diff --git a/clients/tui/newt/nmt-newt-hacks.h b/clients/tui/newt/nmt-newt-hacks.h new file mode 100644 index 00000000..ba8464af --- /dev/null +++ b/clients/tui/newt/nmt-newt-hacks.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_HACKS_H +#define NMT_NEWT_HACKS_H + +#include "config.h" + +#include <newt.h> + +#ifndef HAVE_NEWTCOMPONENTGETSIZE +void newtComponentGetSize (newtComponent component, + int *width, + int *height); + +void newtComponentGetPosition (newtComponent component, + int *left, + int *top); +#endif + +#ifndef HAVE_NEWTENTRYGETCURSORPOSITION +int newtEntryGetCursorPosition (newtComponent component); +void newtEntrySetCursorPosition (newtComponent component, + int position); +#endif + +#endif /* NMT_NEWT_HACKS_H */ diff --git a/clients/tui/newt/nmt-newt-label.c b/clients/tui/newt/nmt-newt-label.c new file mode 100644 index 00000000..9c4df8ed --- /dev/null +++ b/clients/tui/newt/nmt-newt-label.c @@ -0,0 +1,326 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-label + * @short_description: Labels + * + * #NmtNewtLabel implements a single-line label. + * + * See also #NmtNewtTextbox, for multiline. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-label.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtLabel, nmt_newt_label, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_LABEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_LABEL, NmtNewtLabelPrivate)) + +typedef struct { + char *text; + NmtNewtLabelStyle style; + gboolean highlight; +} NmtNewtLabelPrivate; + +enum { + PROP_0, + PROP_TEXT, + PROP_STYLE, + PROP_HIGHLIGHT, + + LAST_PROP +}; + +/** + * nmt_newt_label_new: + * @text: the initial label text + * + * Creates a new #NmtNewtLabel + * + * Returns: a new #NmtNewtLabel + */ +NmtNewtWidget * +nmt_newt_label_new (const char *text) +{ + return g_object_new (NMT_TYPE_NEWT_LABEL, + "text", text, + NULL); +} + +/** + * nmt_newt_label_set_text: + * @label: an #NmtNewtLabel + * @text: the new text + * + * Updates @label's text. + */ +void +nmt_newt_label_set_text (NmtNewtLabel *label, + const char *text) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + if (!g_strcmp0 (priv->text, text)) + return; + + g_free (priv->text); + priv->text = g_strdup (text); + + g_object_notify (G_OBJECT (label), "text"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label)); +} + +/** + * nmt_newt_label_get_text: + * @label: an #NmtNewtLabel + * + * Gets @label's text + * + * Returns: @label's text + */ +const char * +nmt_newt_label_get_text (NmtNewtLabel *label) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + return priv->text; +} + +/** + * NmtNewtLabelStyle: + * @NMT_NEWT_LABEL_NORMAL: a normal label + * @NMT_NEWT_LABEL_PLAIN: a "plain-looking" label + * + * The label style. Normal labels are blue. Plain labels are black, + * making them look more like they are text in their own right rather + * than just being a label for something else. + */ + +/** + * nmt_newt_label_set_style: + * @label: an #NmtNewtLabel + * @style: the #NmtNewtLabelStyle + * + * Sets the style of @label + */ +void +nmt_newt_label_set_style (NmtNewtLabel *label, + NmtNewtLabelStyle style) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + if (priv->style == style) + return; + + priv->style = style; + g_object_notify (G_OBJECT (label), "style"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label)); +} + +/** + * nmt_newt_label_get_style: + * @label: an #NmtNewtLabel + * + * Gets the style of @label + * + * Returns: the style of @label + */ +NmtNewtLabelStyle +nmt_newt_label_get_style (NmtNewtLabel *label) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + return priv->style; +} + +/** + * nmt_newt_label_set_highlight: + * @label: an #NmtNewtLabel + * @highlight: %TRUE if @label should be highlighted + * + * Sets whether @label is highlighted. Highlighted labels are red; + * this is generally used to highlight invalid widgets. + */ +void +nmt_newt_label_set_highlight (NmtNewtLabel *label, + gboolean highlight) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + highlight = !!highlight; + if (priv->highlight == highlight) + return; + + priv->highlight = highlight; + g_object_notify (G_OBJECT (label), "highlight"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label)); +} + +/** + * nmt_newt_label_get_highlight: + * @label: an #NmtNewtLabel + * + * Gets whether @label is highlighted. + * + * Returns: whether @label is highlighted. + */ +gboolean +nmt_newt_label_get_highlight (NmtNewtLabel *label) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + return priv->highlight; +} + +static void +nmt_newt_label_init (NmtNewtLabel *label) +{ +} + +static void +nmt_newt_label_finalize (GObject *object) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (object); + + g_free (priv->text); + + G_OBJECT_CLASS (nmt_newt_label_parent_class)->finalize (object); +} + +static newtComponent +nmt_newt_label_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (component); + newtComponent co; + char *text_lc; + + text_lc = nmt_newt_locale_from_utf8 (priv->text); + co = newtLabel (-1, -1, text_lc); + g_free (text_lc); + + if (priv->highlight) + newtLabelSetColors (co, NMT_NEWT_COLORSET_BAD_LABEL); + else if (priv->style == NMT_NEWT_LABEL_PLAIN) + newtLabelSetColors (co, NMT_NEWT_COLORSET_PLAIN_LABEL); + + return co; +} + +static void +nmt_newt_label_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtLabel *label = NMT_NEWT_LABEL (object); + + switch (prop_id) { + case PROP_TEXT: + nmt_newt_label_set_text (label, g_value_get_string (value)); + break; + case PROP_STYLE: + nmt_newt_label_set_style (label, g_value_get_int (value)); + break; + case PROP_HIGHLIGHT: + nmt_newt_label_set_highlight (label, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_label_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_TEXT: + g_value_set_string (value, priv->text); + break; + case PROP_STYLE: + g_value_set_int (value, priv->style); + break; + case PROP_HIGHLIGHT: + g_value_set_boolean (value, priv->highlight); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_label_class_init (NmtNewtLabelClass *label_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (label_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (label_class); + + g_type_class_add_private (label_class, sizeof (NmtNewtLabelPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_label_set_property; + object_class->get_property = nmt_newt_label_get_property; + object_class->finalize = nmt_newt_label_finalize; + + component_class->build_component = nmt_newt_label_build_component; + + /** + * NmtNewtLabel:text: + * + * The label's text + */ + g_object_class_install_property + (object_class, PROP_TEXT, + g_param_spec_string ("text", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtLabel:style: + * + * The label's #NmtNewtLabelStyle + */ + g_object_class_install_property + (object_class, PROP_STYLE, + g_param_spec_int ("style", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtLabel:highlight: + * + * Whether the label is highlighted. + */ + g_object_class_install_property + (object_class, PROP_HIGHLIGHT, + g_param_spec_boolean ("highlight", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-label.h b/clients/tui/newt/nmt-newt-label.h new file mode 100644 index 00000000..a4e0dcca --- /dev/null +++ b/clients/tui/newt/nmt-newt-label.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_LABEL_H +#define NMT_NEWT_LABEL_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_LABEL (nmt_newt_label_get_type ()) +#define NMT_NEWT_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabel)) +#define NMT_NEWT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_LABEL, NmtNewtLabelClass)) +#define NMT_IS_NEWT_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_LABEL)) +#define NMT_IS_NEWT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_LABEL)) +#define NMT_NEWT_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabelClass)) + +struct _NmtNewtLabel { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtLabelClass; + +GType nmt_newt_label_get_type (void); + +typedef enum { + NMT_NEWT_LABEL_NORMAL, + NMT_NEWT_LABEL_PLAIN +} NmtNewtLabelStyle; + +NmtNewtWidget *nmt_newt_label_new (const char *text); + +void nmt_newt_label_set_text (NmtNewtLabel *label, + const char *text); +const char *nmt_newt_label_get_text (NmtNewtLabel *label); + +void nmt_newt_label_set_style (NmtNewtLabel *label, + NmtNewtLabelStyle style); +NmtNewtLabelStyle nmt_newt_label_get_style (NmtNewtLabel *label); + +void nmt_newt_label_set_highlight (NmtNewtLabel *label, + gboolean highlight); +gboolean nmt_newt_label_get_highlight (NmtNewtLabel *label); + +G_END_DECLS + +#endif /* NMT_NEWT_LABEL_H */ diff --git a/clients/tui/newt/nmt-newt-listbox.c b/clients/tui/newt/nmt-newt-listbox.c new file mode 100644 index 00000000..e450df91 --- /dev/null +++ b/clients/tui/newt/nmt-newt-listbox.c @@ -0,0 +1,548 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-listbox + * @short_description: Single-choice listboxes + * + * #NmtNewtListbox implements a single-choice listbox. + * + * A listbox has some number of rows, each associated with an + * arbitrary pointer value. The pointer values do not need to be + * unique, but some APIs will not be usable if they aren't. You + * can also cause rows with %NULL keys to be treated specially. + * + * The listbox will emit #NmtNewtWidget::activate when the user + * presses Return on a selection. + */ + +#include "config.h" + +#include "nmt-newt-listbox.h" +#include "nmt-newt-form.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtListbox, nmt_newt_listbox, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_LISTBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxPrivate)) + +typedef struct { + int height, alloc_height, width; + gboolean fixed_height; + NmtNewtListboxFlags flags; + + GPtrArray *entries; + GPtrArray *keys; + + int active; + gpointer active_key; + gboolean skip_null_keys; + +} NmtNewtListboxPrivate; + +enum { + PROP_0, + PROP_HEIGHT, + PROP_FLAGS, + PROP_ACTIVE, + PROP_ACTIVE_KEY, + PROP_SKIP_NULL_KEYS, + + LAST_PROP +}; + +/** + * NmtNewtListboxFlags: + * @NMT_NEWT_LISTBOX_SCROLL: the listbox should have a scroll bar. + * @NMT_NEWT_LISTBOX_BORDER: the listbox should have a border around it. + * + * Flags describing an #NmtNewtListbox + */ + +/** + * nmt_newt_listbox_new: + * @height: the height of the listbox, or -1 for no fixed height + * @flags: the listbox flags + * + * Creates a new #NmtNewtListbox + * + * Returns: a new #NmtNewtListbox + */ +NmtNewtWidget * +nmt_newt_listbox_new (int height, + NmtNewtListboxFlags flags) +{ + return g_object_new (NMT_TYPE_NEWT_LISTBOX, + "height", height, + "flags", flags, + NULL); +} + +/** + * nmt_newt_listbox_append: + * @listbox: an #NmtNewtListbox + * @entry: the text for the new row + * @key: (allow-none): the key associated with @entry + * + * Adds a row to @listbox. + */ +void +nmt_newt_listbox_append (NmtNewtListbox *listbox, + const char *entry, + gpointer key) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + g_ptr_array_add (priv->entries, nmt_newt_locale_from_utf8 (entry)); + g_ptr_array_add (priv->keys, key); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (listbox)); +} + +/** + * nmt_newt_listbox_clear: + * @listbox: an #NmtNewtListbox + * + * Clears the contents of @listbox. + */ +void +nmt_newt_listbox_clear (NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + g_ptr_array_set_size (priv->entries, 0); + g_ptr_array_set_size (priv->keys, 0); + + priv->active = -1; + priv->active_key = NULL; + + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (listbox)); +} + +/** + * nmt_newt_listbox_set_active: + * @listbox: an #NmtNewtListbox + * @active: the row to make active + * + * Sets @active to be the currently-selected row in @listbox, + * scrolling it into view if needed. + */ +void +nmt_newt_listbox_set_active (NmtNewtListbox *listbox, + int active) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + if (active == priv->active) + return; + + g_return_if_fail (active >= 0 && active < priv->entries->len); + g_return_if_fail (!priv->skip_null_keys || priv->keys->pdata[active]); + + priv->active = active; + priv->active_key = priv->keys->pdata[active]; + + g_object_notify (G_OBJECT (listbox), "active"); + g_object_notify (G_OBJECT (listbox), "active-key"); +} + +/** + * nmt_newt_listbox_set_active_key: + * @listbox: an #NmtNewtListbox + * @active_key: the key for the row to make active + * + * Selects the (first) row in @listbox with @active_key as its key, + * scrolling it into view if needed. + */ +void +nmt_newt_listbox_set_active_key (NmtNewtListbox *listbox, + gpointer active_key) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + int i; + + if (active_key == priv->active_key) + return; + + g_return_if_fail (!priv->skip_null_keys || active_key); + + for (i = 0; i < priv->keys->len; i++) { + if (priv->keys->pdata[i] == active_key) { + priv->active = i; + priv->active_key = active_key; + + g_object_notify (G_OBJECT (listbox), "active"); + g_object_notify (G_OBJECT (listbox), "active-key"); + return; + } + } +} + +/** + * nmt_newt_listbox_get_active: + * @listbox: an #NmtNewtListbox + * + * Gets the currently-selected row in @listbox. + * + * Returns: the currently-selected row in @listbox. + */ +int +nmt_newt_listbox_get_active (NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + return priv->active; +} + +/** + * nmt_newt_listbox_get_active_key: + * @listbox: an #NmtNewtListbox + * + * Gets the key of the currently-selected row in @listbox. + * + * Returns: the key of the currently-selected row in @listbox. + */ +gpointer +nmt_newt_listbox_get_active_key (NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + return priv->active_key; +} + +/** + * nmt_newt_listbox_set_height: + * @listbox: an #NmtNewtListbox + * @height: the new height, or -1 for no fixed heigh + * + * Updates @listbox's height. + */ +void +nmt_newt_listbox_set_height (NmtNewtListbox *listbox, + int height) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + priv->height = height; + priv->fixed_height = priv->height != 0; + g_object_notify (G_OBJECT (listbox), "height"); +} + +static void +nmt_newt_listbox_init (NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + priv->entries = g_ptr_array_new_with_free_func (g_free); + priv->keys = g_ptr_array_new (); + + priv->active = -1; +} + +static void +nmt_newt_listbox_finalize (GObject *object) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object); + + g_ptr_array_unref (priv->entries); + g_ptr_array_unref (priv->keys); + + G_OBJECT_CLASS (nmt_newt_listbox_parent_class)->finalize (object); +} + +static void +nmt_newt_listbox_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (widget); + + NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)-> + size_request (widget, width, height); + + priv->alloc_height = -1; + if (!priv->fixed_height) + *height = 1; + priv->width = *width; +} + +static void +nmt_newt_listbox_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (widget); + + if (width > priv->width) { + newtListboxSetWidth (nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget)), + width); + } + + NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)-> + size_allocate (widget, x, y, width, height); + + priv->alloc_height = height; + + if (!priv->fixed_height && height != priv->height) { + priv->height = height; + nmt_newt_widget_needs_rebuild (widget); + } +} + +static void +update_active_internal (NmtNewtListbox *listbox, + int new_active) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + if (priv->active == new_active) + return; + if (new_active >= priv->keys->len) + return; + + if (priv->skip_null_keys && !priv->keys->pdata[new_active]) { + if (new_active > priv->active) { + while ( new_active < priv->entries->len + && !priv->keys->pdata[new_active]) + new_active++; + } else { + while ( new_active >= 0 + && !priv->keys->pdata[new_active]) + new_active--; + } + + if ( new_active < 0 + || new_active >= priv->entries->len + || !priv->keys->pdata[new_active]) { + g_assert (priv->active >= 0 && priv->active < priv->entries->len); + return; + } + } + + nmt_newt_listbox_set_active (listbox, new_active); +} + +static void +selection_changed_callback (newtComponent co, + void *user_data) +{ + NmtNewtListbox *listbox = user_data; + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + int new_active; + + new_active = GPOINTER_TO_UINT (newtListboxGetCurrent (co)); + update_active_internal (listbox, new_active); + + if (priv->active != new_active) + newtListboxSetCurrent (co, priv->active); +} + +static guint +convert_flags (NmtNewtListboxFlags flags) +{ + guint newt_flags = NEWT_FLAG_RETURNEXIT; + + if (flags & NMT_NEWT_LISTBOX_SCROLL) + newt_flags |= NEWT_FLAG_SCROLL; + if (flags & NMT_NEWT_LISTBOX_BORDER) + newt_flags |= NEWT_FLAG_BORDER; + + return newt_flags; +} + +static newtComponent +nmt_newt_listbox_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (component); + newtComponent co; + int i, active; + + if (priv->active == -1) + update_active_internal (NMT_NEWT_LISTBOX (component), 0); + active = priv->active; + + co = newtListbox (-1, -1, priv->height, convert_flags (priv->flags)); + newtComponentAddCallback (co, selection_changed_callback, component); + + for (i = 0; i < priv->entries->len; i++) { + newtListboxAppendEntry (co, priv->entries->pdata[i], GUINT_TO_POINTER (i)); + if (active == -1 && priv->keys->pdata[i] == priv->active_key) + active = i; + } + + if (active != -1) + newtListboxSetCurrent (co, active); + + return co; +} + +static void +nmt_newt_listbox_activated (NmtNewtWidget *widget) +{ + NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (widget); + newtComponent co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget)); + + nmt_newt_listbox_set_active (listbox, GPOINTER_TO_UINT (newtListboxGetCurrent (co))); + + NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)->activated (widget); +} + +static void +nmt_newt_listbox_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (object); + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_HEIGHT: + priv->height = g_value_get_int (value); + priv->fixed_height = (priv->height != 0); + break; + case PROP_FLAGS: + priv->flags = g_value_get_uint (value); + break; + case PROP_ACTIVE: + nmt_newt_listbox_set_active (listbox, g_value_get_int (value)); + break; + case PROP_ACTIVE_KEY: + nmt_newt_listbox_set_active_key (listbox, g_value_get_pointer (value)); + break; + case PROP_SKIP_NULL_KEYS: + priv->skip_null_keys = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_listbox_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_HEIGHT: + g_value_set_int (value, priv->height); + break; + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_ACTIVE: + g_value_set_int (value, priv->active); + break; + case PROP_ACTIVE_KEY: + g_value_set_pointer (value, priv->active_key); + break; + case PROP_SKIP_NULL_KEYS: + g_value_set_boolean (value, priv->skip_null_keys); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_listbox_class_init (NmtNewtListboxClass *listbox_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (listbox_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (listbox_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (listbox_class); + + g_type_class_add_private (listbox_class, sizeof (NmtNewtListboxPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_listbox_set_property; + object_class->get_property = nmt_newt_listbox_get_property; + object_class->finalize = nmt_newt_listbox_finalize; + + widget_class->size_request = nmt_newt_listbox_size_request; + widget_class->size_allocate = nmt_newt_listbox_size_allocate; + widget_class->activated = nmt_newt_listbox_activated; + + component_class->build_component = nmt_newt_listbox_build_component; + + /* properties */ + + /** + * NmtNewtListbox:height: + * + * The listbox's height, or -1 if it has no fixed height. + */ + g_object_class_install_property + (object_class, PROP_HEIGHT, + g_param_spec_int ("height", "", "", + -1, 255, -1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtListbox:flags: + * + * The listbox's #NmtNewtListboxFlags. + */ + g_object_class_install_property + (object_class, PROP_FLAGS, + g_param_spec_uint ("flags", "", "", + 0, 0xFFFF, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtListbox:active: + * + * The currently-selected row. + */ + g_object_class_install_property + (object_class, PROP_ACTIVE, + g_param_spec_int ("active", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtListbox:active-key: + * + * The key of the currently-selected row. + */ + g_object_class_install_property + (object_class, PROP_ACTIVE_KEY, + g_param_spec_pointer ("active-key", "", "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtListbox:skip-null-keys: + * + * If %TRUE, rows with %NULL key values will be skipped over when + * navigating the list with the arrow keys. + */ + g_object_class_install_property + (object_class, PROP_SKIP_NULL_KEYS, + g_param_spec_boolean ("skip-null-keys", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-listbox.h b/clients/tui/newt/nmt-newt-listbox.h new file mode 100644 index 00000000..c18c9f8e --- /dev/null +++ b/clients/tui/newt/nmt-newt-listbox.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_LISTBOX_H +#define NMT_NEWT_LISTBOX_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_LISTBOX (nmt_newt_listbox_get_type ()) +#define NMT_NEWT_LISTBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListbox)) +#define NMT_NEWT_LISTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass)) +#define NMT_IS_NEWT_LISTBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_LISTBOX)) +#define NMT_IS_NEWT_LISTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_LISTBOX)) +#define NMT_NEWT_LISTBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass)) + +struct _NmtNewtListbox { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtListboxClass; + +GType nmt_newt_listbox_get_type (void); + +typedef enum { + NMT_NEWT_LISTBOX_SCROLL = (1 << 0), + NMT_NEWT_LISTBOX_BORDER = (1 << 1) +} NmtNewtListboxFlags; + +NmtNewtWidget *nmt_newt_listbox_new (int height, + NmtNewtListboxFlags flags); + +void nmt_newt_listbox_set_height (NmtNewtListbox *listbox, + int height); + +void nmt_newt_listbox_append (NmtNewtListbox *listbox, + const char *entry, + gpointer key); +void nmt_newt_listbox_clear (NmtNewtListbox *listbox); + +void nmt_newt_listbox_set_active (NmtNewtListbox *listbox, + int active); +void nmt_newt_listbox_set_active_key (NmtNewtListbox *listbox, + gpointer active_key); + +int nmt_newt_listbox_get_active (NmtNewtListbox *listbox); +gpointer nmt_newt_listbox_get_active_key (NmtNewtListbox *listbox); + +G_END_DECLS + +#endif /* NMT_NEWT_LISTBOX_H */ diff --git a/clients/tui/newt/nmt-newt-popup.c b/clients/tui/newt/nmt-newt-popup.c new file mode 100644 index 00000000..b604eaaa --- /dev/null +++ b/clients/tui/newt/nmt-newt-popup.c @@ -0,0 +1,360 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-popup + * @short_description: Pop-up menus + * + * #NmtNewtPopup implements a pop-up menu. When inactive, they appear + * the same as #NmtNewtButtons, displaying the label from the + * #NmtNewtPopup:active entry. When activated, they pop up a temporary + * #NmtNewtForm containing an #NmtNewtListbox to select from. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-newt-popup.h" +#include "nmt-newt-form.h" +#include "nmt-newt-hacks.h" +#include "nmt-newt-listbox.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtPopup, nmt_newt_popup, NMT_TYPE_NEWT_BUTTON) + +#define NMT_NEWT_POPUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_POPUP, NmtNewtPopupPrivate)) + +typedef struct { + GArray *entries; + int active; +} NmtNewtPopupPrivate; + +enum { + PROP_0, + PROP_ACTIVE, + PROP_ACTIVE_ID, + + LAST_PROP +}; + +/** + * NmtNewtPopupEntry: + * @label: the user-visible label for the entry + * @id: the internal ID of the entry + * + * A single entry in a pop-up menu. + */ + +/** + * nmt_newt_popup_new: + * @entries: an array of #NmtNewtPopupEntry, terminated by an + * entry with a %NULL label + * + * Creates a new #NmtNewtPopup with the given entries. + * + * Returns: a new #NmtNewtPopup + */ +NmtNewtWidget * +nmt_newt_popup_new (NmtNewtPopupEntry *entries) +{ + NmtNewtWidget *widget; + NmtNewtPopupPrivate *priv; + int i; + + widget = g_object_new (NMT_TYPE_NEWT_POPUP, NULL); + priv = NMT_NEWT_POPUP_GET_PRIVATE (widget); + + for (i = 0; entries[i].label; i++) { + NmtNewtPopupEntry entry; + + entry.label = nmt_newt_locale_from_utf8 (_(entries[i].label)); + entry.id = g_strdup (entries[i].id); + g_array_append_val (priv->entries, entry); + } + + return widget; +} + +static void +popup_entry_clear_func (NmtNewtPopupEntry *entry) +{ + g_free (entry->label); + g_free (entry->id); +} + +static void +nmt_newt_popup_init (NmtNewtPopup *popup) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup); + + priv->entries = g_array_sized_new (FALSE, FALSE, sizeof (NmtNewtPopupEntry), 10); + g_array_set_clear_func (priv->entries, (GDestroyNotify) popup_entry_clear_func); +} + +static void +nmt_newt_popup_finalize (GObject *object) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (object); + + g_array_unref (priv->entries); + + G_OBJECT_CLASS (nmt_newt_popup_parent_class)->finalize (object); +} + +static newtComponent +nmt_newt_popup_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (component); + NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data; + + nmt_newt_button_set_label (NMT_NEWT_BUTTON (component), + entries[priv->active].label); + return NMT_NEWT_COMPONENT_CLASS (nmt_newt_popup_parent_class)-> + build_component (component, sensitive); +} + +static void +nmt_newt_popup_activated (NmtNewtWidget *widget) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (widget); + NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data; + NmtNewtForm *form; + NmtNewtWidget *listbox, *ret; + int button_x, button_y; + int window_x, window_y; + int list_w, list_h; + int i, active; + + listbox = nmt_newt_listbox_new (priv->entries->len, 0); + nmt_newt_widget_set_exit_on_activate (listbox, TRUE); + for (i = 0; i < priv->entries->len; i++) + nmt_newt_listbox_append (NMT_NEWT_LISTBOX (listbox), entries[i].label, NULL); + nmt_newt_listbox_set_active (NMT_NEWT_LISTBOX (listbox), priv->active); + nmt_newt_widget_set_padding (listbox, 1, 0, 1, 0); + + nmt_newt_widget_size_request (listbox, &list_w, &list_h); + + g_object_get (nmt_newt_widget_get_form (widget), + "x", &window_x, + "y", &window_y, + NULL); + newtComponentGetPosition (nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget)), + &button_x, &button_y); + /* (window_x + button_x) is the screen X coordinate of the newtComponent. A + * newtButton labelled "Foo" is rendered as " <Foo>" (with a preceding + * space), so the "F" is at (window_x + button_x + 2). We've added 1 column + * of padding to the left of the listbox, so we need to position the popup + * at (window_x + button_x + 1) in order for its text to be aligned with the + * button's text. (The x and y coordinates given to NmtNewtForm are the + * coordinates of the top left of the window content, ignoring the border + * graphics.) + */ + window_x += button_x + 1; + window_y += button_y - priv->active; + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "x", window_x, + "y", window_y, + "width", list_w, + "height", list_h, + "padding", 0, + "escape-exits", TRUE, + NULL); + nmt_newt_form_set_content (form, listbox); + + ret = nmt_newt_form_run_sync (form); + if (ret == listbox) + active = nmt_newt_listbox_get_active (NMT_NEWT_LISTBOX (listbox)); + else + active = priv->active; + + g_object_unref (form); + + if (active != priv->active) { + priv->active = active; + g_object_notify (G_OBJECT (widget), "active"); + g_object_notify (G_OBJECT (widget), "active-id"); + nmt_newt_widget_needs_rebuild (widget); + } + + NMT_NEWT_WIDGET_CLASS (nmt_newt_popup_parent_class)->activated (widget); +} + +/** + * nmt_newt_popup_get_active: + * @popup: a #NmtNewtPopup + * + * Gets the index of the active entry in @popup. + * + * Returns: the index of the active entry in @popup. + */ +int +nmt_newt_popup_get_active (NmtNewtPopup *popup) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup); + + return priv->active; +} + +/** + * nmt_newt_popup_set_active: + * @popup: a #NmtNewtPopup + * @active: the index of the new active entry + * + * Sets the active entry in @popup. + */ +void +nmt_newt_popup_set_active (NmtNewtPopup *popup, + int active) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup); + + active = CLAMP (active, 0, priv->entries->len - 1); + + if (active != priv->active) { + priv->active = active; + g_object_notify (G_OBJECT (popup), "active"); + g_object_notify (G_OBJECT (popup), "active-id"); + } +} + +/** + * nmt_newt_popup_get_active_id: + * @popup: a #NmtNewtPopup + * + * Gets the textual ID of the active entry in @popup. + * + * Returns: the ID of the active entry in @popup. + */ +const char * +nmt_newt_popup_get_active_id (NmtNewtPopup *popup) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup); + NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data; + + return entries[priv->active].id; +} + +/** + * nmt_newt_popup_set_active_id: + * @popup: a #NmtNewtPopup + * @active_id: the ID of the new active entry + * + * Sets the active entry in @popup. + */ +void +nmt_newt_popup_set_active_id (NmtNewtPopup *popup, + const char *active_id) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup); + NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data; + int i; + + for (i = 0; i < priv->entries->len; i++) { + if (!g_strcmp0 (active_id, entries[i].id)) { + nmt_newt_popup_set_active (popup, i); + return; + } + } +} + +static void +nmt_newt_popup_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtPopup *popup = NMT_NEWT_POPUP (object); + + switch (prop_id) { + case PROP_ACTIVE: + nmt_newt_popup_set_active (popup, g_value_get_uint (value)); + break; + case PROP_ACTIVE_ID: + nmt_newt_popup_set_active_id (popup, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_popup_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtPopup *popup = NMT_NEWT_POPUP (object); + + switch (prop_id) { + case PROP_ACTIVE: + g_value_set_uint (value, nmt_newt_popup_get_active (popup)); + break; + case PROP_ACTIVE_ID: + g_value_set_string (value, nmt_newt_popup_get_active_id (popup)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_popup_class_init (NmtNewtPopupClass *popup_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (popup_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (popup_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (popup_class); + + g_type_class_add_private (popup_class, sizeof (NmtNewtPopupPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_popup_set_property; + object_class->get_property = nmt_newt_popup_get_property; + object_class->finalize = nmt_newt_popup_finalize; + + widget_class->activated = nmt_newt_popup_activated; + + component_class->build_component = nmt_newt_popup_build_component; + + /** + * NmtNewtPopup:active: + * + * The index of the currently-active entry. + */ + g_object_class_install_property + (object_class, PROP_ACTIVE, + g_param_spec_uint ("active", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtPopup:active-id: + * + * The textual ID of the currently-active entry. + */ + g_object_class_install_property + (object_class, PROP_ACTIVE_ID, + g_param_spec_string ("active-id", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-popup.h b/clients/tui/newt/nmt-newt-popup.h new file mode 100644 index 00000000..643ea2d8 --- /dev/null +++ b/clients/tui/newt/nmt-newt-popup.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_POPUP_H +#define NMT_NEWT_POPUP_H + +#include "nmt-newt-button.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_POPUP (nmt_newt_popup_get_type ()) +#define NMT_NEWT_POPUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopup)) +#define NMT_NEWT_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_POPUP, NmtNewtPopupClass)) +#define NMT_IS_NEWT_POPUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_POPUP)) +#define NMT_IS_NEWT_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_POPUP)) +#define NMT_NEWT_POPUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopupClass)) + +struct _NmtNewtPopup { + NmtNewtButton parent; + +}; + +typedef struct { + NmtNewtButtonClass parent; + +} NmtNewtPopupClass; + +GType nmt_newt_popup_get_type (void); + +typedef struct { + char *label; + char *id; +} NmtNewtPopupEntry; + +NmtNewtWidget *nmt_newt_popup_new (NmtNewtPopupEntry *entries); + +int nmt_newt_popup_get_active (NmtNewtPopup *popup); +void nmt_newt_popup_set_active (NmtNewtPopup *popup, + int active); + +const char *nmt_newt_popup_get_active_id (NmtNewtPopup *popup); +void nmt_newt_popup_set_active_id (NmtNewtPopup *popup, + const char *active_id); + +G_END_DECLS + +#endif /* NMT_NEWT_POPUP_H */ diff --git a/clients/tui/newt/nmt-newt-section.c b/clients/tui/newt/nmt-newt-section.c new file mode 100644 index 00000000..1a5035fc --- /dev/null +++ b/clients/tui/newt/nmt-newt-section.c @@ -0,0 +1,447 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-section + * @short_description: A collapsible section + * + * #NmtNewtSection is a container with two children; the header and + * the body. The header is always visible, but the body is only + * visible when the container is #NmtNewtSection:open. + * + * Note that there is no default way to open and close an + * #NmtNewtSection. You need to implement this yourself. (Eg, by + * binding the #NmtToggleButton:active property of an #NmtToggleButton + * in the section's header to the section's #NmtNewtSection:open + * property.) + * + * In addition to the header and body, the #NmtNewtSection also + * optionally draws a border along the left side, indicating the + * extent of the section. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-section.h" +#include "nmt-newt-grid.h" +#include "nmt-newt-label.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtSection, nmt_newt_section, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_SECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_SECTION, NmtNewtSectionPrivate)) + +typedef struct { + NmtNewtWidget *header; + int hheight_req, hwidth_req; + + NmtNewtWidget *body; + int bheight_req, bwidth_req; + + gboolean show_border; + NmtNewtWidget *border_grid; + NmtNewtWidget *border_open_label; + NmtNewtWidget *border_closed_label; + NmtNewtWidget *border_end_label; + GPtrArray *border_line_labels; + + gboolean open; +} NmtNewtSectionPrivate; + +static char *closed_glyph, *open_glyph, *line_glyph, *end_glyph; + +enum { + PROP_0, + + PROP_SHOW_BORDER, + PROP_OPEN, + + LAST_PROP +}; + +/** + * nmt_newt_section_new: + * @show_border: whether to show the border on the side of the section + * + * Creates a new #NmtNewtSection + * + * Returns: a new #NmtNewtSection + */ +NmtNewtWidget * +nmt_newt_section_new (gboolean show_border) +{ + return g_object_new (NMT_TYPE_NEWT_SECTION, + "show-border", show_border, + NULL); +} + +static void +nmt_newt_section_init (NmtNewtSection *section) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class); + + priv->show_border = TRUE; + + priv->border_grid = nmt_newt_grid_new (); + parent_class->add (NMT_NEWT_CONTAINER (section), priv->border_grid); + + priv->border_open_label = nmt_newt_label_new (open_glyph); + nmt_newt_widget_set_visible (priv->border_open_label, FALSE); + nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_open_label, 0, 0); + + priv->border_closed_label = nmt_newt_label_new (closed_glyph); + nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_closed_label, 0, 0); + + priv->border_end_label = nmt_newt_label_new (end_glyph); + nmt_newt_widget_set_visible (priv->border_open_label, FALSE); + nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_end_label, 0, 1); + + priv->border_line_labels = g_ptr_array_new (); +} + +static void +nmt_newt_section_finalize (GObject *object) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object); + + g_ptr_array_unref (priv->border_line_labels); + + G_OBJECT_CLASS (nmt_newt_section_parent_class)->finalize (object); +} + +/** + * nmt_newt_section_set_header: + * @section: an #NmtNewtSection + * @header: the header widget + * + * Sets @section's header widget. + */ +void +nmt_newt_section_set_header (NmtNewtSection *section, + NmtNewtWidget *header) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class); + NmtNewtContainer *container = NMT_NEWT_CONTAINER (section); + + if (priv->header) + parent_class->remove (container, priv->header); + priv->header = header; + parent_class->add (container, header); +} + +/** + * nmt_newt_section_get_header: + * @section: an #NmtNewtSection + * + * Gets @section's header widget. + * + * Returns: @section's header widget. + */ +NmtNewtWidget * +nmt_newt_section_get_header (NmtNewtSection *section) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + + return priv->header; +} + +/** + * nmt_newt_section_set_body: + * @section: an #NmtNewtSection + * @body: the body widget + * + * Sets @section's body widget. + */ +void +nmt_newt_section_set_body (NmtNewtSection *section, + NmtNewtWidget *body) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class); + NmtNewtContainer *container = NMT_NEWT_CONTAINER (section); + + if (priv->body) + parent_class->remove (container, priv->body); + priv->body = body; + parent_class->add (container, body); +} + +/** + * nmt_newt_section_get_body: + * @section: an #NmtNewtSection + * + * Gets @section's body widget. + * + * Returns: @section's body widget. + */ +NmtNewtWidget * +nmt_newt_section_get_body (NmtNewtSection *section) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + + return priv->body; +} + +static void +nmt_newt_section_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtSection *section = NMT_NEWT_SECTION (container); + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class); + + if (widget == priv->header) + priv->header = NULL; + else if (widget == priv->body) + priv->body = NULL; + else if (widget == priv->border_grid) + priv->border_grid = NULL; + + parent_class->remove (container, widget); +} + +static newtComponent * +nmt_newt_section_get_components (NmtNewtWidget *widget) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget); + newtComponent *child_cos; + GPtrArray *cos; + int i; + + g_return_val_if_fail (priv->header != NULL && priv->body != NULL, NULL); + + cos = g_ptr_array_new (); + + if (priv->show_border) { + child_cos = nmt_newt_widget_get_components (priv->border_grid); + for (i = 0; child_cos[i]; i++) + g_ptr_array_add (cos, child_cos[i]); + g_free (child_cos); + } + + child_cos = nmt_newt_widget_get_components (priv->header); + for (i = 0; child_cos[i]; i++) + g_ptr_array_add (cos, child_cos[i]); + g_free (child_cos); + + if (priv->open) { + child_cos = nmt_newt_widget_get_components (priv->body); + for (i = 0; child_cos[i]; i++) + g_ptr_array_add (cos, child_cos[i]); + g_free (child_cos); + } + + g_ptr_array_add (cos, NULL); + return (newtComponent *) g_ptr_array_free (cos, FALSE); +} + +static void +nmt_newt_section_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget); + int w_ignore, h_ignore; + + g_return_if_fail (priv->header != NULL && priv->body != NULL); + + if (priv->show_border) + nmt_newt_widget_size_request (priv->border_grid, &w_ignore, &h_ignore); + nmt_newt_widget_size_request (priv->header, &priv->hwidth_req, &priv->hheight_req); + nmt_newt_widget_size_request (priv->body, &priv->bwidth_req, &priv->bheight_req); + + *width = MAX (priv->hwidth_req, priv->bwidth_req) + 2; + if (priv->open) + *height = priv->hheight_req + priv->bheight_req + (priv->show_border ? 1 : 0); + else + *height = priv->hheight_req; +} + +static void +adjust_border_for_allocation (NmtNewtSectionPrivate *priv, + int height) +{ + int i; + + /* We have to use a series of one-line labels rather than a multi-line + * textbox, because newt will hide any component that's partially offscreen, + * but we want the on-screen portion of the border to show even if part of + * it is offscreen. + */ + + if (height == 1) { + nmt_newt_widget_set_visible (priv->border_closed_label, TRUE); + nmt_newt_widget_set_visible (priv->border_open_label, FALSE); + for (i = 0; i < priv->border_line_labels->len; i++) + nmt_newt_widget_set_visible (priv->border_line_labels->pdata[i], FALSE); + nmt_newt_widget_set_visible (priv->border_end_label, FALSE); + } else { + nmt_newt_widget_set_visible (priv->border_closed_label, FALSE); + nmt_newt_widget_set_visible (priv->border_open_label, TRUE); + for (i = 0; i < height - 2; i++) { + if (i >= priv->border_line_labels->len) { + NmtNewtWidget *label; + + label = nmt_newt_label_new (line_glyph); + g_ptr_array_add (priv->border_line_labels, label); + nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), label, 0, i + 1); + } else + nmt_newt_widget_set_visible (priv->border_line_labels->pdata[i], TRUE); + } + nmt_newt_widget_set_visible (priv->border_end_label, TRUE); + nmt_newt_grid_move (NMT_NEWT_GRID (priv->border_grid), priv->border_end_label, 0, height - 1); + } +} + +static void +nmt_newt_section_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget); + + if (priv->show_border) { + int w_ignore, h_ignore; + + adjust_border_for_allocation (priv, height); + nmt_newt_widget_size_request (priv->border_grid, &w_ignore, &h_ignore); + nmt_newt_widget_size_allocate (priv->border_grid, x, y, 1, height); + nmt_newt_widget_size_allocate (priv->header, x + 2, y, width, priv->hheight_req); + } else + nmt_newt_widget_size_allocate (priv->header, x, y, width, priv->hheight_req); + + if (priv->open) { + nmt_newt_widget_size_allocate (priv->body, x + 2, y + priv->hheight_req, + width, height - priv->hheight_req); + } +} + +static void +nmt_newt_section_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SHOW_BORDER: + priv->show_border = g_value_get_boolean (value); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (object)); + break; + case PROP_OPEN: + priv->open = g_value_get_boolean (value); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (object)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_section_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SHOW_BORDER: + g_value_set_boolean (value, priv->show_border); + break; + case PROP_OPEN: + g_value_set_boolean (value, priv->open); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_section_class_init (NmtNewtSectionClass *section_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (section_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (section_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (section_class); + + g_type_class_add_private (section_class, sizeof (NmtNewtSectionPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_section_set_property; + object_class->get_property = nmt_newt_section_get_property; + object_class->finalize = nmt_newt_section_finalize; + + widget_class->get_components = nmt_newt_section_get_components; + widget_class->size_request = nmt_newt_section_size_request; + widget_class->size_allocate = nmt_newt_section_size_allocate; + + container_class->remove = nmt_newt_section_remove; + + /* properties */ + + /** + * NmtNewtSection:show-border: + * + * %TRUE if the section should show a border along the left side. + */ + g_object_class_install_property + (object_class, PROP_SHOW_BORDER, + g_param_spec_boolean ("show-border", "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtNewtSection:open: + * + * %TRUE if the section is open (ie, its body is visible), %FALSE + * if not. + */ + g_object_class_install_property + (object_class, PROP_OPEN, + g_param_spec_boolean ("open", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /* globals */ + closed_glyph = nmt_newt_locale_from_utf8 ("\342\225\220"); /* ═ */ + open_glyph = nmt_newt_locale_from_utf8 ("\342\225\244"); /* ╤ */ + line_glyph = nmt_newt_locale_from_utf8 ("\342\224\202"); /* │ */ + end_glyph = nmt_newt_locale_from_utf8 ("\342\224\224"); /* └ */ + if (!*closed_glyph || !*open_glyph || !*line_glyph || !*end_glyph) { + g_free (closed_glyph); + g_free (open_glyph); + g_free (line_glyph); + g_free (end_glyph); + + closed_glyph = g_strdup ("-"); + open_glyph = g_strdup ("+"); + line_glyph = g_strdup ("|"); + end_glyph = g_strdup ("\\"); + } +} diff --git a/clients/tui/newt/nmt-newt-section.h b/clients/tui/newt/nmt-newt-section.h new file mode 100644 index 00000000..6bf59ebb --- /dev/null +++ b/clients/tui/newt/nmt-newt-section.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_SECTION_H +#define NMT_NEWT_SECTION_H + +#include "nmt-newt-container.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_SECTION (nmt_newt_section_get_type ()) +#define NMT_NEWT_SECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSection)) +#define NMT_NEWT_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass)) +#define NMT_IS_NEWT_SECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_SECTION)) +#define NMT_IS_NEWT_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_SECTION)) +#define NMT_NEWT_SECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass)) + +struct _NmtNewtSection { + NmtNewtContainer parent; + +}; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtNewtSectionClass; + +GType nmt_newt_section_get_type (void); + +NmtNewtWidget *nmt_newt_section_new (gboolean show_border); + +void nmt_newt_section_set_header (NmtNewtSection *section, + NmtNewtWidget *header); +NmtNewtWidget *nmt_newt_section_get_header (NmtNewtSection *section); + +void nmt_newt_section_set_body (NmtNewtSection *section, + NmtNewtWidget *body); +NmtNewtWidget *nmt_newt_section_get_body (NmtNewtSection *section); + +G_END_DECLS + +#endif /* NMT_NEWT_SECTION_H */ diff --git a/clients/tui/newt/nmt-newt-separator.c b/clients/tui/newt/nmt-newt-separator.c new file mode 100644 index 00000000..07deb1af --- /dev/null +++ b/clients/tui/newt/nmt-newt-separator.c @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-separator + * @short_description: Separator + * + * #NmtNewtSeparator is just a blank label, which is used in a few places + * where a widget is needed but none is desired, or to add blank space + * between widgets in containers that don't implement padding. + */ + +#include "config.h" + +#include "nmt-newt-separator.h" + +G_DEFINE_TYPE (NmtNewtSeparator, nmt_newt_separator, NMT_TYPE_NEWT_COMPONENT) + +/** + * nmt_newt_separator_new: + * + * Creates a new #NmtNewtSeparator. + * + * Returns: a new #NmtNewtSeparator + */ +NmtNewtWidget * +nmt_newt_separator_new (void) +{ + return g_object_new (NMT_TYPE_NEWT_SEPARATOR, NULL); +} + +static void +nmt_newt_separator_init (NmtNewtSeparator *separator) +{ +} + +static newtComponent +nmt_newt_separator_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + return newtLabel (-1, -1, " "); +} + +static void +nmt_newt_separator_class_init (NmtNewtSeparatorClass *separator_class) +{ + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (separator_class); + + /* virtual methods */ + component_class->build_component = nmt_newt_separator_build_component; +} diff --git a/clients/tui/newt/nmt-newt-separator.h b/clients/tui/newt/nmt-newt-separator.h new file mode 100644 index 00000000..3f4183d5 --- /dev/null +++ b/clients/tui/newt/nmt-newt-separator.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_SEPARATOR_H +#define NMT_NEWT_SEPARATOR_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_SEPARATOR (nmt_newt_separator_get_type ()) +#define NMT_NEWT_SEPARATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparator)) +#define NMT_NEWT_SEPARATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparatorClass)) +#define NMT_IS_NEWT_SEPARATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_SEPARATOR)) +#define NMT_IS_NEWT_SEPARATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_SEPARATOR)) +#define NMT_NEWT_SEPARATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparatorClass)) + +struct _NmtNewtSeparator { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtSeparatorClass; + +GType nmt_newt_separator_get_type (void); + +NmtNewtWidget *nmt_newt_separator_new (void); + +G_END_DECLS + +#endif /* NMT_NEWT_SEPARATOR_H */ diff --git a/clients/tui/newt/nmt-newt-stack.c b/clients/tui/newt/nmt-newt-stack.c new file mode 100644 index 00000000..d94bdebf --- /dev/null +++ b/clients/tui/newt/nmt-newt-stack.c @@ -0,0 +1,368 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-stack + * @short_description: A stack of alternative widgets + * + * #NmtNewtStack implements a stack of widgets, only one of which is + * visible at any time. + * + * The height and width of the widget is determined only by its + * visible child. Likewise, the widget's #NmtNewtWidget:valid is + * determined only by the validity of its visible child, not its other + * children. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-stack.h" + +G_DEFINE_TYPE (NmtNewtStack, nmt_newt_stack, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_STACK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_STACK, NmtNewtStackPrivate)) + +typedef struct { + GPtrArray *children; + GPtrArray *ids; + + guint active; +} NmtNewtStackPrivate; + +enum { + PROP_0, + PROP_ACTIVE, + PROP_ACTIVE_ID, + + LAST_PROP +}; + +/** + * nmt_newt_stack_new: + * + * Creates a new #NmtNewtStack + * + * Returns: a new #NmtNewtStack + */ +NmtNewtWidget * +nmt_newt_stack_new (void) +{ + return g_object_new (NMT_TYPE_NEWT_STACK, NULL); +} + +static void +nmt_newt_stack_init (NmtNewtStack *stack) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + + priv->children = g_ptr_array_new (); + priv->ids = g_ptr_array_new_with_free_func (g_free); +} + +static void +nmt_newt_stack_finalize (GObject *object) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (object); + + g_ptr_array_unref (priv->children); + g_ptr_array_unref (priv->ids); + + G_OBJECT_CLASS (nmt_newt_stack_parent_class)->finalize (object); +} + +static newtComponent * +nmt_newt_stack_get_components (NmtNewtWidget *widget) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (widget); + + if (priv->active > priv->children->len) + return NULL; + + return nmt_newt_widget_get_components (priv->children->pdata[priv->active]); +} + +static void +nmt_newt_stack_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtStack *stack = NMT_NEWT_STACK (widget); + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + int i, child_width, child_height; + + if (priv->active > priv->children->len) { + *width = *height = 0; + return; + } + + /* We size-request all pages so that embedded NmtPageGrids will + * participate in their size-grouping (so that switching pages + * won't result in the column widths changing). + */ + for (i = 0; i < priv->children->len; i++) { + nmt_newt_widget_size_request (priv->children->pdata[i], &child_width, &child_height); + if (i == priv->active) { + *width = child_width; + *height = child_height; + } + } +} + +static void +nmt_newt_stack_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (widget); + + if (priv->active > priv->children->len) + return; + + nmt_newt_widget_size_allocate (priv->children->pdata[priv->active], x, y, width, height); +} + +/** + * nmt_newt_stack_add: + * @stack: an #NmtNewtStack + * @id: the ID for the new page + * @widget: the widget to add + * + * Adds @widget to @stack with the given @id. + */ +void +nmt_newt_stack_add (NmtNewtStack *stack, + const char *id, + NmtNewtWidget *widget) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + + g_ptr_array_add (priv->children, widget); + g_ptr_array_add (priv->ids, g_strdup (id)); + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)->add (NMT_NEWT_CONTAINER (stack), widget); +} + +static void +nmt_newt_stack_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtStack *stack = NMT_NEWT_STACK (container); + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + int i; + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)->remove (container, widget); + + for (i = 0; i < priv->children->len; i++) { + if (priv->children->pdata[i] == widget) { + g_ptr_array_remove_index (priv->children, i); + g_ptr_array_remove_index (priv->ids, i); + return; + } + } +} + +static void +nmt_newt_stack_child_validity_changed (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (container); + + if (priv->active > priv->children->len) + return; + + if (priv->children->pdata[priv->active] == (gpointer) widget) { + NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)-> + child_validity_changed (container, widget); + } +} + +/** + * nmt_newt_stack_set_active: + * @stack: an #NmtNewtStack + * @active: the index of the new active page + * + * Sets the active page on @stack to @active. + */ +void +nmt_newt_stack_set_active (NmtNewtStack *stack, + guint active) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + + if (priv->active == active) + return; + + priv->active = active; + g_object_notify (G_OBJECT (stack), "active"); + g_object_notify (G_OBJECT (stack), "active-id"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (stack)); +} + +/** + * nmt_newt_stack_get_active: + * @stack: an #NmtNewtStack + * + * Gets the index of the active page on @stack + * + * Returns: the index of the active page on @stack + */ +guint +nmt_newt_stack_get_active (NmtNewtStack *stack) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + + return priv->active; +} + +/** + * nmt_newt_stack_set_active_id: + * @stack: an #NmtNewtStack + * @active_id: the ID of the new active page + * + * Sets the active page on @stack to @active_id. + */ +void +nmt_newt_stack_set_active_id (NmtNewtStack *stack, + const char *id) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + int i; + + if (!g_strcmp0 (priv->ids->pdata[priv->active], id)) + return; + + for (i = 0; i < priv->ids->len; i++) { + if (!g_strcmp0 (priv->ids->pdata[i], id)) { + priv->active = i; + g_object_notify (G_OBJECT (stack), "active"); + g_object_notify (G_OBJECT (stack), "active-id"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (stack)); + return; + } + } +} + +/** + * nmt_newt_stack_get_active_id: + * @stack: an #NmtNewtStack + * + * Gets the ID of the active page on @stack + * + * Returns: the ID of the active page on @stack + */ +const char * +nmt_newt_stack_get_active_id (NmtNewtStack *stack) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + + if (priv->active > priv->children->len) + return NULL; + + return priv->ids->pdata[priv->active]; +} + +static void +nmt_newt_stack_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtStack *stack = NMT_NEWT_STACK (object); + + switch (prop_id) { + case PROP_ACTIVE: + nmt_newt_stack_set_active (stack, g_value_get_uint (value)); + break; + case PROP_ACTIVE_ID: + nmt_newt_stack_set_active_id (stack, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_stack_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtStack *stack = NMT_NEWT_STACK (object); + + switch (prop_id) { + case PROP_ACTIVE: + g_value_set_uint (value, nmt_newt_stack_get_active (stack)); + break; + case PROP_ACTIVE_ID: + g_value_set_string (value, nmt_newt_stack_get_active_id (stack)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_stack_class_init (NmtNewtStackClass *stack_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (stack_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (stack_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (stack_class); + + g_type_class_add_private (stack_class, sizeof (NmtNewtStackPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_stack_set_property; + object_class->get_property = nmt_newt_stack_get_property; + object_class->finalize = nmt_newt_stack_finalize; + + widget_class->get_components = nmt_newt_stack_get_components; + widget_class->size_request = nmt_newt_stack_size_request; + widget_class->size_allocate = nmt_newt_stack_size_allocate; + + container_class->remove = nmt_newt_stack_remove; + container_class->child_validity_changed = nmt_newt_stack_child_validity_changed; + + /** + * NmtNewtStack:active: + * + * The index of the active page + */ + g_object_class_install_property + (object_class, PROP_ACTIVE, + g_param_spec_uint ("active", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtStack:active-id: + * + * The ID of the active page + */ + g_object_class_install_property + (object_class, PROP_ACTIVE_ID, + g_param_spec_string ("active-id", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-stack.h b/clients/tui/newt/nmt-newt-stack.h new file mode 100644 index 00000000..09afe4ba --- /dev/null +++ b/clients/tui/newt/nmt-newt-stack.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_STACK_H +#define NMT_NEWT_STACK_H + +#include "nmt-newt-container.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_STACK (nmt_newt_stack_get_type ()) +#define NMT_NEWT_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_STACK, NmtNewtStack)) +#define NMT_NEWT_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_STACK, NmtNewtStackClass)) +#define NMT_IS_NEWT_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_STACK)) +#define NMT_IS_NEWT_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_STACK)) +#define NMT_NEWT_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_STACK, NmtNewtStackClass)) + +struct _NmtNewtStack { + NmtNewtContainer parent; + +}; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtNewtStackClass; + +GType nmt_newt_stack_get_type (void); + +NmtNewtWidget *nmt_newt_stack_new (void); + +void nmt_newt_stack_add (NmtNewtStack *stack, + const char *id, + NmtNewtWidget *widget); + +void nmt_newt_stack_set_active (NmtNewtStack *stack, + guint active); +guint nmt_newt_stack_get_active (NmtNewtStack *stack); + +void nmt_newt_stack_set_active_id (NmtNewtStack *stack, + const char *id); +const char * nmt_newt_stack_get_active_id (NmtNewtStack *stack); + +G_END_DECLS + +#endif /* NMT_NEWT_STACK_H */ diff --git a/clients/tui/newt/nmt-newt-textbox.c b/clients/tui/newt/nmt-newt-textbox.c new file mode 100644 index 00000000..3d297160 --- /dev/null +++ b/clients/tui/newt/nmt-newt-textbox.c @@ -0,0 +1,295 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-textbox + * @short_description: Multi-line text box + * + * #NmtNewtTextbox implements a multi-line text, optionally with + * word-wrapping. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-textbox.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtTextbox, nmt_newt_textbox, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_TEXTBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxPrivate)) + +typedef struct { + int wrap_width; + NmtNewtTextboxFlags flags; + + char *text; + int width, height; +} NmtNewtTextboxPrivate; + +enum { + PROP_0, + PROP_TEXT, + PROP_FLAGS, + PROP_WRAP_WIDTH, + + LAST_PROP +}; + +/** + * NmtNewtTextboxFlags: + * @NMT_NEWT_TEXTBOX_SCROLLABLE: the textbox should be scollable. + * @NMT_NEWT_TEXTBOX_SET_BACKGROUND: the textbox should have a + * white background + * + * Flags for an #NmtNewtTextbox + */ + +/** + * nmt_newt_textbox_new: + * @flags: the textbox's flags + * @wrap_width: width in characters at which to word-wrap, or + * 0 to not wrap. + * + * Creates a new #NmtNewtTextbox. + * + * Returns: a new #NmtNewtTextbox + */ +NmtNewtWidget * +nmt_newt_textbox_new (NmtNewtTextboxFlags flags, + int wrap_width) +{ + return g_object_new (NMT_TYPE_NEWT_TEXTBOX, + "flags", flags, + "wrap-width", wrap_width, + NULL); +} + +/** + * nmt_newt_textbox_get_text: + * @textbox: an #NmtNewtTextbox + * + * Gets @textbox's text + * + * Returns: @textbox's text + */ +void +nmt_newt_textbox_set_text (NmtNewtTextbox *textbox, + const char *text) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox); + char **lines; + int i, width; + + if (!text) + text = ""; + if (!strcmp (priv->text, text)) + return; + + g_free (priv->text); + priv->text = g_strdup (text); + + priv->width = priv->height = 0; + lines = g_strsplit (priv->text, "\n", -1); + for (i = 0; lines[i]; i++) { + width = nmt_newt_text_width (lines[i]); + if (width > priv->width) + priv->width = width; + } + g_free (lines); + priv->height = MIN (i, 1); + + g_object_notify (G_OBJECT (textbox), "text"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (textbox)); +} + +/** + * nmt_newt_textbox_get_text: + * @textbox: an #NmtNewtTextbox + * + * Gets @textbox's text + * + * Returns: @textbox's text + */ +const char * +nmt_newt_textbox_get_text (NmtNewtTextbox *textbox) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox); + + return priv->text; +} + +static void +nmt_newt_textbox_init (NmtNewtTextbox *textbox) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox); + + priv->text = g_strdup (""); +} + +static void +nmt_newt_textbox_finalize (GObject *object) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (object); + + g_free (priv->text); + + G_OBJECT_CLASS (nmt_newt_textbox_parent_class)->finalize (object); +} + +static guint +convert_flags (NmtNewtTextboxFlags flags) +{ + guint newt_flags = 0; + + if (flags & NMT_NEWT_TEXTBOX_SCROLLABLE) + newt_flags |= NEWT_FLAG_SCROLL; + + return newt_flags; +} + +static newtComponent +nmt_newt_textbox_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (component); + newtComponent co; + const char *text; + char *text_lc; + + text = priv->text; + if (!*text) + text = "\n"; + + text_lc = nmt_newt_locale_from_utf8 (text); + if (priv->wrap_width > 0) { + co = newtTextboxReflowed (-1, -1, text_lc, priv->wrap_width, 0, 0, 0); + } else { + co = newtTextbox (-1, -1, priv->width, priv->height, convert_flags (priv->flags)); + newtTextboxSetText (co, text_lc); + } + g_free (text_lc); + + if (priv->flags & NMT_NEWT_TEXTBOX_SET_BACKGROUND) + newtTextboxSetColors (co, NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, NEWT_COLORSET_ACTTEXTBOX); + + return co; +} + +static void +nmt_newt_textbox_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtTextbox *textbox = NMT_NEWT_TEXTBOX (object); + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox); + + switch (prop_id) { + case PROP_TEXT: + nmt_newt_textbox_set_text (textbox, g_value_get_string (value)); + break; + case PROP_FLAGS: + priv->flags = g_value_get_uint (value); + break; + case PROP_WRAP_WIDTH: + priv->wrap_width = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_textbox_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_TEXT: + g_value_set_string (value, priv->text); + break; + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_WRAP_WIDTH: + g_value_set_int (value, priv->wrap_width); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_textbox_class_init (NmtNewtTextboxClass *textbox_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (textbox_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (textbox_class); + + g_type_class_add_private (textbox_class, sizeof (NmtNewtTextboxPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_textbox_set_property; + object_class->get_property = nmt_newt_textbox_get_property; + object_class->finalize = nmt_newt_textbox_finalize; + + component_class->build_component = nmt_newt_textbox_build_component; + + /** + * NmtNewtTextbox:text: + * + * The textbox's text + */ + g_object_class_install_property + (object_class, PROP_TEXT, + g_param_spec_string ("text", "", "", + "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtTextbox:flags: + * + * The textbox's flags + */ + g_object_class_install_property + (object_class, PROP_FLAGS, + g_param_spec_uint ("flags", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtTextbox:wrap-width: + * + * The width in characters at which the textbox's text + * will wrap, or 0 if it does not wrap. + */ + g_object_class_install_property + (object_class, PROP_WRAP_WIDTH, + g_param_spec_int ("wrap-width", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-textbox.h b/clients/tui/newt/nmt-newt-textbox.h new file mode 100644 index 00000000..b3743aea --- /dev/null +++ b/clients/tui/newt/nmt-newt-textbox.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_TEXTBOX_H +#define NMT_NEWT_TEXTBOX_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_TEXTBOX (nmt_newt_textbox_get_type ()) +#define NMT_NEWT_TEXTBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextbox)) +#define NMT_NEWT_TEXTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxClass)) +#define NMT_IS_NEWT_TEXTBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_TEXTBOX)) +#define NMT_IS_NEWT_TEXTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_TEXTBOX)) +#define NMT_NEWT_TEXTBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxClass)) + +struct _NmtNewtTextbox { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtTextboxClass; + +GType nmt_newt_textbox_get_type (void); + +typedef enum { + NMT_NEWT_TEXTBOX_SCROLLABLE = (1 << 0), + NMT_NEWT_TEXTBOX_SET_BACKGROUND = (1 << 1) +} NmtNewtTextboxFlags; + +NmtNewtWidget *nmt_newt_textbox_new (NmtNewtTextboxFlags flags, + int wrap_width); + +void nmt_newt_textbox_set_text (NmtNewtTextbox *textbox, + const char *text); +const char *nmt_newt_textbox_get_text (NmtNewtTextbox *textbox); + +G_END_DECLS + +#endif /* NMT_NEWT_TEXTBOX_H */ diff --git a/clients/tui/newt/nmt-newt-toggle-button.c b/clients/tui/newt/nmt-newt-toggle-button.c new file mode 100644 index 00000000..ec4d9aca --- /dev/null +++ b/clients/tui/newt/nmt-newt-toggle-button.c @@ -0,0 +1,237 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-toggle-button + * @short_description: Toggle buttons + * + * #NmtNewtToggleButton implements a two-state toggle button. + */ + +#include "config.h" + +#include "nmt-newt-toggle-button.h" + +G_DEFINE_TYPE (NmtNewtToggleButton, nmt_newt_toggle_button, NMT_TYPE_NEWT_BUTTON) + +#define NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonPrivate)) + +typedef struct { + char *on_label, *off_label; + gboolean active; +} NmtNewtToggleButtonPrivate; + +enum { + PROP_0, + PROP_ON_LABEL, + PROP_OFF_LABEL, + PROP_ACTIVE, + + LAST_PROP +}; + +/** + * nmt_newt_toggle_button_new: + * @on_label: the button's label when it is in the "on" state + * @off_label: the button's label when it is in the "off" state + * + * Creates a new #NmtNewtToggleButton + * + * Returns: a new #NmtNewtToggleButton + */ +NmtNewtWidget * +nmt_newt_toggle_button_new (const char *on_label, + const char *off_label) +{ + return g_object_new (NMT_TYPE_NEWT_TOGGLE_BUTTON, + "on-label", on_label, + "off-label", off_label, + NULL); +} + +/** + * nmt_newt_toggle_button_get_active: + * @button: an #NmtNewtToggleButton + * + * Gets whether @button is currently "on" or "off" + * + * Returns: whether @button is currently "on" (%TRUE) or "off" (%FALSE) + */ +gboolean +nmt_newt_toggle_button_get_active (NmtNewtToggleButton *button) +{ + NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (button); + + return priv->active; +} + +/** + * nmt_newt_toggle_button_set_active: + * @button: an #NmtNewtToggleButton + * @active: whether @button should be "on" or "off" + * + * Sets whether @button is currently "on" or "off" + */ +void +nmt_newt_toggle_button_set_active (NmtNewtToggleButton *button, + gboolean active) +{ + NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (button); + + if (priv->active == active) + return; + + priv->active = active; + g_object_set (G_OBJECT (button), + "label", active ? priv->on_label : priv->off_label, + NULL); + g_object_notify (G_OBJECT (button), "active"); +} + +static void +nmt_newt_toggle_button_init (NmtNewtToggleButton *button) +{ +} + +static void +nmt_newt_toggle_button_finalize (GObject *object) +{ + NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object); + + g_free (priv->on_label); + g_free (priv->off_label); + + G_OBJECT_CLASS (nmt_newt_toggle_button_parent_class)->finalize (object); +} + +static void +nmt_newt_toggle_button_activated (NmtNewtWidget *widget) +{ + NmtNewtToggleButton *button = NMT_NEWT_TOGGLE_BUTTON (widget); + + nmt_newt_toggle_button_set_active (button, !nmt_newt_toggle_button_get_active (button)); + + NMT_NEWT_WIDGET_CLASS (nmt_newt_toggle_button_parent_class)->activated (widget); +} + +static void +nmt_newt_toggle_button_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ON_LABEL: + g_free (priv->on_label); + priv->on_label = g_value_dup_string (value); + if (priv->active) + g_object_set (object, "label", priv->on_label, NULL); + break; + case PROP_OFF_LABEL: + g_free (priv->off_label); + priv->off_label = g_value_dup_string (value); + if (!priv->active) + g_object_set (object, "label", priv->off_label, NULL); + break; + case PROP_ACTIVE: + priv->active = g_value_get_boolean (value); + g_object_set (object, + "label", priv->active ? priv->on_label : priv->off_label, + NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_toggle_button_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ON_LABEL: + g_value_set_string (value, priv->on_label); + break; + case PROP_OFF_LABEL: + g_value_set_string (value, priv->off_label); + break; + case PROP_ACTIVE: + g_value_set_boolean (value, priv->active); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_toggle_button_class_init (NmtNewtToggleButtonClass *button_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (button_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (button_class); + + g_type_class_add_private (button_class, sizeof (NmtNewtToggleButtonPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_toggle_button_set_property; + object_class->get_property = nmt_newt_toggle_button_get_property; + object_class->finalize = nmt_newt_toggle_button_finalize; + + widget_class->activated = nmt_newt_toggle_button_activated; + + /** + * NmtNewtToggleButton:on-label: + * + * The label the button displays when it is "on". + */ + g_object_class_install_property + (object_class, PROP_ON_LABEL, + g_param_spec_string ("on-label", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtToggleButton:off-label: + * + * The label the button displays when it is "off". + */ + g_object_class_install_property + (object_class, PROP_OFF_LABEL, + g_param_spec_string ("off-label", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtToggleButton:active: + * + * Whether the button is currently "on" (%TRUE) or "off" (%FALSE) + */ + g_object_class_install_property + (object_class, PROP_ACTIVE, + g_param_spec_boolean ("active", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-toggle-button.h b/clients/tui/newt/nmt-newt-toggle-button.h new file mode 100644 index 00000000..e54c6d3f --- /dev/null +++ b/clients/tui/newt/nmt-newt-toggle-button.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_TOGGLE_BUTTON_H +#define NMT_NEWT_TOGGLE_BUTTON_H + +#include "nmt-newt-button.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_TOGGLE_BUTTON (nmt_newt_toggle_button_get_type ()) +#define NMT_NEWT_TOGGLE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButton)) +#define NMT_NEWT_TOGGLE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonClass)) +#define NMT_IS_NEWT_TOGGLE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON)) +#define NMT_IS_NEWT_TOGGLE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_TOGGLE_BUTTON)) +#define NMT_NEWT_TOGGLE_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonClass)) + +struct _NmtNewtToggleButton { + NmtNewtButton parent; + +}; + +typedef struct { + NmtNewtButtonClass parent; + +} NmtNewtToggleButtonClass; + +GType nmt_newt_toggle_button_get_type (void); + +NmtNewtWidget *nmt_newt_toggle_button_new (const char *on_label, + const char *off_label); + +gboolean nmt_newt_toggle_button_get_active (NmtNewtToggleButton *button); +void nmt_newt_toggle_button_set_active (NmtNewtToggleButton *button, + gboolean active); + +G_END_DECLS + +#endif /* NMT_NEWT_TOGGLE_BUTTON_H */ diff --git a/clients/tui/newt/nmt-newt-types.h b/clients/tui/newt/nmt-newt-types.h new file mode 100644 index 00000000..608f7ff6 --- /dev/null +++ b/clients/tui/newt/nmt-newt-types.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_TYPES_H +#define NMT_NEWT_TYPES_H + +#include <glib-object.h> +#include <newt.h> +#include "nm-glib-compat.h" + +G_BEGIN_DECLS + +typedef struct _NmtNewtButton NmtNewtButton; +typedef struct _NmtNewtButtonBox NmtNewtButtonBox; +typedef struct _NmtNewtCheckbox NmtNewtCheckbox; +typedef struct _NmtNewtComponent NmtNewtComponent; +typedef struct _NmtNewtContainer NmtNewtContainer; +typedef struct _NmtNewtEntry NmtNewtEntry; +typedef struct _NmtNewtEntryNumeric NmtNewtEntryNumeric; +typedef struct _NmtNewtForm NmtNewtForm; +typedef struct _NmtNewtGrid NmtNewtGrid; +typedef struct _NmtNewtLabel NmtNewtLabel; +typedef struct _NmtNewtListbox NmtNewtListbox; +typedef struct _NmtNewtPopup NmtNewtPopup; +typedef struct _NmtNewtSection NmtNewtSection; +typedef struct _NmtNewtSectionBorder NmtNewtSectionBorder; +typedef struct _NmtNewtSeparator NmtNewtSeparator; +typedef struct _NmtNewtStack NmtNewtStack; +typedef struct _NmtNewtTextbox NmtNewtTextbox; +typedef struct _NmtNewtToggleButton NmtNewtToggleButton; +typedef struct _NmtNewtWidget NmtNewtWidget; + +G_END_DECLS + +#endif /* NMT_NEWT_COMPONENT_H */ diff --git a/clients/tui/newt/nmt-newt-utils.c b/clients/tui/newt/nmt-newt-utils.c new file mode 100644 index 00000000..70a65a84 --- /dev/null +++ b/clients/tui/newt/nmt-newt-utils.c @@ -0,0 +1,393 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-utils + * @short_description: Utility functions + */ + +#include "config.h" + +#include <errno.h> +#include <stdarg.h> +#include <unistd.h> +#include <sys/wait.h> + +#include <glib/gi18n-lib.h> + +#include "nm-glib-compat.h" +#include "nmt-newt-utils.h" + +static void +nmt_newt_dialog_g_log_handler (const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer user_data) +{ + const char *level_name; + char *full_message; + int screen_width, screen_height; + newtComponent text, ok, form; + newtGrid grid; + + g_assert (!(log_level & G_LOG_FLAG_RECURSION)); + + if (log_level & G_LOG_LEVEL_DEBUG) + return; + + switch (log_level & G_LOG_LEVEL_MASK) { + case G_LOG_LEVEL_ERROR: + level_name = "ERROR"; + break; + case G_LOG_LEVEL_CRITICAL: + level_name = "CRITICAL"; + break; + case G_LOG_LEVEL_WARNING: + level_name = "WARNING"; + break; + case G_LOG_LEVEL_MESSAGE: + level_name = "Message"; + break; + default: + level_name = NULL; + } + + full_message = g_strdup_printf ("%s%s%s%s%s", + log_domain ? log_domain : "", + log_domain && level_name ? " " : "", + level_name ? level_name : "", + log_domain || level_name ? ": " : "", + message); + + /* newtWinMessage() wraps the window too narrowly by default, so + * we don't want to use that. But we intentionally avoid using any + * NmtNewt classes, to avoid possible error recursion. + */ + + newtGetScreenSize (&screen_width, &screen_height); + text = newtTextboxReflowed (-1, -1, full_message, MAX (70, screen_width - 10), 0, 0, 0); + g_free (full_message); + + ok = newtButton (-1, -1, "OK"); + + grid = newtCreateGrid (1, 2); + newtGridSetField (grid, 0, 0, NEWT_GRID_COMPONENT, text, 0, 0, 0, 0, 0, 0); + newtGridSetField (grid, 0, 1, NEWT_GRID_COMPONENT, ok, 0, 1, 0, 0, + NEWT_ANCHOR_RIGHT, 0); + + newtGridWrappedWindow (grid, (char *) (level_name ? level_name : "")); + newtGridFree (grid, TRUE); + + form = newtForm (NULL, NULL, 0); + newtFormAddComponents (form, text, ok, NULL); + newtRunForm (form); + newtFormDestroy (form); + newtPopWindow (); +} + +static void +nmt_newt_basic_g_log_handler (const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer user_data) +{ + newtSuspend (); + g_log_default_handler (log_domain, log_level, message, NULL); + newtResume (); +} + +static void +nmt_newt_suspend_callback (gpointer user_data) +{ + newtSuspend (); + kill (getpid (), SIGTSTP); + newtResume (); +} + +/** + * nmt_newt_init: + * + * Wrapper for newtInit() that also does some nmt-newt-internal setup. + * This should be called once, before any other nmt-newt functions. + */ +void +nmt_newt_init (void) +{ + newtInit (); + newtCls (); + + newtSetColor (NEWT_COLORSET_CHECKBOX, "black", "lightgray"); + newtSetColor (NMT_NEWT_COLORSET_BAD_LABEL, "red", "lightgray"); + newtSetColor (NMT_NEWT_COLORSET_PLAIN_LABEL, "black", "lightgray"); + newtSetColor (NMT_NEWT_COLORSET_DISABLED_BUTTON, "blue", "lightgray"); + newtSetColor (NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, "black", "white"); + + if (g_getenv ("NMTUI_DEBUG")) + g_log_set_default_handler (nmt_newt_dialog_g_log_handler, NULL); + else + g_log_set_default_handler (nmt_newt_basic_g_log_handler, NULL); + + newtSetSuspendCallback (nmt_newt_suspend_callback, NULL); +} + +/** + * nmt_newt_finished: + * + * Wrapper for newtFinished(). Should be called at the end of the program. + */ +void +nmt_newt_finished (void) +{ + newtFinished (); + g_log_set_default_handler (g_log_default_handler, NULL); +} + +/** + * nmt_newt_message_dialog: + * @message: a printf()-style message format + * @...: arguments + * + * Displays the given message in a dialog box with a single "OK" + * button, and returns after the user clicks "OK". + */ +void +nmt_newt_message_dialog (const char *message, + ...) +{ + va_list ap; + char *msg, *msg_lc, *ok_lc; + + va_start (ap, message); + msg = g_strdup_vprintf (message, ap); + va_end (ap); + + msg_lc = nmt_newt_locale_from_utf8 (msg); + ok_lc = nmt_newt_locale_from_utf8 (_("OK")); + newtWinMessage (NULL, ok_lc, "%s", msg_lc); + + g_free (ok_lc); + g_free (msg_lc); + g_free (msg); +} + +/** + * nmt_newt_choice_dialog: + * @button1: the label for the first button + * @button2: the label for the second button + * @message: a printf()-style message format + * @...: arguments + * + * Displays the given message in a dialog box with two buttons with + * the indicated labels, and waits for the user to click one. + * + * Returns: which button was clicked: 0 for @button1 or 1 for @button2 + */ +int +nmt_newt_choice_dialog (const char *button1, + const char *button2, + const char *message, + ...) +{ + va_list ap; + char *msg, *msg_lc, *button1_lc, *button2_lc; + int choice; + + va_start (ap, message); + msg = g_strdup_vprintf (message, ap); + va_end (ap); + + msg_lc = nmt_newt_locale_from_utf8 (msg); + button1_lc = nmt_newt_locale_from_utf8 (button1); + button2_lc = nmt_newt_locale_from_utf8 (button2); + choice = newtWinChoice (NULL, button1_lc, button2_lc, "%s", msg_lc); + + g_free (button1_lc); + g_free (button2_lc); + g_free (msg_lc); + g_free (msg); + + return choice; +} + +/** + * nmt_newt_locale_to_utf8: + * @str_lc: a string in the user's locale encoding + * + * Convenience wrapper around g_locale_to_utf8(). + * + * Note that libnewt works in terms of the user's locale character + * set, NOT UTF-8, so all strings received from libnewt must be + * converted back to UTF-8 before being returned to the caller or used + * in other APIs. + * + * Returns: @str_lc, converted to UTF-8. + */ +char * +nmt_newt_locale_to_utf8 (const char *str_lc) +{ + char *str_utf8; + + str_utf8 = g_locale_to_utf8 (str_lc, -1, NULL, NULL, NULL); + if (!str_utf8) + str_utf8 = g_strdup (""); + return str_utf8; +} + +/** + * nmt_newt_locale_from_utf8: + * @str_utf8: a UTF-8 string + * + * Convenience wrapper around g_locale_from_utf8(). + * + * Note that libnewt works in terms of the user's locale character + * set, NOT UTF-8, so all strings from nmt-newt must be converted to + * locale encoding before being passed to libnewt. + * + * Returns: @str_utf8, converted to the user's locale encoding. + */ +char * +nmt_newt_locale_from_utf8 (const char *str_utf8) +{ + char *str_lc; + + str_lc = g_locale_from_utf8 (str_utf8, -1, NULL, NULL, NULL); + if (!str_lc) + str_lc = g_strdup (""); + return str_lc; +} + +/** + * nmt_newt_text_width + * @str: a UTF-8 string + * + * Computes the width (in terminal columns) of @str. + * + * Returns: the width of @str + */ +int +nmt_newt_text_width (const char *str) +{ + int width; + gunichar ch; + + for (width = 0; *str; str = g_utf8_next_char (str)) { + ch = g_utf8_get_char (str); + + /* Based on _vte_iso2022_unichar_width */ + if (G_LIKELY (ch < 0x80)) + width += 1; + else if (G_UNLIKELY (g_unichar_iszerowidth (ch))) + width += 0; + else if (G_UNLIKELY (g_unichar_iswide (ch))) + width += 2; + else + width += 1; + } + + return width; +} + +/** + * nmt_newt_edit_string: + * @data: data to edit + * + * libnewt does not have a multi-line editable text component, so + * nmt-newt provides this function instead, which will open the user's + * editor to edit a file containing the given @data (ensuring that the + * current screen state is saved before starting the editor and + * restored after it returns). + * + * Returns: the edited data, or %NULL if an error occurred. + */ +char * +nmt_newt_edit_string (const char *data) +{ + gssize len, nwrote; + char *filename, *argv[3]; + GError *error = NULL; + int fd, status; + char *new_data = NULL; + + fd = g_file_open_tmp ("XXXXXX.json", &filename, &error); + if (fd == -1) { + nmt_newt_message_dialog (_("Could not create temporary file: %s"), error->message); + g_error_free (error); + return NULL; + } + + len = data ? strlen (data) : 0; + while (len) { + do + nwrote = write (fd, data, len); + while (nwrote == -1 && errno == EINTR); + + len -= nwrote; + data += nwrote; + } + close (fd); + + argv[0] = (char *) g_getenv ("VISUAL"); + if (!argv[0]) + argv[0] = (char *) g_getenv ("EDITOR"); + if (!argv[0]) + argv[0] = (char *) "vi"; + argv[1] = filename; + argv[2] = NULL; + + newtSuspend (); + g_spawn_sync (NULL, argv, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, + NULL, NULL, NULL, NULL, + &status, &error); + newtResume (); + + if (error) { + nmt_newt_message_dialog (_("Could not create temporary file: %s"), error->message); + g_error_free (error); + goto done; + } + +#if GLIB_CHECK_VERSION (2, 34, 0) + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + if (!g_spawn_check_exit_status (status, &error)) { + nmt_newt_message_dialog (_("Editor failed: %s"), error->message); + g_error_free (error); + goto done; + } + G_GNUC_END_IGNORE_DEPRECATIONS +#else + if (WIFEXITED (status)) { + if (WEXITSTATUS (status) != 0) + nmt_newt_message_dialog (_("Editor failed with status %d"), WEXITSTATUS (status)); + } else if (WIFSIGNALED (status)) + nmt_newt_message_dialog (_("Editor failed with signal %d"), WTERMSIG (status)); +#endif + + if (!g_file_get_contents (filename, &new_data, NULL, &error)) { + nmt_newt_message_dialog (_("Could not re-read file: %s"), error->message); + g_error_free (error); + goto done; + } + + done: + unlink (filename); + g_free (filename); + + return new_data; +} + diff --git a/clients/tui/newt/nmt-newt-utils.h b/clients/tui/newt/nmt-newt-utils.h new file mode 100644 index 00000000..3b37868a --- /dev/null +++ b/clients/tui/newt/nmt-newt-utils.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_UTILS_H +#define NMT_NEWT_UTILS_H + +#include <newt.h> +#include <glib.h> + +G_BEGIN_DECLS + +void nmt_newt_init (void); +void nmt_newt_finished (void); + +typedef enum { + NMT_NEWT_COLORSET_BAD_LABEL = NEWT_COLORSET_CUSTOM (0), + NMT_NEWT_COLORSET_PLAIN_LABEL, + NMT_NEWT_COLORSET_DISABLED_BUTTON, + NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND +} NmtNewtColorsets; + +char *nmt_newt_locale_to_utf8 (const char *str_lc); +char *nmt_newt_locale_from_utf8 (const char *str_utf8); + +int nmt_newt_text_width (const char *str); + +void nmt_newt_message_dialog (const char *message, + ...); +int nmt_newt_choice_dialog (const char *button1, + const char *button2, + const char *message, + ...); + +char *nmt_newt_edit_string (const char *data); + +G_END_DECLS + +#endif /* NMT_NEWT_UTILS_H */ diff --git a/clients/tui/newt/nmt-newt-widget.c b/clients/tui/newt/nmt-newt-widget.c new file mode 100644 index 00000000..24c94db9 --- /dev/null +++ b/clients/tui/newt/nmt-newt-widget.c @@ -0,0 +1,668 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-widget + * @short_description: Base TUI Widget class + * + * #NmtNewtWidget is the abstract base class for nmt-newt. All widgets + * inherit from one of its two subclasses: #NmtNewtComponent, for + * widgets that wrap a (single) #newtComponent, and #NmtNewtContainer, + * for widgets consisting of multiple components. See those classes + * for more details. + * + * With the exception of #NmtNewtForm, all widgets start out with a + * floating reference, which will be sunk by the container they are + * added to. #NmtNewtForm is the "top-level" widget type, and so does + * not have a floating reference. + * + * FIXME: need RTL support + */ + +#include "config.h" + +#include "nmt-newt-widget.h" +#include "nmt-newt-form.h" + +G_DEFINE_ABSTRACT_TYPE (NmtNewtWidget, nmt_newt_widget, G_TYPE_INITIALLY_UNOWNED) + +#define NMT_NEWT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetPrivate)) + +typedef struct { + NmtNewtWidget *parent; + gboolean visible, realized, valid; + gboolean exit_on_activate; + + int pad_left, pad_top, pad_right, pad_bottom; +} NmtNewtWidgetPrivate; + +enum { + PROP_0, + + PROP_PARENT, + PROP_VISIBLE, + PROP_VALID, + PROP_EXIT_ON_ACTIVATE, + + LAST_PROP +}; + +enum { + NEEDS_REBUILD, + ACTIVATED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +nmt_newt_widget_init (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + priv->visible = TRUE; + priv->valid = TRUE; +} + +static void +nmt_newt_widget_finalize (GObject *object) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (object); + + nmt_newt_widget_unrealize (NMT_NEWT_WIDGET (object)); + g_clear_object (&priv->parent); + + G_OBJECT_CLASS (nmt_newt_widget_parent_class)->finalize (object); +} + +/** + * nmt_newt_widget_realize: + * @widget: an #NmtNewtWidget + * + * "Realizes" @widget. That is, creates #newtComponents corresponding + * to @widget and its children. + * + * You should not need to call this yourself; an #NmtNewtForm will + * cause its children to be realized and unrealized as needed. + */ +void +nmt_newt_widget_realize (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + if (!priv->realized) { + NMT_NEWT_WIDGET_GET_CLASS (widget)->realize (widget); + priv->realized = TRUE; + } +} + +/** + * nmt_newt_widget_unrealize: + * @widget: an #NmtNewtWidget + * + * "Unrealizes" @widget, destroying its #newtComponents. + * + * You should not need to call this yourself; an #NmtNewtForm will + * cause its children to be realized and unrealized as needed. + */ +void +nmt_newt_widget_unrealize (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + if (priv->realized) { + NMT_NEWT_WIDGET_GET_CLASS (widget)->unrealize (widget); + priv->realized = FALSE; + } +} + +/** + * nmt_newt_widget_get_realized: + * @widget: an #NmtNewtWidget + * + * Checks if @widget is realized or not. + * + * Returns: whether @widget is realized. + */ +gboolean +nmt_newt_widget_get_realized (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + return priv->realized; +} + +/** + * nmt_newt_widget_get_components: + * @widget: an #NmtNewtWidget + * + * Gets the #newtComponents that make up @widget, if @widget is + * visible. If @widget has not yet been realized, it will be realized + * first. + * + * If this function is called on a widget, then the widget will assume + * that someone else is now responsible for destroying the components, + * and so it will not destroy them itself when the widget is + * destroyed. Normally, components will end up being destroyed by the + * #NmtNewtForm they are added to. + * + * Returns: a %NULL-terminated array of components, in focus-chain + * order. You must free the array with g_free() when you are done + * with it. + */ +newtComponent * +nmt_newt_widget_get_components (NmtNewtWidget *widget) +{ + if (nmt_newt_widget_get_visible (widget)) { + nmt_newt_widget_realize (widget); + return NMT_NEWT_WIDGET_GET_CLASS (widget)->get_components (widget); + } else + return NULL; +} + +/** + * nmt_newt_widget_find_component: + * @widget: an #NmtNewtWidget + * @co: a #newtComponent + * + * Finds the widget inside @widget that owns @co. + * + * Return value: @co's owner, or %NULL if it was not found. + */ +NmtNewtWidget * +nmt_newt_widget_find_component (NmtNewtWidget *widget, + newtComponent co) +{ + return NMT_NEWT_WIDGET_GET_CLASS (widget)->find_component (widget, co); +} + +/** + * nmt_newt_widget_set_padding: + * @widget: an #NmtNewtWidget + * @pad_left: padding on the left of @widget + * @pad_top: padding on the top of @widget + * @pad_right: padding on the right of @widget + * @pad_bottom: padding on the bottom of @widget + * + * Sets the padding on @widget. + */ +void +nmt_newt_widget_set_padding (NmtNewtWidget *widget, + int pad_left, + int pad_top, + int pad_right, + int pad_bottom) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + priv->pad_left = pad_left; + priv->pad_top = pad_top; + priv->pad_right = pad_right; + priv->pad_bottom = pad_bottom; +} + +/** + * nmt_newt_widget_size_request: + * @widget: an #NmtNewtWidget + * @width: (out): on output, the widget's requested width + * @height: (out): on output, the widget's requested height + * + * Asks @widget for its requested size. If @widget is not visible, + * this will return 0, 0. If @widget has not yet been realized, it + * will be realized first. + */ +void +nmt_newt_widget_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + if (nmt_newt_widget_get_visible (widget)) { + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + nmt_newt_widget_realize (widget); + NMT_NEWT_WIDGET_GET_CLASS (widget)->size_request (widget, width, height); + + *width += priv->pad_left + priv->pad_right; + *height += priv->pad_top + priv->pad_bottom; + } else + *width = *height = 0; +} + +/** + * nmt_newt_widget_size_allocate: + * @widget: an #NmtNewtWidget + * @x: the widget's (absolute) X coordinate + * @y: the widget's (absolute) Y coordinate + * @width: the widget's allocated width + * @height: the widget's allocated height + * + * Positions @widget at the given coordinates, with the given size. If + * @widget is not visible, this has no effect. If @widget has not yet + * been realized, it will be realized first. + * + * @x and @y are absolute coordinates (ie, relative to the screen / + * terminal window, not relative to @widget's parent). + * + * In general, the results are undefined if @width or @height is less + * than the widget's requested size. If @width or @height is larger + * than the requested size, most #NmtNewtComponents will ignore the + * extra space, but some components and most containers will expand to + * fit. + */ +void +nmt_newt_widget_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + if (nmt_newt_widget_get_visible (widget)) { + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + nmt_newt_widget_realize (widget); + x += priv->pad_left; + y += priv->pad_top; + width -= priv->pad_left + priv->pad_right; + height -= priv->pad_top + priv->pad_bottom; + + NMT_NEWT_WIDGET_GET_CLASS (widget)->size_allocate (widget, x, y, width, height); + } +} + +/** + * nmt_newt_widget_get_focus_component: + * @widget: an #NmtNewtWidget + * + * Gets the #newtComponent that should be given the keyboard focus when + * @widget is focused. + * + * Returns: the #newtComponent to focus, or %NULL if @widget can't + * take the focus. + */ +newtComponent +nmt_newt_widget_get_focus_component (NmtNewtWidget *widget) +{ + if (!NMT_NEWT_WIDGET_GET_CLASS (widget)->get_focus_component) + return NULL; + + return NMT_NEWT_WIDGET_GET_CLASS (widget)->get_focus_component (widget); +} + +static void +nmt_newt_widget_real_activated (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + if (priv->exit_on_activate) + nmt_newt_form_quit (nmt_newt_widget_get_form (widget)); +} + +/** + * nmt_newt_widget_activated: + * @widget: an #NmtNewtWidget + * + * Tells @widget that its #newtComponent has been activated (ie, the + * user hit "Return" on it) and emits #NmtNewtWidget::activated. + * + * If #NmtNewtWidget:exit-on-activate is set on @widget, then this + * will call nmt_newt_form_quit() on the widget's form. + */ +void +nmt_newt_widget_activated (NmtNewtWidget *widget) +{ + g_signal_emit (widget, signals[ACTIVATED], 0); +} + +/** + * nmt_newt_widget_get_exit_on_activate: + * @widget: an #NmtNewtWidget + * + * Gets @widget's #NmtNewtWidget:exit-on-activate flag, qv. + * + * Returns: @widget's #NmtNewtWidget:exit-on-activate flag + */ +gboolean +nmt_newt_widget_get_exit_on_activate (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + return priv->exit_on_activate; +} + +/** + * nmt_newt_widget_set_exit_on_activate: + * @widget: an #NmtNewtWidget + * @exit_on_activate: whether @widget should exit on activate. + * + * Sets @widget's #NmtNewtWidget:exit-on-activate flag, qv. + */ +void +nmt_newt_widget_set_exit_on_activate (NmtNewtWidget *widget, + gboolean exit_on_activate) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + exit_on_activate = !!exit_on_activate; + if (priv->exit_on_activate != exit_on_activate) { + priv->exit_on_activate = exit_on_activate; + g_object_notify (G_OBJECT (widget), "exit-on-activate"); + } +} + +/** + * nmt_newt_widget_get_visible: + * @widget: an #NmtNewtWidget + * + * Gets @widget's #NmtNewtWidget:visible flag, qv. + * + * Returns: @widget's #NmtNewtWidget:visible flag + */ +gboolean +nmt_newt_widget_get_visible (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + return priv->visible; +} + +/** + * nmt_newt_widget_set_visible: + * @widget: an #NmtNewtWidget + * @visible: whether @widget should be visible + * + * Sets @widget's #NmtNewtWidget:visible flag, qv. + */ +void +nmt_newt_widget_set_visible (NmtNewtWidget *widget, + gboolean visible) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + visible = !!visible; + if (priv->visible != visible) { + priv->visible = visible; + g_object_notify (G_OBJECT (widget), "visible"); + nmt_newt_widget_needs_rebuild (widget); + } +} + +/** + * nmt_newt_widget_set_parent: + * @widget: an #NmtNewtWidget + * @parent: @widget's parent + * + * Sets @widget's parent to @parent. This is used internally by + * #NmtNewtContainer implementations; you must use an appropriate + * container-specific method to actually add a widget to a container. + */ +void +nmt_newt_widget_set_parent (NmtNewtWidget *widget, + NmtNewtWidget *parent) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + g_clear_object (&priv->parent); + priv->parent = parent ? g_object_ref (parent) : NULL; + g_object_notify (G_OBJECT (widget), "parent"); +} + +/** + * nmt_newt_widget_get_parent: + * @widget: an #NmtNewtWidget + * + * Gets @widget's parent + * + * Returns: @widget's parent + */ +NmtNewtWidget * +nmt_newt_widget_get_parent (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + return priv->parent; +} + +/** + * nmt_newt_widget_get_form: + * @widget: an #NmtNewtWidget + * + * Gets @widget's top-level form. + * + * Returns: @widget's #NmtNewtForm + */ +NmtNewtForm * +nmt_newt_widget_get_form (NmtNewtWidget *widget) +{ + while (widget) { + if (NMT_IS_NEWT_FORM (widget)) + return NMT_NEWT_FORM (widget); + widget = nmt_newt_widget_get_parent (widget); + } + + return NULL; +} + +/** + * nmt_newt_widget_get_valid: + * @widget: an #NmtNewtWidget + * + * Gets @widget's #NmtNewtWidget:valid flag, indicating whether its + * content is valid. + * + * Returns: @widget's #NmtNewtWidget:valid flag + */ +gboolean +nmt_newt_widget_get_valid (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + return priv->valid; +} + +/** + * nmt_newt_widget_set_valid: + * @widget: an #NmtNewtWidget + * @valid: whether @widget is valid + * + * Sets @widget's #NmtNewtWidget:valid flag, indicating whether its + * content is valid. + * + * This method should be considered "protected"; if you change it, the + * widget implementation will likely just change it back at some + * point. + */ +void +nmt_newt_widget_set_valid (NmtNewtWidget *widget, + gboolean valid) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + valid = !!valid; + if (priv->valid == valid) + return; + + priv->valid = valid; + g_object_notify (G_OBJECT (widget), "valid"); +} + +/** + * nmt_newt_widget_needs_rebuilds: + * @widget: an #NmtNewtWidget + * + * Marks @widget as needing to be "rebuilt" (ie, re-realized). This is + * called automatically in some cases (such as when adding a widget to + * or removing it from a container). #NmtNewtComponent implementations + * should also call this if they need to make some change that can + * only be done by destroying their current #newtComponent and + * creating a new one. + */ +void +nmt_newt_widget_needs_rebuild (NmtNewtWidget *widget) +{ + g_signal_emit (widget, signals[NEEDS_REBUILD], 0); +} + +static void +nmt_newt_widget_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtWidget *widget = NMT_NEWT_WIDGET (object); + + switch (prop_id) { + case PROP_PARENT: + nmt_newt_widget_set_parent (widget, g_value_get_object (value)); + break; + case PROP_VISIBLE: + nmt_newt_widget_set_visible (widget, g_value_get_boolean (value)); + break; + case PROP_EXIT_ON_ACTIVATE: + nmt_newt_widget_set_exit_on_activate (widget, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_widget_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object (value, priv->parent); + break; + case PROP_VISIBLE: + g_value_set_boolean (value, priv->visible); + break; + case PROP_VALID: + g_value_set_boolean (value, priv->valid); + break; + case PROP_EXIT_ON_ACTIVATE: + g_value_set_boolean (value, priv->exit_on_activate); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_widget_class_init (NmtNewtWidgetClass *widget_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (widget_class); + + g_type_class_add_private (widget_class, sizeof (NmtNewtWidgetPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_widget_set_property; + object_class->get_property = nmt_newt_widget_get_property; + object_class->finalize = nmt_newt_widget_finalize; + + widget_class->activated = nmt_newt_widget_real_activated; + + /* signals */ + + /** + * NmtNewtWidget::needs-rebuild: + * @widget: the #NmtNewtWidget + * + * Emitted when nmt_newt_widget_need_rebuild() is called on @widget + * or any of its children. This signal propagates up the container + * hierarchy, eventually reaching the top-level #NmtNewtForm. + */ + signals[NEEDS_REBUILD] = + g_signal_new ("needs-rebuild", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NmtNewtWidgetClass, needs_rebuild), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /** + * NmtNewtWidget::activated: + * @widget: the #NmtNewtWidget + * + * Emitted when the widget's #newtComponent is activated. + */ + signals[ACTIVATED] = + g_signal_new ("activated", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NmtNewtWidgetClass, activated), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /* properties */ + + /** + * NmtNewtWidget:parent: + * + * The widget's parent widget, or %NULL if it has no parent. + */ + g_object_class_install_property + (object_class, PROP_PARENT, + g_param_spec_object ("parent", "", "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtWidget:visible: + * + * Whether the widget is visible. Invisible widgets do not get + * realized or sized. + */ + g_object_class_install_property + (object_class, PROP_VISIBLE, + g_param_spec_boolean ("visible", "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtWidget:valid: + * + * Whether the widget's content is considered valid. Components + * determine their own validity. A container, by default, is + * considered valid if all of its children are valid. + */ + g_object_class_install_property + (object_class, PROP_VALID, + g_param_spec_boolean ("valid", "", "", + TRUE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtWidget:exit-on-activate: + * + * If %TRUE, the widget will call nmt_newt_form_quit() on its form + * when it is activated. + */ + g_object_class_install_property + (object_class, PROP_EXIT_ON_ACTIVATE, + g_param_spec_boolean ("exit-on-activate", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/newt/nmt-newt-widget.h b/clients/tui/newt/nmt-newt-widget.h new file mode 100644 index 00000000..a526a674 --- /dev/null +++ b/clients/tui/newt/nmt-newt-widget.h @@ -0,0 +1,118 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_WIDGET_H +#define NMT_NEWT_WIDGET_H + +#include "nmt-newt-types.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_WIDGET (nmt_newt_widget_get_type ()) +#define NMT_NEWT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidget)) +#define NMT_NEWT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass)) +#define NMT_IS_NEWT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_WIDGET)) +#define NMT_IS_NEWT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_WIDGET)) +#define NMT_NEWT_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass)) + +struct _NmtNewtWidget { + GInitiallyUnowned parent; + +}; + +typedef struct { + GInitiallyUnownedClass parent; + + /* signals */ + void (*needs_rebuild) (NmtNewtWidget *widget); + void (*activated) (NmtNewtWidget *widget); + + /* methods */ + void (*realize) (NmtNewtWidget *widget); + void (*unrealize) (NmtNewtWidget *widget); + + newtComponent * (*get_components) (NmtNewtWidget *widget); + NmtNewtWidget * (*find_component) (NmtNewtWidget *widget, + newtComponent co); + + void (*size_request) (NmtNewtWidget *widget, + int *width, + int *height); + void (*size_allocate) (NmtNewtWidget *widget, + int x, + int y, + int width, + int height); + + newtComponent (*get_focus_component) (NmtNewtWidget *widget); + +} NmtNewtWidgetClass; + +GType nmt_newt_widget_get_type (void); + +void nmt_newt_widget_realize (NmtNewtWidget *widget); +void nmt_newt_widget_unrealize (NmtNewtWidget *widget); +gboolean nmt_newt_widget_get_realized (NmtNewtWidget *widget); + +newtComponent *nmt_newt_widget_get_components (NmtNewtWidget *widget); + +void nmt_newt_widget_set_padding (NmtNewtWidget *widget, + int pad_left, + int pad_top, + int pad_right, + int pad_bottom); + +void nmt_newt_widget_size_request (NmtNewtWidget *widget, + int *width, + int *height); +void nmt_newt_widget_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height); + +void nmt_newt_widget_set_parent (NmtNewtWidget *widget, + NmtNewtWidget *parent); +NmtNewtWidget *nmt_newt_widget_get_parent (NmtNewtWidget *widget); + +NmtNewtForm *nmt_newt_widget_get_form (NmtNewtWidget *widget); + +gboolean nmt_newt_widget_get_visible (NmtNewtWidget *widget); +void nmt_newt_widget_set_visible (NmtNewtWidget *widget, + gboolean visible); + +newtComponent nmt_newt_widget_get_focus_component (NmtNewtWidget *widget); + +void nmt_newt_widget_activated (NmtNewtWidget *widget); +gboolean nmt_newt_widget_get_exit_on_activate (NmtNewtWidget *widget); +void nmt_newt_widget_set_exit_on_activate (NmtNewtWidget *widget, + gboolean exit_on_activate); + +gboolean nmt_newt_widget_get_valid (NmtNewtWidget *widget); + +NmtNewtWidget *nmt_newt_widget_find_component (NmtNewtWidget *widget, + newtComponent co); + +/* protected */ +void nmt_newt_widget_needs_rebuild (NmtNewtWidget *widget); +void nmt_newt_widget_set_valid (NmtNewtWidget *widget, + gboolean valid); + +G_END_DECLS + +#endif /* NMT_NEWT_WIDGET_H */ diff --git a/clients/tui/newt/nmt-newt.h b/clients/tui/newt/nmt-newt.h new file mode 100644 index 00000000..6a9c8d9f --- /dev/null +++ b/clients/tui/newt/nmt-newt.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_H +#define NMT_NEWT_H + +#include "nmt-newt-button.h" +#include "nmt-newt-button-box.h" +#include "nmt-newt-checkbox.h" +#include "nmt-newt-entry.h" +#include "nmt-newt-entry-numeric.h" +#include "nmt-newt-form.h" +#include "nmt-newt-grid.h" +#include "nmt-newt-label.h" +#include "nmt-newt-listbox.h" +#include "nmt-newt-popup.h" +#include "nmt-newt-section.h" +#include "nmt-newt-separator.h" +#include "nmt-newt-stack.h" +#include "nmt-newt-textbox.h" +#include "nmt-newt-toggle-button.h" +#include "nmt-newt-utils.h" + +#endif /* NMT_NEWT_H */ diff --git a/clients/tui/nm-editor-bindings.c b/clients/tui/nm-editor-bindings.c new file mode 100644 index 00000000..9218c31f --- /dev/null +++ b/clients/tui/nm-editor-bindings.c @@ -0,0 +1,1101 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nm-editor-bindings + * @short_description: #GBinding-based NM connection editor helpers + * + * nm-editor-bindings contains helper functions to bind NMSettings objects + * to connection editing widgets. The goal is that this should eventually be + * shared between nmtui, nm-connection-editor, and gnome-control-center. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> + +#include "nm-editor-bindings.h" +#include "nm-gvaluearray-compat.h" + +static void +value_transform_string_int (const GValue *src_value, + GValue *dest_value) +{ + long val; + char *end; + + val = strtol (g_value_get_string (src_value), &end, 10); + if (val < G_MININT || val > G_MAXINT || *end) + return; + + g_value_set_int (dest_value, (int) val); +} + +static void +value_transform_string_uint (const GValue *src_value, + GValue *dest_value) +{ + long val; + char *end; + + val = strtol (g_value_get_string (src_value), &end, 10); + if (val < 0 || val > G_MAXUINT || *end) + return; + + g_value_set_uint (dest_value, (gint) val); +} + +void +nm_editor_bindings_init (void) +{ + /* glib registers number -> string, but not string -> number */ + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, value_transform_string_int); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_UINT, value_transform_string_uint); +} + +static gboolean +parse_addr_prefix (const char *text, + int family, + char **addr, + guint32 *prefix) +{ + const char *slash; + char *addrstr, *end; + gboolean valid; + + slash = strchr (text, '/'); + + if (slash) + addrstr = g_strndup (text, slash - text); + else + addrstr = g_strdup (text); + valid = nm_utils_ipaddr_valid (family, addrstr); + + if (slash) { + *prefix = strtoul (slash + 1, &end, 10); + if ( *end + || *prefix == 0 + || (family == AF_INET && *prefix > 32) + || (family == AF_INET6 && *prefix > 128)) + valid = FALSE; + } else if (prefix) { + if (family == AF_INET) + *prefix = 32; + else + *prefix = 128; + } + + if (addr && valid) + *addr = addrstr; + else + g_free (addrstr); + return valid; +} + +static gboolean +ip_addresses_with_prefix_to_strv (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *addrs; + NMIPAddress *addr; + const char *addrstr; + guint32 prefix; + char **strings; + int i; + + addrs = g_value_get_boxed (source_value); + strings = g_new0 (char *, addrs->len + 1); + + for (i = 0; i < addrs->len; i++) { + addr = addrs->pdata[i]; + addrstr = nm_ip_address_get_address (addr); + prefix = nm_ip_address_get_prefix (addr); + + if (addrstr) + strings[i] = g_strdup_printf ("%s/%d", addrstr, (int) prefix); + else + strings[i] = g_strdup (""); + } + + g_value_take_boxed (target_value, strings); + return TRUE; +} + +static gboolean +ip_addresses_with_prefix_from_strv (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + int family = GPOINTER_TO_INT (user_data); + char **strings; + GPtrArray *addrs; + NMIPAddress *addr; + char *addrstr; + guint32 prefix; + int i; + + strings = g_value_get_boxed (source_value); + /* Fetch the original property value, so as to preserve their extra attributes */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &addrs, + NULL); + + for (i = 0; strings[i]; i++) { + if (i >= addrs->len) { + if (family == AF_INET) + addr = nm_ip_address_new (AF_INET, "0.0.0.0", 32, NULL); + else + addr = nm_ip_address_new (AF_INET6, "::", 128, NULL); + g_ptr_array_add (addrs, addr); + } else + addr = addrs->pdata[i]; + + if (!parse_addr_prefix (strings[i], family, &addrstr, &prefix)) { + g_ptr_array_unref (addrs); + return FALSE; + } + + nm_ip_address_set_address (addr, addrstr); + nm_ip_address_set_prefix (addr, prefix); + g_free (addrstr); + } + + g_ptr_array_set_size (addrs, i); + g_value_take_boxed (target_value, addrs); + return TRUE; +} + +/** + * nm_editor_bind_ip_addresses_with_prefix_to_strv: + * @family: the IP address family + * @source: the source object (eg, an #NMSettingIP4Config) + * @source_property: the property on @source to bind (eg, + * %NM_SETTING_IP4_CONFIG_ADDRESSES) + * @target: the target object (eg, an #NmtAddressList) + * @target_property: the property on @target to bind + * (eg, "strings") + * @flags: %GBindingFlags + * + * Binds the #GPtrArray-of-#NMIPAddress property @source_property on @source to + * the %G_TYPE_STRV property @target_property on @target. + * + * Each #NMIPAddress in @source_property will be converted to a string of the + * form "ip.ad.dr.ess/prefix" or "ip:ad:dr:ess/prefix" in @target_property (and + * vice versa if %G_BINDING_BIDIRECTIONAL) is specified. + */ +void +nm_editor_bind_ip_addresses_with_prefix_to_strv (int family, + gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + target, target_property, + flags, + ip_addresses_with_prefix_to_strv, + ip_addresses_with_prefix_from_strv, + GINT_TO_POINTER (family), NULL); +} + +static gboolean +ip_addresses_check_and_copy (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + int family = GPOINTER_TO_INT (user_data); + char **strings; + int i; + + strings = g_value_get_boxed (source_value); + + for (i = 0; strings[i]; i++) { + if (!nm_utils_ipaddr_valid (family, strings[i])) + return FALSE; + } + + g_value_set_boxed (target_value, strings); + return TRUE; +} + +/** + * nm_editor_bind_ip_addresses_to_strv: + * @family: the IP address family + * @source: the source object (eg, an #NMSettingIP4Config) + * @source_property: the property on @source to bind (eg, + * %NM_SETTING_IP4_CONFIG_DNS) + * @target: the target object (eg, an #NmtAddressList) + * @target_property: the property on @target to bind + * (eg, "strings") + * @flags: %GBindingFlags + * + * Binds the %G_TYPE_STRV property @source_property on @source to the + * %G_TYPE_STRV property @target_property on @target, verifying that + * each string is a valid address of type @family when copying. + */ +void +nm_editor_bind_ip_addresses_to_strv (int family, + gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + target, target_property, + flags, + ip_addresses_check_and_copy, + ip_addresses_check_and_copy, + GINT_TO_POINTER (family), NULL); +} + +static gboolean +ip_gateway_to_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + g_value_set_string (target_value, g_value_get_string (source_value)); + return TRUE; +} + +static gboolean +ip_gateway_from_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + int family = GPOINTER_TO_INT (user_data); + const char *gateway; + + gateway = g_value_get_string (source_value); + if (gateway && !nm_utils_ipaddr_valid (family, gateway)) + gateway = NULL; + + g_value_set_string (target_value, gateway); + return TRUE; +} + +static gboolean +ip_addresses_to_gateway (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *addrs; + + addrs = g_value_get_boxed (source_value); + if (addrs->len == 0) { + g_value_set_string (target_value, NULL); + return TRUE; + } else + return FALSE; +} + +static gboolean +ip_addresses_to_sensitivity (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *addrs; + + addrs = g_value_get_boxed (source_value); + g_value_set_boolean (target_value, addrs->len != 0); + return TRUE; +} + +/** + * nm_editor_bind_ip_gateway_to_string: + * @family: the IP address family + * @source: the source #NMSettingIPConfig + * @target: the target object (eg, an #NmtIPEntry) + * @target_property: the property on @target to bind (eg, "text") + * @target_sensitive_property: the "sensitivity" property on @target to bind + * @flags: %GBindingFlags + * + * Binds the #NMSettingIPConfig:gateway property on @source to the + * %G_TYPE_STRING property @target_property and %G_TYPE_BOOLEAN property + * @target_sensitive_property on @target, also taking the + * #NMSettingIPConfig:addresses property on @source into account. + * + * In particular, if @source has no static IP addresses, then @target_property + * will be set to "" and @target_sensitive_property will be set to %FALSE. + * + * If @source has at least one static IP address, then + * @target_sensitive_property will be set to %TRUE, @target_property will be + * initialized from @source's #NMSettingIPConfig:gateway, and @source will be + * updated with the value of @target_property whenever it contains a valid IP + * address. + */ +void +nm_editor_bind_ip_gateway_to_string (int family, + NMSettingIPConfig *source, + gpointer target, + const gchar *target_property, + const gchar *target_sensitive_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, "gateway", + target, target_property, + flags, + ip_gateway_to_string, + ip_gateway_from_string, + GINT_TO_POINTER (family), NULL); + g_object_bind_property_full (source, "addresses", + source, "gateway", + (flags & G_BINDING_SYNC_CREATE), + ip_addresses_to_gateway, + NULL, + NULL, NULL); + g_object_bind_property_full (source, "addresses", + target, target_sensitive_property, + (flags & G_BINDING_SYNC_CREATE), + ip_addresses_to_sensitivity, + NULL, + NULL, NULL); +} + +static gboolean +ip_route_transform_to_dest_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIPRoute *route; + const char *addrstr; + char *string; + + route = g_value_get_boxed (source_value); + if (route) + addrstr = nm_ip_route_get_dest (route); + else + addrstr = NULL; + + if (addrstr) { + string = g_strdup_printf ("%s/%d", addrstr, (int) nm_ip_route_get_prefix (route)); + g_value_take_string (target_value, string); + } else + g_value_set_string (target_value, ""); + return TRUE; +} + +static gboolean +ip_route_transform_to_next_hop_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIPRoute *route; + const char *addrstr; + + route = g_value_get_boxed (source_value); + if (route) { + addrstr = nm_ip_route_get_next_hop (route); + if (!addrstr) + addrstr = ""; + } else + addrstr = ""; + + g_value_set_string (target_value, addrstr); + return TRUE; +} + +static gboolean +ip_route_transform_to_metric_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIPRoute *route; + char *string; + + route = g_value_get_boxed (source_value); + if (route && nm_ip_route_get_dest (route) && nm_ip_route_get_metric (route) != -1) { + string = g_strdup_printf ("%lu", (gulong) nm_ip_route_get_metric (route)); + g_value_take_string (target_value, string); + } else + g_value_set_string (target_value, ""); + return TRUE; +} + +static gboolean +ip_route_transform_from_dest_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + int family = GPOINTER_TO_INT (user_data); + NMIPRoute *route; + const char *text; + char *addrstr; + guint32 prefix; + + text = g_value_get_string (source_value); + if (!parse_addr_prefix (text, family, &addrstr, &prefix)) + return FALSE; + + /* Fetch the original property value */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &route, + NULL); + + nm_ip_route_set_dest (route, addrstr); + nm_ip_route_set_prefix (route, prefix); + g_free (addrstr); + + g_value_take_boxed (target_value, route); + return TRUE; +} + +static gboolean +ip_route_transform_from_next_hop_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + int family = GPOINTER_TO_INT (user_data); + NMIPRoute *route; + const char *text; + + text = g_value_get_string (source_value); + if (*text) { + if (!nm_utils_ipaddr_valid (family, text)) + return FALSE; + } else + text = NULL; + + /* Fetch the original property value */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &route, + NULL); + + nm_ip_route_set_next_hop (route, text); + + g_value_take_boxed (target_value, route); + return TRUE; +} + +static gboolean +ip_route_transform_from_metric_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIPRoute *route; + const char *text; + gint64 metric; + + text = g_value_get_string (source_value); + if (*text) + metric = strtoul (text, NULL, 10); + else + metric = -1; + + /* Fetch the original property value */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &route, + NULL); + + nm_ip_route_set_metric (route, metric); + + g_value_take_boxed (target_value, route); + return TRUE; +} + +/** + * nm_editor_bind_ip_route_to_strings: + * @family: the IP address family + * @source: the source object + * @source_property: the source property + * @dest_target: the target object for the route's destionation + * @dest_target_property: the property on @dest_target + * @next_hop_target: the target object for the route's next hop + * @next_hop_target_property: the property on @next_hop_target + * @metric_target: the target object for the route's metric + * @metric_target_property: the property on @metric_target + * @flags: %GBindingFlags + * + * Binds the #NMIPRoute-valued property @source_property on @source to the + * three indicated string-valued target properties (and vice versa if + * %G_BINDING_BIDIRECTIONAL is specified). + * + * @dest_target_property should be an "address/prefix" string, as with + * nm_editor_bind_ip4_addresses_with_prefix_to_strv(). @next_hop_target_property + * is a plain IP address, and @metric_target_property is a number. + */ +void +nm_editor_bind_ip_route_to_strings (int family, + gpointer source, + const gchar *source_property, + gpointer dest_target, + const gchar *dest_target_property, + gpointer next_hop_target, + const gchar *next_hop_target_property, + gpointer metric_target, + const gchar *metric_target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + dest_target, dest_target_property, + flags, + ip_route_transform_to_dest_string, + ip_route_transform_from_dest_string, + GINT_TO_POINTER (family), NULL); + g_object_bind_property_full (source, source_property, + next_hop_target, next_hop_target_property, + flags, + ip_route_transform_to_next_hop_string, + ip_route_transform_from_next_hop_string, + GINT_TO_POINTER (family), NULL); + g_object_bind_property_full (source, source_property, + metric_target, metric_target_property, + flags, + ip_route_transform_to_metric_string, + ip_route_transform_from_metric_string, + GINT_TO_POINTER (family), NULL); +} + +/* Wireless security method binding */ +typedef struct { + NMConnection *connection; + NMSettingWirelessSecurity *s_wsec; + gboolean s_wsec_in_use; + + GObject *target; + char *target_property; + + gboolean updating; +} NMEditorWirelessSecurityMethodBinding; + +static const char * +get_security_type (NMEditorWirelessSecurityMethodBinding *binding) +{ + const char *key_mgmt, *auth_alg; + + if (!binding->s_wsec_in_use) + return "none"; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (binding->s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (binding->s_wsec); + + /* No IEEE 802.1x */ + if (!strcmp (key_mgmt, "none")) { + NMWepKeyType wep_type = nm_setting_wireless_security_get_wep_key_type (binding->s_wsec); + + if (wep_type == NM_WEP_KEY_TYPE_KEY) + return "wep-key"; + else + return "wep-passphrase"; + } + + if (!strcmp (key_mgmt, "ieee8021x")) { + if (auth_alg && !strcmp (auth_alg, "leap")) + return "leap"; + return "dynamic-wep"; + } + + if ( !strcmp (key_mgmt, "wpa-none") + || !strcmp (key_mgmt, "wpa-psk")) + return "wpa-personal"; + + if (!strcmp (key_mgmt, "wpa-eap")) + return "wpa-enterprise"; + + return NULL; +} + +static void +wireless_security_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMEditorWirelessSecurityMethodBinding *binding = user_data; + + if (binding->updating) + return; + + binding->updating = TRUE; + g_object_set (binding->target, + binding->target_property, get_security_type (binding), + NULL); + binding->updating = FALSE; +} + +static void +wireless_connection_changed (NMConnection *connection, + gpointer user_data) +{ + NMEditorWirelessSecurityMethodBinding *binding = user_data; + NMSettingWirelessSecurity *s_wsec; + + if (binding->updating) + return; + + s_wsec = nm_connection_get_setting_wireless_security (connection); + if ( (s_wsec && binding->s_wsec_in_use) + || (!s_wsec && !binding->s_wsec_in_use)) + return; + + binding->s_wsec_in_use = !binding->s_wsec_in_use; + wireless_security_changed (NULL, NULL, binding); +} + +static void +wireless_security_target_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMEditorWirelessSecurityMethodBinding *binding = user_data; + char *method; + + if (binding->updating) + return; + + g_object_get (binding->target, + binding->target_property, &method, + NULL); + + binding->updating = TRUE; + + if (!strcmp (method, "none")) { + if (!binding->s_wsec_in_use) + return; + binding->s_wsec_in_use = FALSE; + nm_connection_remove_setting (binding->connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + + binding->updating = FALSE; + return; + } + + if (!binding->s_wsec_in_use) { + binding->s_wsec_in_use = TRUE; + nm_connection_add_setting (binding->connection, NM_SETTING (binding->s_wsec)); + } + + if (!strcmp (method, "wep-key")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_KEY, + NULL); + } else if (!strcmp (method, "wep-passphrase")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_PASSPHRASE, + NULL); + } else if (!strcmp (method, "leap")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, + NULL); + } else if (!strcmp (method, "dynamic-wep")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, + NULL); + } else if (!strcmp (method, "wpa-personal")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, NULL, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, + NULL); + } else if (!strcmp (method, "wpa-enterprise")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, NULL, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, + NULL); + } else + g_warn_if_reached (); + + binding->updating = FALSE; +} + +static void +wireless_security_target_destroyed (gpointer user_data, + GObject *ex_target) +{ + NMEditorWirelessSecurityMethodBinding *binding = user_data; + + g_signal_handlers_disconnect_by_func (binding->s_wsec, G_CALLBACK (wireless_security_changed), binding); + g_object_unref (binding->s_wsec); + g_object_unref (binding->connection); + + g_free (binding->target_property); + + g_slice_free (NMEditorWirelessSecurityMethodBinding, binding); +} + +/** + * nm_editor_bind_wireless_security_method: + * @connection: an #NMConnection + * @s_wsec: an #NMSettingWirelessSecurity + * @target: the target widget + * @target_property: the string-valued property on @target to bind + * @flags: %GBindingFlags + * + * Binds the wireless security method on @connection to + * @target_property on @target (and vice versa if + * %G_BINDING_BIDIRECTIONAL). + * + * @target_property will be of the values "none", "wpa-personal", + * "wpa-enterprise", "wep-key", "wep-passphrase", "dynamic-wep", or + * "leap". + * + * If binding bidirectionally, @s_wsec will be automatically added to + * or removed from @connection as needed when @target_property + * changes. + */ +void +nm_editor_bind_wireless_security_method (NMConnection *connection, + NMSettingWirelessSecurity *s_wsec, + gpointer target, + const char *target_property, + GBindingFlags flags) +{ + NMEditorWirelessSecurityMethodBinding *binding; + char *notify; + + binding = g_slice_new0 (NMEditorWirelessSecurityMethodBinding); + + binding->target = target; + binding->target_property = g_strdup (target_property); + if (flags & G_BINDING_BIDIRECTIONAL) { + notify = g_strdup_printf ("notify::%s", target_property); + g_signal_connect (target, notify, G_CALLBACK (wireless_security_target_changed), binding); + g_free (notify); + } + g_object_weak_ref (target, wireless_security_target_destroyed, binding); + + binding->connection = g_object_ref (connection); + g_signal_connect (connection, NM_CONNECTION_CHANGED, + G_CALLBACK (wireless_connection_changed), binding); + binding->s_wsec_in_use = (nm_connection_get_setting_wireless_security (connection) != NULL); + + binding->s_wsec = g_object_ref (s_wsec); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + G_CALLBACK (wireless_security_changed), binding); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + G_CALLBACK (wireless_security_changed), binding); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, + G_CALLBACK (wireless_security_changed), binding); + + if (flags & G_BINDING_SYNC_CREATE) + wireless_security_changed (NULL, NULL, binding); +} + +/* WEP key binding */ + +typedef struct { + NMSettingWirelessSecurity *s_wsec; + GObject *entry, *key_selector; + char *entry_property, *key_selector_property; + + gboolean updating; +} NMEditorWepKeyBinding; + +static void +wep_key_setting_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMEditorWepKeyBinding *binding = user_data; + const char *key; + int index; + + if (binding->updating) + return; + + index = nm_setting_wireless_security_get_wep_tx_keyidx (binding->s_wsec); + key = nm_setting_wireless_security_get_wep_key (binding->s_wsec, index); + + binding->updating = TRUE; + g_object_set (binding->key_selector, + binding->key_selector_property, index, + NULL); + g_object_set (binding->entry, + binding->entry_property, key, + NULL); + binding->updating = FALSE; +} + +static void +wep_key_ui_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMEditorWepKeyBinding *binding = user_data; + char *key; + int index; + + if (binding->updating) + return; + + g_object_get (binding->key_selector, + binding->key_selector_property, &index, + NULL); + g_object_get (binding->entry, + binding->entry_property, &key, + NULL); + + binding->updating = TRUE; + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, index, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, index == 0 ? key : NULL, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, index == 1 ? key : NULL, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, index == 2 ? key : NULL, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, index == 3 ? key : NULL, + NULL); + binding->updating = FALSE; + + g_free (key); +} + +static void +wep_key_target_destroyed (gpointer user_data, + GObject *ex_target) +{ + NMEditorWepKeyBinding *binding = user_data; + + g_signal_handlers_disconnect_by_func (binding->s_wsec, G_CALLBACK (wep_key_setting_changed), binding); + + if (ex_target != binding->entry) { + g_signal_handlers_disconnect_by_func (binding->entry, G_CALLBACK (wep_key_ui_changed), binding); + g_object_weak_unref (binding->entry, wep_key_target_destroyed, binding); + } else { + g_signal_handlers_disconnect_by_func (binding->key_selector, G_CALLBACK (wep_key_ui_changed), binding); + g_object_weak_unref (binding->key_selector, wep_key_target_destroyed, binding); + } + + g_object_unref (binding->s_wsec); + g_free (binding->entry_property); + g_free (binding->key_selector_property); + + g_slice_free (NMEditorWepKeyBinding, binding); +} + +/** + * nm_editor_bind_wireless_security_wep_key: + * @s_wsec: an #NMSettingWirelessSecurity + * @entry: an entry widget + * @entry_property: the string-valued property on @entry to bind + * @key_selector: a pop-up widget of some sort + * @key_selector_property: the integer-valued property on + * @key_selector to bind + * @flags: %GBindingFlags + * + * Binds the "wep-tx-keyidx" property on @s_wsec to + * @key_selector_property on @key_selector, and the corresponding + * "wep-keyN" property to @entry_property on @entry (and vice versa if + * %G_BINDING_BIDIRECTIONAL). + */ +void +nm_editor_bind_wireless_security_wep_key (NMSettingWirelessSecurity *s_wsec, + gpointer entry, + const char *entry_property, + gpointer key_selector, + const char *key_selector_property, + GBindingFlags flags) +{ + NMEditorWepKeyBinding *binding; + char *notify; + + binding = g_slice_new0 (NMEditorWepKeyBinding); + binding->s_wsec = g_object_ref (s_wsec); + binding->entry = entry; + binding->entry_property = g_strdup (entry_property); + binding->key_selector = key_selector; + binding->key_selector_property = g_strdup (key_selector_property); + + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, + G_CALLBACK (wep_key_setting_changed), binding); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, + G_CALLBACK (wep_key_setting_changed), binding); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, + G_CALLBACK (wep_key_setting_changed), binding); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, + G_CALLBACK (wep_key_setting_changed), binding); + + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, + G_CALLBACK (wep_key_setting_changed), binding); + + if (flags & G_BINDING_BIDIRECTIONAL) { + notify = g_strdup_printf ("notify::%s", entry_property); + g_signal_connect (entry, notify, G_CALLBACK (wep_key_ui_changed), binding); + g_free (notify); + + notify = g_strdup_printf ("notify::%s", key_selector_property); + g_signal_connect (key_selector, notify, G_CALLBACK (wep_key_ui_changed), binding); + g_free (notify); + } + + g_object_weak_ref (entry, wep_key_target_destroyed, binding); + g_object_weak_ref (key_selector, wep_key_target_destroyed, binding); + + if (flags & G_BINDING_SYNC_CREATE) + wep_key_setting_changed (NULL, NULL, binding); +} + +/* VLAN binding */ + +typedef struct { + NMSettingVlan *s_vlan; + NMSettingConnection *s_con; + + char *last_ifname_parent; + int last_ifname_id; + + gboolean updating; +} NMEditorVlanWidgetBinding; + +static gboolean +parse_interface_name (const char *ifname, + char **parent_ifname, + int *id) +{ + const char *ifname_end; + char *end; + + if (!ifname || !*ifname) + return FALSE; + + if (g_str_has_prefix (ifname, "vlan")) { + ifname_end = ifname + 4; + *id = strtoul (ifname_end, &end, 10); + if (*end || end == (char *)ifname_end || *id < 0) + return FALSE; + *parent_ifname = NULL; + return TRUE; + } + + ifname_end = strchr (ifname, '.'); + if (ifname_end) { + *id = strtoul (ifname_end + 1, &end, 10); + if (*end || end == (char *)ifname_end + 1 || *id < 0) + return FALSE; + *parent_ifname = g_strndup (ifname, ifname_end - ifname); + return TRUE; + } + + return FALSE; +} + +static void +vlan_settings_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMEditorVlanWidgetBinding *binding = user_data; + const char *ifname, *parent; + char *ifname_parent; + int ifname_id, id; + + if (binding->updating) + return; + + ifname = nm_setting_connection_get_interface_name (binding->s_con); + parent = nm_setting_vlan_get_parent (binding->s_vlan); + id = nm_setting_vlan_get_id (binding->s_vlan); + + if (!parse_interface_name (ifname, &ifname_parent, &ifname_id)) + return; + + /* If the id in INTERFACE_NAME changed, and ID is either unset, or was previously + * in sync with INTERFACE_NAME, then update ID. + */ + if ( id != ifname_id + && (id == binding->last_ifname_id || id == 0)) { + binding->updating = TRUE; + g_object_set (G_OBJECT (binding->s_vlan), + NM_SETTING_VLAN_ID, ifname_id, + NULL); + binding->updating = FALSE; + } + + /* If the PARENT in INTERFACE_NAME changed, and PARENT is either unset, or was + * previously in sync with INTERFACE_NAME, then update PARENT. + */ + if ( g_strcmp0 (parent, ifname_parent) != 0 + && ( g_strcmp0 (parent, binding->last_ifname_parent) == 0 + || !parent || !*parent)) { + binding->updating = TRUE; + g_object_set (G_OBJECT (binding->s_vlan), + NM_SETTING_VLAN_PARENT, ifname_parent, + NULL); + binding->updating = FALSE; + } + + g_free (binding->last_ifname_parent); + binding->last_ifname_parent = ifname_parent; + binding->last_ifname_id = ifname_id; +} + +static void +vlan_target_destroyed (gpointer user_data, + GObject *ex_target) +{ + NMEditorVlanWidgetBinding *binding = user_data; + + g_free (binding->last_ifname_parent); + g_slice_free (NMEditorVlanWidgetBinding, binding); +} + +/** + * nm_editor_bind_vlan_name: + * @s_vlan: an #NMSettingVlan + * + * Binds together several properties on @s_vlan, so that if the + * %NM_SETTING_VLAN_INTERFACE_NAME matches %NM_SETTING_VLAN_PARENT + * and %NM_SETTING_VLAN_ID in the obvious way, then changes to + * %NM_SETTING_VLAN_INTERFACE_NAME will propagate to the other + * two properties automatically. + */ +void +nm_editor_bind_vlan_name (NMSettingVlan *s_vlan, + NMSettingConnection *s_con) +{ + NMEditorVlanWidgetBinding *binding; + const char *ifname; + + binding = g_slice_new0 (NMEditorVlanWidgetBinding); + binding->s_vlan = s_vlan; + binding->s_con = s_con; + + g_signal_connect (s_con, "notify::" NM_SETTING_CONNECTION_INTERFACE_NAME, + G_CALLBACK (vlan_settings_changed), binding); + + g_object_weak_ref (G_OBJECT (s_vlan), vlan_target_destroyed, binding); + + ifname = nm_setting_connection_get_interface_name (s_con); + if (!parse_interface_name (ifname, &binding->last_ifname_parent, &binding->last_ifname_id)) { + binding->last_ifname_parent = NULL; + binding->last_ifname_id = 0; + } +} diff --git a/clients/tui/nm-editor-bindings.h b/clients/tui/nm-editor-bindings.h new file mode 100644 index 00000000..c4ed95d5 --- /dev/null +++ b/clients/tui/nm-editor-bindings.h @@ -0,0 +1,76 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NM_EDITOR_BINDINGS_H +#define NM_EDITOR_BINDINGS_H + +#include <NetworkManager.h> + +G_BEGIN_DECLS + +void nm_editor_bindings_init (void); + +void nm_editor_bind_ip_addresses_with_prefix_to_strv (int family, + gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags); +void nm_editor_bind_ip_addresses_to_strv (int family, + gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags); + +void nm_editor_bind_ip_gateway_to_string (int family, + NMSettingIPConfig *source, + gpointer target, + const gchar *target_property, + const gchar *target_sensitive_property, + GBindingFlags flags); + +void nm_editor_bind_ip_route_to_strings (int family, + gpointer source, + const gchar *source_property, + gpointer dest_target, + const gchar *dest_target_property, + gpointer next_hop_target, + const gchar *next_hop_target_property, + gpointer metric_target, + const gchar *metric_target_property, + GBindingFlags flags); + +void nm_editor_bind_wireless_security_method (NMConnection *connection, + NMSettingWirelessSecurity *s_wsec, + gpointer target, + const char *target_property, + GBindingFlags flags); +void nm_editor_bind_wireless_security_wep_key (NMSettingWirelessSecurity *s_wsec, + gpointer entry, + const char *entry_property, + gpointer key_selector, + const char *key_selector_property, + GBindingFlags flags); + +void nm_editor_bind_vlan_name (NMSettingVlan *s_vlan, + NMSettingConnection *s_con); + +G_END_DECLS + +#endif /* NM_EDITOR_BINDINGS_H */ diff --git a/clients/tui/nm-editor-utils.c b/clients/tui/nm-editor-utils.c new file mode 100644 index 00000000..f936c959 --- /dev/null +++ b/clients/tui/nm-editor-utils.c @@ -0,0 +1,414 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2012, 2013 Red Hat, Inc. + */ + +/** + * SECTION:nm-editor-utils + * @short_description: Miscellaneous connection editor utilities + * + * nm-editor-utils contains helper functions for connection editors. + * The goal is that this should eventually be shared between nmtui, + * nm-connection-editor, and gnome-control-center. + */ + +#include "config.h" + +#include <glib/gi18n.h> +#include <NetworkManager.h> + +#include "nm-editor-utils.h" +#if 0 +#include "vpn-helpers.h" + +static GSList *vpn_plugins; + +static gint +sort_vpn_plugins (gconstpointer a, gconstpointer b) +{ + NMVpnPluginUiInterface *aa = NM_VPN_PLUGIN_UI_INTERFACE (a); + NMVpnPluginUiInterface *bb = NM_VPN_PLUGIN_UI_INTERFACE (b); + char *aa_desc = NULL, *bb_desc = NULL; + int ret; + + g_object_get (aa, NM_VPN_PLUGIN_UI_INTERFACE_NAME, &aa_desc, NULL); + g_object_get (bb, NM_VPN_PLUGIN_UI_INTERFACE_NAME, &bb_desc, NULL); + + ret = g_strcmp0 (aa_desc, bb_desc); + + g_free (aa_desc); + g_free (bb_desc); + + return ret; +} +#endif + +static void +wifi_connection_setup_func (NMConnection *connection, + NMSettingConnection *s_con, + NMSetting *s_hw) +{ + g_object_set (G_OBJECT (s_hw), + NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, + NULL); +} + +static void +bond_connection_setup_func (NMConnection *connection, + NMSettingConnection *s_con, + NMSetting *s_hw) +{ + NMSettingBond *s_bond = NM_SETTING_BOND (s_hw); + const char **options, *def, *cur; + int i; + + options = nm_setting_bond_get_valid_options (s_bond); + for (i = 0; options[i]; i++) { + def = nm_setting_bond_get_option_default (s_bond, options[i]); + cur = nm_setting_bond_get_option_by_name (s_bond, options[i]); + if (g_strcmp0 (def, cur) != 0) + nm_setting_bond_add_option (s_bond, options[i], def); + } +} + +typedef void (*NMEditorNewConnectionSetupFunc) (NMConnection *connection, + NMSettingConnection *s_con, + NMSetting *s_hw); + +typedef struct { + NMEditorConnectionTypeData data; + + const char *id_format; + NMEditorNewConnectionSetupFunc connection_setup_func; + gboolean no_autoconnect; +} NMEditorConnectionTypeDataReal; + +static gint +sort_types (gconstpointer a, gconstpointer b) +{ + NMEditorConnectionTypeData *typea = *(NMEditorConnectionTypeData **)a; + NMEditorConnectionTypeData *typeb = *(NMEditorConnectionTypeData **)b; + + if (typea->virtual && !typeb->virtual) + return 1; + else if (typeb->virtual && !typea->virtual) + return -1; + + if (typea->setting_type == NM_TYPE_SETTING_VPN && + typeb->setting_type != NM_TYPE_SETTING_VPN) + return 1; + else if (typeb->setting_type == NM_TYPE_SETTING_VPN && + typea->setting_type != NM_TYPE_SETTING_VPN) + return -1; + + return g_utf8_collate (typea->name, typeb->name); +} + +/** + * nm_editor_utils_get_connection_type_list: + * + * Gets an array of information about supported connection types. The + * array is sorted in a standard presentation order (hardware types + * first, alphabetized, then virtual types, alphabetized, then VPN + * types, alphabetized). + * + * Returns: the array of connection type information + */ +NMEditorConnectionTypeData ** +nm_editor_utils_get_connection_type_list (void) +{ + GPtrArray *array; + NMEditorConnectionTypeDataReal *item; + static NMEditorConnectionTypeData **list; +#if 0 + GHashTable *vpn_plugins_hash; + gboolean have_vpn_plugins; +#endif + + if (list) + return list; + + array = g_ptr_array_new (); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Ethernet"); + item->data.setting_type = NM_TYPE_SETTING_WIRED; + item->data.device_type = NM_TYPE_DEVICE_ETHERNET; + item->data.virtual = FALSE; + item->id_format = _("Ethernet connection %d"); + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Wi-Fi"); + item->data.setting_type = NM_TYPE_SETTING_WIRELESS; + item->data.device_type = NM_TYPE_DEVICE_WIFI; + item->data.virtual = FALSE; + item->id_format = _("Wi-Fi connection %d"); + item->connection_setup_func = wifi_connection_setup_func; + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("InfiniBand"); + item->data.setting_type = NM_TYPE_SETTING_INFINIBAND; + item->data.device_type = NM_TYPE_DEVICE_INFINIBAND; + item->data.virtual = FALSE; + item->id_format = _("InfiniBand connection %d"); + g_ptr_array_add (array, item); + +#if 0 + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Mobile Broadband"); + item->data.setting_type = NM_TYPE_SETTING_GSM; + item->data.virtual = FALSE; + item->id_format = _("Mobile broadband connection %d"); + item->no_autoconnect = TRUE; + g_ptr_array_add (array, item); +#endif + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("DSL"); + item->data.setting_type = NM_TYPE_SETTING_PPPOE; + item->data.device_type = NM_TYPE_DEVICE_ETHERNET; + item->data.virtual = FALSE; + item->id_format = _("DSL connection %d"); + item->no_autoconnect = TRUE; + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Bond"); + item->data.setting_type = NM_TYPE_SETTING_BOND; + item->data.device_type = NM_TYPE_DEVICE_BOND; + item->data.virtual = TRUE; + item->id_format = _("Bond connection %d"); + item->connection_setup_func = bond_connection_setup_func; + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Bridge"); + item->data.setting_type = NM_TYPE_SETTING_BRIDGE; + item->data.slave_setting_type = NM_TYPE_SETTING_BRIDGE_PORT; + item->data.device_type = NM_TYPE_DEVICE_BRIDGE; + item->data.virtual = TRUE; + item->id_format = _("Bridge connection %d"); + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Team"); + item->data.setting_type = NM_TYPE_SETTING_TEAM; + item->data.slave_setting_type = NM_TYPE_SETTING_TEAM_PORT; + item->data.device_type = NM_TYPE_DEVICE_TEAM; + item->data.virtual = TRUE; + item->id_format = _("Team connection %d"); + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("VLAN"); + item->data.setting_type = NM_TYPE_SETTING_VLAN; + item->data.device_type = NM_TYPE_DEVICE_VLAN; + item->data.virtual = TRUE; + item->id_format = _("VLAN connection %d"); + g_ptr_array_add (array, item); + +#if 0 + /* Add "VPN" only if there are plugins */ + vpn_plugins_hash = vpn_get_plugins (NULL); + have_vpn_plugins = vpn_plugins_hash && g_hash_table_size (vpn_plugins_hash); + if (have_vpn_plugins) { + GHashTableIter iter; + gpointer name, plugin; + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("VPN"); + item->data.setting_type = NM_TYPE_SETTING_VPN; + item->data.virtual = TRUE; + item->id_format = _("VPN connection %d"); + item->no_autoconnect = TRUE; + g_ptr_array_add (array, item); + + vpn_plugins = NULL; + g_hash_table_iter_init (&iter, vpn_plugins_hash); + while (g_hash_table_iter_next (&iter, &name, &plugin)) + vpn_plugins = g_slist_prepend (vpn_plugins, plugin); + vpn_plugins = g_slist_sort (vpn_plugins, sort_vpn_plugins); + } +#endif + + g_ptr_array_sort (array, sort_types); + g_ptr_array_add (array, NULL); + + list = (NMEditorConnectionTypeData **)g_ptr_array_free (array, FALSE); + return list; +} + +static char * +get_available_connection_name (const char *format, + NMClient *client) +{ + const GPtrArray *conns; + GSList *names = NULL, *iter; + char *cname = NULL; + int i = 0; + + conns = nm_client_get_connections (client); + for (i = 0; i < conns->len; i++) { + const char *id; + + id = nm_connection_get_id (NM_CONNECTION (conns->pdata[i])); + g_assert (id); + names = g_slist_append (names, (gpointer) id); + } + + /* Find the next available unique connection name */ + for (i = 1; !cname && i < 10000; i++) { + char *temp; + gboolean found = FALSE; + + temp = g_strdup_printf (format, i); + for (iter = names; iter; iter = g_slist_next (iter)) { + if (!strcmp (iter->data, temp)) { + found = TRUE; + break; + } + } + if (!found) + cname = temp; + else + g_free (temp); + } + + g_slist_free (names); + return cname; +} + +/** + * nm_editor_utils_create_connection: + * @type: the type of the connection's primary #NMSetting + * @master: (allow-none): the connection's master, if any + * @client: an #NMClient + * + * Creates a new #NMConnection of the given type, automatically + * creating a UUID and an appropriate not-currently-in-use connection + * name, setting #NMSettingConnection:autoconnect appropriately for + * the connection type, filling in slave-related information if + * @master is not %NULL, and initializing any other mandatory-to-set + * properties to reasonable initial values. + * + * Returns: a new #NMConnection + */ +NMConnection * +nm_editor_utils_create_connection (GType type, + NMConnection *master, + NMClient *client) +{ + NMEditorConnectionTypeData **types; + NMEditorConnectionTypeDataReal *type_data = NULL; + const char *master_setting_type = NULL, *master_uuid = NULL; + GType master_type = G_TYPE_INVALID, slave_setting_type = G_TYPE_INVALID; + NMConnection *connection; + NMSettingConnection *s_con; + NMSetting *s_hw, *s_slave; + char *uuid, *id; + int i; + + if (master) { + NMSettingConnection *master_s_con; + + master_s_con = nm_connection_get_setting_connection (master); + master_setting_type = nm_setting_connection_get_connection_type (master_s_con); + master_uuid = nm_setting_connection_get_uuid (master_s_con); + master_type = nm_setting_lookup_type (master_setting_type); + } + + types = nm_editor_utils_get_connection_type_list (); + for (i = 0; types[i]; i++) { + if (types[i]->setting_type == type) + type_data = (NMEditorConnectionTypeDataReal *)types[i]; + if (types[i]->setting_type == master_type) + slave_setting_type = types[i]->slave_setting_type; + + } + if (!type_data) { + g_return_val_if_reached (NULL); + return NULL; + } + + connection = nm_simple_connection_new (); + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + s_hw = g_object_new (type, NULL); + nm_connection_add_setting (connection, s_hw); + + if (slave_setting_type != G_TYPE_INVALID) { + s_slave = g_object_new (slave_setting_type, NULL); + nm_connection_add_setting (connection, s_slave); + } + + uuid = nm_utils_uuid_generate (); + id = get_available_connection_name (type_data->id_format, client); + + g_object_set (s_con, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_ID, id, + NM_SETTING_CONNECTION_TYPE, nm_setting_get_name (s_hw), + NM_SETTING_CONNECTION_AUTOCONNECT, !type_data->no_autoconnect, + NM_SETTING_CONNECTION_MASTER, master_uuid, + NM_SETTING_CONNECTION_SLAVE_TYPE, master_setting_type, + NULL); + + g_free (uuid); + g_free (id); + + if (type_data->connection_setup_func) + type_data->connection_setup_func (connection, s_con, s_hw); + + return connection; +} + +/** + * nm_editor_utils_get_connection_type_data: + * @conn: an #NMConnection + * + * Gets the #NMEditorConnectionTypeData corresponding to + * @conn's connection type. + * + * Returns: the #NMEditorConnectionTypeData + */ +NMEditorConnectionTypeData * +nm_editor_utils_get_connection_type_data (NMConnection *conn) +{ + NMSettingConnection *s_con; + const char *conn_type; + GType conn_gtype; + NMEditorConnectionTypeData **types; + int i; + + s_con = nm_connection_get_setting_connection (conn); + g_return_val_if_fail (s_con != NULL, NULL); + + conn_type = nm_setting_connection_get_connection_type (s_con); + conn_gtype = nm_setting_lookup_type (conn_type); + g_return_val_if_fail (conn_gtype != G_TYPE_INVALID, NULL); + + types = nm_editor_utils_get_connection_type_list (); + for (i = 0; types[i]; i++) { + if (types[i]->setting_type == conn_gtype) + return types[i]; + } + + return NULL; +} diff --git a/clients/tui/nm-editor-utils.h b/clients/tui/nm-editor-utils.h new file mode 100644 index 00000000..5e402ce7 --- /dev/null +++ b/clients/tui/nm-editor-utils.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2012, 2013 Red Hat, Inc. + */ + +#ifndef NM_EDITOR_UTILS_H +#define NM_EDITOR_UTILS_H + +#include <NetworkManager.h> + +G_BEGIN_DECLS + +typedef struct { + const char *name; + GType setting_type; + GType slave_setting_type; + GType device_type; + gboolean virtual; +} NMEditorConnectionTypeData; + +NMEditorConnectionTypeData **nm_editor_utils_get_connection_type_list (void); +NMEditorConnectionTypeData *nm_editor_utils_get_connection_type_data (NMConnection *conn); + +NMConnection *nm_editor_utils_create_connection (GType type, + NMConnection *master, + NMClient *client); + +G_END_DECLS + +#endif /* NM_EDITOR_UTILS_H */ diff --git a/clients/tui/nmt-address-list.c b/clients/tui/nmt-address-list.c new file mode 100644 index 00000000..42873c52 --- /dev/null +++ b/clients/tui/nmt-address-list.c @@ -0,0 +1,285 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-address-list + * @short_description: An editable list of IP addresses or hostnames + * + * #NmtAddressList is a subclass of #NmtWidgetList that contains + * entries displaying IP addresses, address/prefix strings, or + * hostnames. This is designed for binding its #NmtAddressList:strings + * property to an appropriate #NMSettingIP4Config or + * #NMSettingIP6Config property via one of the nm-editor-bindings + * functions. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> + +#include <glib/gi18n-lib.h> + +#include "nmt-address-list.h" +#include "nmt-ip-entry.h" + +G_DEFINE_TYPE (NmtAddressList, nmt_address_list, NMT_TYPE_WIDGET_LIST) + +#define NMT_ADDRESS_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ADDRESS_LIST, NmtAddressListPrivate)) + +typedef struct { + NmtAddressListType list_type; + char **strings; +} NmtAddressListPrivate; + +enum { + PROP_0, + PROP_LIST_TYPE, + PROP_STRINGS, + + LAST_PROP +}; + +/** + * NmtAddressListType: + * @NMT_ADDRESS_LIST_IP4_WITH_PREFIX: IPv4 address/prefix strings + * @NMT_ADDRESS_LIST_IP4: IPv4 addresses + * @NMT_ADDRESS_LIST_IP6_WITH_PREFIX: IPv6 address/prefix strings + * @NMT_ADDRESS_LIST_IP6: IPv6 addresses + * @NMT_ADDRESS_LIST_HOSTNAME: hostnames + * + * The type of address in an #NmtAddressList + */ + +/** + * nmt_address_list_new: + * @list_type: the type of address the list will contain + * + * Creates a new #NmtAddressList + * + * Returns: a new #NmtAddressList + */ +NmtNewtWidget * +nmt_address_list_new (NmtAddressListType list_type) +{ + return g_object_new (NMT_TYPE_ADDRESS_LIST, + "list-type", list_type, + NULL); +} + +static void +nmt_address_list_init (NmtAddressList *list) +{ +} + +static gboolean +strings_transform_to_entry (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + int n = GPOINTER_TO_INT (user_data); + char **strings; + + strings = g_value_get_boxed (source_value); + if (n >= g_strv_length (strings)) + return FALSE; + + g_value_set_string (target_value, strings[n]); + return TRUE; +} + +static gboolean +strings_transform_from_entry (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NmtAddressList *list = NMT_ADDRESS_LIST (g_binding_get_source (binding)); + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list); + int n = GPOINTER_TO_INT (user_data); + + if (n >= g_strv_length (priv->strings)) + return FALSE; + + g_free (priv->strings[n]); + priv->strings[n] = g_value_dup_string (source_value); + + g_value_set_boxed (target_value, priv->strings); + return TRUE; +} + +static gboolean +hostname_filter (NmtNewtEntry *entry, + const char *text, + int ch, + int position, + gpointer user_data) +{ + return g_ascii_isalnum (ch) || ch == '.' || ch == '-'; +} + +static NmtNewtWidget * +nmt_address_list_create_widget (NmtWidgetList *list, + int num) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list); + NmtNewtWidget *entry; + + if (priv->list_type == NMT_ADDRESS_LIST_IP4_WITH_PREFIX) + entry = nmt_ip_entry_new (25, AF_INET, TRUE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_IP4) + entry = nmt_ip_entry_new (25, AF_INET, FALSE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_IP6_WITH_PREFIX) + entry = nmt_ip_entry_new (25, AF_INET6, TRUE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_IP6) + entry = nmt_ip_entry_new (25, AF_INET6, FALSE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_HOSTNAME) { + entry = nmt_newt_entry_new (25, NMT_NEWT_ENTRY_NONEMPTY); + nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), hostname_filter, list); + } else + g_assert_not_reached (); + + g_object_bind_property_full (list, "strings", entry, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + strings_transform_to_entry, + strings_transform_from_entry, + GINT_TO_POINTER (num), NULL); + + return entry; +} + +static void +nmt_address_list_add_clicked (NmtWidgetList *list) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list); + int len; + + len = priv->strings ? g_strv_length (priv->strings) : 0; + priv->strings = g_renew (char *, priv->strings, len + 2); + priv->strings[len] = g_strdup (""); + priv->strings[len + 1] = NULL; + + nmt_widget_list_set_length (list, len + 1); + g_object_notify (G_OBJECT (list), "strings"); +} + +static void +nmt_address_list_remove_clicked (NmtWidgetList *list, + int num) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list); + int len; + + len = g_strv_length (priv->strings); + g_free (priv->strings[num]); + memmove (priv->strings + num, priv->strings + num + 1, (len - num) * sizeof (char *)); + + nmt_widget_list_set_length (list, len - 1); + g_object_notify (G_OBJECT (list), "strings"); +} + +static void +nmt_address_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LIST_TYPE: + priv->list_type = g_value_get_uint (value); + break; + case PROP_STRINGS: + g_strfreev (priv->strings); + priv->strings = g_value_dup_boxed (value); + if (!priv->strings) + priv->strings = g_new0 (char *, 1); + nmt_widget_list_set_length (NMT_WIDGET_LIST (object), + g_strv_length (priv->strings)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_address_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LIST_TYPE: + g_value_set_uint (value, priv->list_type); + break; + case PROP_STRINGS: + g_value_set_boxed (value, priv->strings); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_address_list_class_init (NmtAddressListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (list_class); + NmtWidgetListClass *widget_list_class = NMT_WIDGET_LIST_CLASS (list_class); + + g_type_class_add_private (list_class, sizeof (NmtAddressListPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_address_list_set_property; + object_class->get_property = nmt_address_list_get_property; + + widget_list_class->create_widget = nmt_address_list_create_widget; + widget_list_class->add_clicked = nmt_address_list_add_clicked; + widget_list_class->remove_clicked = nmt_address_list_remove_clicked; + + /** + * NmtAddressList:list-type: + * + * The type of address the list holds. + */ + g_object_class_install_property + (object_class, PROP_LIST_TYPE, + g_param_spec_uint ("list-type", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtAddressList:strings: + * + * The strings in the list's entries. + */ + g_object_class_install_property + (object_class, PROP_STRINGS, + g_param_spec_boxed ("strings", "", "", + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-address-list.h b/clients/tui/nmt-address-list.h new file mode 100644 index 00000000..df7a4f79 --- /dev/null +++ b/clients/tui/nmt-address-list.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_ADDRESS_LIST_H +#define NMT_ADDRESS_LIST_H + +#include "nmt-widget-list.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_ADDRESS_LIST (nmt_address_list_get_type ()) +#define NMT_ADDRESS_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressList)) +#define NMT_ADDRESS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass)) +#define NMT_IS_ADDRESS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ADDRESS_LIST)) +#define NMT_IS_ADDRESS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ADDRESS_LIST)) +#define NMT_ADDRESS_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass)) + +typedef struct { + NmtWidgetList parent; + +} NmtAddressList; + +typedef struct { + NmtWidgetListClass parent; + +} NmtAddressListClass; + +GType nmt_address_list_get_type (void); + +typedef enum { + NMT_ADDRESS_LIST_IP4_WITH_PREFIX, + NMT_ADDRESS_LIST_IP4, + NMT_ADDRESS_LIST_IP6_WITH_PREFIX, + NMT_ADDRESS_LIST_IP6, + NMT_ADDRESS_LIST_HOSTNAME +} NmtAddressListType; + +NmtNewtWidget *nmt_address_list_new (NmtAddressListType list_type); + +G_END_DECLS + +#endif /* NMT_ADDRESS_LIST_H */ diff --git a/clients/tui/nmt-connect-connection-list.c b/clients/tui/nmt-connect-connection-list.c new file mode 100644 index 00000000..08058066 --- /dev/null +++ b/clients/tui/nmt-connect-connection-list.c @@ -0,0 +1,701 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-connect-connection-list + * @short_description: Connection list for "nmtui connect" + * + * #NmtConnectConnectionList is the list of devices, connections, and + * access points displayed by "nmtui connect". + */ + +#include "config.h" + +#include <stdlib.h> +#include <glib/gi18n-lib.h> + +#include <NetworkManager.h> + +#include "nmtui.h" +#include "nmt-connect-connection-list.h" + +G_DEFINE_TYPE (NmtConnectConnectionList, nmt_connect_connection_list, NMT_TYPE_NEWT_LISTBOX) + +#define NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListPrivate)) + +typedef struct { + char *name; + NMDevice *device; + + int sort_order; + + GSList *conns; +} NmtConnectDevice; + +typedef struct { + const char *name; + char *ssid; + + NMConnection *conn; + NMAccessPoint *ap; + NMDevice *device; + NMActiveConnection *active; +} NmtConnectConnection; + +typedef struct { + GSList *nmt_devices; +} NmtConnectConnectionListPrivate; + +/** + * nmt_connect_connection_list_new: + * + * Creates a new #NmtConnectConnectionList + * + * Returns: a new #NmtConnectConnectionList + */ +NmtNewtWidget * +nmt_connect_connection_list_new (void) +{ + return g_object_new (NMT_TYPE_CONNECT_CONNECTION_LIST, + "flags", NMT_NEWT_LISTBOX_SCROLL | NMT_NEWT_LISTBOX_BORDER, + "skip-null-keys", TRUE, + NULL); +} + +static void +nmt_connect_connection_list_init (NmtConnectConnectionList *list) +{ +} + +static void +nmt_connect_connection_free (NmtConnectConnection *nmtconn) +{ + g_clear_object (&nmtconn->conn); + g_clear_object (&nmtconn->ap); + g_clear_object (&nmtconn->active); + g_free (nmtconn->ssid); +} + +static void +nmt_connect_device_free (NmtConnectDevice *nmtdev) +{ + g_clear_pointer (&nmtdev->name, g_free); + g_clear_object (&nmtdev->device); + + g_slist_free_full (nmtdev->conns, (GDestroyNotify) nmt_connect_connection_free); +} + +static const char *device_sort_order[] = { + "NMDeviceEthernet", + "NMDeviceInfiniband", + "NMDeviceWifi", + NM_SETTING_VLAN_SETTING_NAME, + NM_SETTING_BOND_SETTING_NAME, + NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_BRIDGE_SETTING_NAME, + "NMDeviceModem", + "NMDeviceBt" +}; +static const int device_sort_order_len = G_N_ELEMENTS (device_sort_order); + +static int +get_sort_order_for_device (NMDevice *device) +{ + const char *type; + int i; + + type = G_OBJECT_TYPE_NAME (device); + for (i = 0; i < device_sort_order_len; i++) { + if (!strcmp (type, device_sort_order[i])) + return i; + } + + return -1; +} + +static int +get_sort_order_for_connection (NMConnection *conn) +{ + NMSettingConnection *s_con; + const char *type; + int i; + + s_con = nm_connection_get_setting_connection (conn); + type = nm_setting_connection_get_connection_type (s_con); + + for (i = 0; i < device_sort_order_len; i++) { + if (!strcmp (type, device_sort_order[i])) + return i; + } + + return -1; +} + +static int +sort_connections (gconstpointer a, + gconstpointer b) +{ + NmtConnectConnection *nmta = (NmtConnectConnection *)a; + NmtConnectConnection *nmtb = (NmtConnectConnection *)b; + + /* If nmta and nmtb both have NMConnections, sort them by timestamp */ + if (nmta->conn && nmtb->conn) { + NMSettingConnection *s_con_a, *s_con_b; + guint64 time_a, time_b; + + s_con_a = nm_connection_get_setting_connection (nmta->conn); + s_con_b = nm_connection_get_setting_connection (nmtb->conn); + + time_a = nm_setting_connection_get_timestamp (s_con_a); + time_b = nm_setting_connection_get_timestamp (s_con_b); + + return (int) (time_b - time_a); + } + + /* If one is an NMConnection and the other is an NMAccessPoint, the + * connection comes first. + */ + if (nmta->conn) + return -1; + else if (nmtb->conn) + return 1; + + g_return_val_if_fail (nmta->ap && nmtb->ap, 0); + + /* If both are access points, then sort by strength */ + return nm_access_point_get_strength (nmtb->ap) - nm_access_point_get_strength (nmta->ap); +} + +static void +add_connections_for_device (NmtConnectDevice *nmtdev, + const GPtrArray *connections) +{ + int i; + + for (i = 0; i < connections->len; i++) { + NMConnection *conn = connections->pdata[i]; + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (conn); + if (nm_setting_connection_get_master (s_con)) + continue; + + if (nm_device_connection_valid (nmtdev->device, conn)) { + NmtConnectConnection *nmtconn = g_slice_new0 (NmtConnectConnection); + + nmtconn->name = nm_connection_get_id (conn); + nmtconn->device = nmtdev->device; + nmtconn->conn = g_object_ref (conn); + nmtdev->conns = g_slist_prepend (nmtdev->conns, nmtconn); + } + } +} + +/* stolen from nm-applet */ +static char * +hash_ap (NMAccessPoint *ap) +{ + unsigned char input[66]; + GBytes *ssid; + NM80211Mode mode; + guint32 flags; + guint32 wpa_flags; + guint32 rsn_flags; + + memset (&input[0], 0, sizeof (input)); + + ssid = nm_access_point_get_ssid (ap); + if (ssid) + memcpy (input, g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid)); + + mode = nm_access_point_get_mode (ap); + if (mode == NM_802_11_MODE_INFRA) + input[32] |= (1 << 0); + else if (mode == NM_802_11_MODE_ADHOC) + input[32] |= (1 << 1); + else + input[32] |= (1 << 2); + + /* Separate out no encryption, WEP-only, and WPA-capable */ + flags = nm_access_point_get_flags (ap); + wpa_flags = nm_access_point_get_wpa_flags (ap); + rsn_flags = nm_access_point_get_rsn_flags (ap); + if ( !(flags & NM_802_11_AP_FLAGS_PRIVACY) + && (wpa_flags == NM_802_11_AP_SEC_NONE) + && (rsn_flags == NM_802_11_AP_SEC_NONE)) + input[32] |= (1 << 3); + else if ( (flags & NM_802_11_AP_FLAGS_PRIVACY) + && (wpa_flags == NM_802_11_AP_SEC_NONE) + && (rsn_flags == NM_802_11_AP_SEC_NONE)) + input[32] |= (1 << 4); + else if ( !(flags & NM_802_11_AP_FLAGS_PRIVACY) + && (wpa_flags != NM_802_11_AP_SEC_NONE) + && (rsn_flags != NM_802_11_AP_SEC_NONE)) + input[32] |= (1 << 5); + else + input[32] |= (1 << 6); + + /* duplicate it */ + memcpy (&input[33], &input[0], 32); + return g_compute_checksum_for_data (G_CHECKSUM_MD5, input, sizeof (input)); +} + +static void +add_connections_for_aps (NmtConnectDevice *nmtdev, + const GPtrArray *connections) +{ + NmtConnectConnection *nmtconn; + NMConnection *conn; + NMAccessPoint *ap; + const GPtrArray *aps; + GHashTable *seen_ssids; + GBytes *ssid; + char *ap_hash; + int i, c; + + aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (nmtdev->device)); + if (!aps->len) + return; + + seen_ssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + for (i = 0; i < aps->len; i++) { + ap = aps->pdata[i]; + + if (!nm_access_point_get_ssid (ap)) + continue; + + ap_hash = hash_ap (ap); + if (g_hash_table_contains (seen_ssids, ap_hash)) { + g_free (ap_hash); + continue; + } + g_hash_table_add (seen_ssids, ap_hash); + + nmtconn = g_slice_new0 (NmtConnectConnection); + nmtconn->device = nmtdev->device; + nmtconn->ap = g_object_ref (ap); + ssid = nm_access_point_get_ssid (ap); + if (ssid) + nmtconn->ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + + for (c = 0; c < connections->len; c++) { + conn = connections->pdata[c]; + if ( nm_device_connection_valid (nmtdev->device, conn) + && nm_access_point_connection_valid (ap, conn)) { + nmtconn->name = nm_connection_get_id (conn); + nmtconn->conn = g_object_ref (conn); + break; + } + } + + if (!nmtconn->name) + nmtconn->name = nmtconn->ssid ? nmtconn->ssid : "<unknown>"; + + nmtdev->conns = g_slist_prepend (nmtdev->conns, nmtconn); + } + + g_hash_table_unref (seen_ssids); +} + +static GSList * +append_nmt_devices_for_devices (GSList *nmt_devices, + const GPtrArray *devices, + char **names, + const GPtrArray *connections) +{ + NmtConnectDevice *nmtdev; + NMDevice *device; + int i, sort_order; + + for (i = 0; i < devices->len; i++) { + device = devices->pdata[i]; + + sort_order = get_sort_order_for_device (device); + if (sort_order == -1) + continue; + + nmtdev = g_slice_new0 (NmtConnectDevice); + nmtdev->name = g_strdup (names[i]); + nmtdev->device = g_object_ref (device); + nmtdev->sort_order = sort_order; + + if (NM_IS_DEVICE_WIFI (device)) + add_connections_for_aps (nmtdev, connections); + else + add_connections_for_device (nmtdev, connections); + nmtdev->conns = g_slist_sort (nmtdev->conns, sort_connections); + + nmt_devices = g_slist_prepend (nmt_devices, nmtdev); + } + + return nmt_devices; +} + +static GSList * +append_nmt_devices_for_virtual_devices (GSList *nmt_devices, + const GPtrArray *connections) +{ + NmtConnectDevice *nmtdev = NULL; + int i; + GHashTable *devices_by_name; + char *name; + NMConnection *conn; + NmtConnectConnection *nmtconn; + int sort_order; + + devices_by_name = g_hash_table_new (g_str_hash, g_str_equal); + + for (i = 0; i < connections->len; i++) { + conn = connections->pdata[i]; + sort_order = get_sort_order_for_connection (conn); + if (sort_order == -1) + continue; + + name = nm_connection_get_virtual_device_description (conn); + if (name) + nmtdev = g_hash_table_lookup (devices_by_name, name); + if (nmtdev) + g_free (name); + else { + nmtdev = g_slice_new0 (NmtConnectDevice); + nmtdev->name = name ? name : g_strdup ("Unknown"); + nmtdev->sort_order = sort_order; + + g_hash_table_insert (devices_by_name, nmtdev->name, nmtdev); + nmt_devices = g_slist_prepend (nmt_devices, nmtdev); + } + + nmtconn = g_slice_new0 (NmtConnectConnection); + nmtconn->name = nm_connection_get_id (conn); + nmtconn->conn = g_object_ref (conn); + + nmtdev->conns = g_slist_insert_sorted (nmtdev->conns, nmtconn, sort_connections); + } + + g_hash_table_destroy (devices_by_name); + return nmt_devices; +} + +static GSList * +append_nmt_devices_for_vpns (GSList *nmt_devices, + const GPtrArray *connections) +{ + NmtConnectDevice *nmtdev; + int i; + NMConnection *conn; + NmtConnectConnection *nmtconn; + + nmtdev = g_slice_new0 (NmtConnectDevice); + nmtdev->name = g_strdup (_("VPN")); + nmtdev->sort_order = 100; + + for (i = 0; i < connections->len; i++) { + conn = connections->pdata[i]; + if (!nm_connection_is_type (conn, NM_SETTING_VPN_SETTING_NAME)) + continue; + + nmtconn = g_slice_new0 (NmtConnectConnection); + nmtconn->name = nm_connection_get_id (conn); + nmtconn->conn = g_object_ref (conn); + + nmtdev->conns = g_slist_insert_sorted (nmtdev->conns, nmtconn, sort_connections); + } + + if (nmtdev->conns) + nmt_devices = g_slist_prepend (nmt_devices, nmtdev); + else + nmt_connect_device_free (nmtdev); + + return nmt_devices; +} + +static int +sort_nmt_devices (gconstpointer a, + gconstpointer b) +{ + NmtConnectDevice *nmta = (NmtConnectDevice *)a; + NmtConnectDevice *nmtb = (NmtConnectDevice *)b; + + if (nmta->sort_order != nmtb->sort_order) + return nmta->sort_order - nmtb->sort_order; + + return strcmp (nmta->name, nmtb->name); +} + +static NMActiveConnection * +connection_find_ac (NMConnection *conn, + const GPtrArray *acs) +{ + NMActiveConnection *ac; + NMRemoteConnection *ac_conn; + int i; + + for (i = 0; i < acs->len; i++) { + ac = acs->pdata[i]; + ac_conn = nm_active_connection_get_connection (ac); + + if (conn == NM_CONNECTION (ac_conn)) + return ac; + } + + return NULL; +} + +static void +nmt_connect_connection_list_rebuild (NmtConnectConnectionList *list) +{ + NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (list); + NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (list); + const GPtrArray *devices, *acs, *connections; + int max_width; + char **names, *row, active_col; + const char *strength_col; + GSList *nmt_devices, *diter, *citer; + NmtConnectDevice *nmtdev; + NmtConnectConnection *nmtconn; + + g_slist_free_full (priv->nmt_devices, (GDestroyNotify) nmt_connect_device_free); + priv->nmt_devices = NULL; + nmt_newt_listbox_clear (listbox); + + devices = nm_client_get_devices (nm_client); + acs = nm_client_get_active_connections (nm_client); + connections = nm_client_get_connections (nm_client); + + nmt_devices = NULL; + + names = nm_device_disambiguate_names ((NMDevice **) devices->pdata, devices->len); + nmt_devices = append_nmt_devices_for_devices (nmt_devices, devices, names, connections); + g_strfreev (names); + + nmt_devices = append_nmt_devices_for_virtual_devices (nmt_devices, connections); + nmt_devices = append_nmt_devices_for_vpns (nmt_devices, connections); + + nmt_devices = g_slist_sort (nmt_devices, sort_nmt_devices); + + max_width = 0; + for (diter = nmt_devices; diter; diter = diter->next) { + nmtdev = diter->data; + for (citer = nmtdev->conns; citer; citer = citer->next) { + nmtconn = citer->data; + + max_width = MAX (max_width, nmt_newt_text_width (nmtconn->name)); + } + } + + for (diter = nmt_devices; diter; diter = diter->next) { + nmtdev = diter->data; + + if (diter != nmt_devices) + nmt_newt_listbox_append (listbox, "", NULL); + nmt_newt_listbox_append (listbox, nmtdev->name, NULL); + + for (citer = nmtdev->conns; citer; citer = citer->next) { + nmtconn = citer->data; + + if (nmtconn->conn) + nmtconn->active = connection_find_ac (nmtconn->conn, acs); + if (nmtconn->active) { + g_object_ref (nmtconn->active); + active_col = '*'; + } else + active_col = ' '; + + if (nmtconn->ap) { + guint8 strength = nm_access_point_get_strength (nmtconn->ap); + + strength_col = nm_utils_wifi_strength_bars (strength); + } else + strength_col = NULL; + + row = g_strdup_printf ("%c %s%-*s%s%s", + active_col, + nmtconn->name, + (int)(max_width - nmt_newt_text_width (nmtconn->name)), "", + strength_col ? " " : "", + strength_col ? strength_col : ""); + + nmt_newt_listbox_append (listbox, row, nmtconn); + g_free (row); + } + } + + priv->nmt_devices = nmt_devices; + + g_object_notify (G_OBJECT (listbox), "active"); + g_object_notify (G_OBJECT (listbox), "active-key"); +} + +static void +rebuild_on_property_changed (GObject *object, + GParamSpec *spec, + gpointer list) +{ + nmt_connect_connection_list_rebuild (list); +} + +static void +nmt_connect_connection_list_constructed (GObject *object) +{ + NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST (object); + + g_signal_connect (nm_client, "notify::" NM_CLIENT_ACTIVE_CONNECTIONS, + G_CALLBACK (rebuild_on_property_changed), list); + g_signal_connect (nm_client, "notify::" NM_CLIENT_CONNECTIONS, + G_CALLBACK (rebuild_on_property_changed), list); + g_signal_connect (nm_client, "notify::" NM_CLIENT_DEVICES, + G_CALLBACK (rebuild_on_property_changed), list); + + nmt_connect_connection_list_rebuild (list); + + G_OBJECT_CLASS (nmt_connect_connection_list_parent_class)->constructed (object); +} + +static void +nmt_connect_connection_list_finalize (GObject *object) +{ + NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (object); + + g_slist_free_full (priv->nmt_devices, (GDestroyNotify) nmt_connect_device_free); + + g_signal_handlers_disconnect_by_func (nm_client, G_CALLBACK (rebuild_on_property_changed), object); + + G_OBJECT_CLASS (nmt_connect_connection_list_parent_class)->finalize (object); +} + +static void +nmt_connect_connection_list_class_init (NmtConnectConnectionListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (list_class); + + g_type_class_add_private (list_class, sizeof (NmtConnectConnectionListPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_connect_connection_list_constructed; + object_class->finalize = nmt_connect_connection_list_finalize; +} + +/** + * nmt_connect_connection_list_get_connection: + * @list: an #NmtConnectConnectionList + * @identifier: a connection ID or UUID, or device name + * @connection: (out) (transfer none): the #NMConnection to be activated + * @device: (out) (transfer none): the #NMDevice to activate @connection on + * @specific_object: (out) (transfer none): the "specific object" to connect to + * @active: (out) (transfer none): the #NMActiveConnection corresponding + * to the selection, if any. + * + * Gets information about the indicated connection. + * + * Returns: %TRUE if there was a match, %FALSE if not. + */ +gboolean +nmt_connect_connection_list_get_connection (NmtConnectConnectionList *list, + const char *identifier, + NMConnection **connection, + NMDevice **device, + NMObject **specific_object, + NMActiveConnection **active) +{ + NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (list); + GSList *diter, *citer; + NmtConnectDevice *nmtdev; + NmtConnectConnection *nmtconn = NULL; + NMConnection *conn = NULL; + + g_return_val_if_fail (identifier, FALSE); + + if (nm_utils_is_uuid (identifier)) + conn = NM_CONNECTION (nm_client_get_connection_by_uuid (nm_client, identifier)); + if (!conn) + conn = NM_CONNECTION (nm_client_get_connection_by_id (nm_client, identifier)); + + for (diter = priv->nmt_devices; diter; diter = diter->next) { + nmtdev = diter->data; + if (!nmtdev->conns) + continue; + + for (citer = nmtdev->conns; citer; citer = citer->next) { + nmtconn = citer->data; + if (conn) { + if (conn == nmtconn->conn) + goto found; + } else if (nmtconn->ssid && !strcmp (identifier, nmtconn->ssid)) + goto found; + } + + if (!conn && nmtdev->device && !strcmp (identifier, nm_device_get_ip_iface (nmtdev->device))) { + nmtconn = nmtdev->conns->data; + goto found; + } + } + + return FALSE; + + found: + if (connection) + *connection = nmtconn->conn; + if (device) + *device = nmtconn->device; + if (specific_object) + *specific_object = NM_OBJECT (nmtconn->ap); + if (active) + *active = nmtconn->active; + + return TRUE; +} + +/** + * nmt_connect_connection_list_get_selection: + * @list: an #NmtConnectConnectionList + * @connection: (out) (transfer none): the #NMConnection to be activated + * @device: (out) (transfer none): the #NMDevice to activate @connection on + * @specific_object: (out) (transfer none): the "specific object" to connect to + * @active: (out) (transfer none): the #NMActiveConnection corresponding + * to the selection, if any. + * + * Gets information about the selected row. + * + * Returns: %TRUE if there is a selection, %FALSE if not. + */ +gboolean +nmt_connect_connection_list_get_selection (NmtConnectConnectionList *list, + NMConnection **connection, + NMDevice **device, + NMObject **specific_object, + NMActiveConnection **active) +{ + NmtConnectConnection *nmtconn; + + nmtconn = nmt_newt_listbox_get_active_key (NMT_NEWT_LISTBOX (list)); + if (!nmtconn) + return FALSE; + + if (connection) + *connection = nmtconn->conn; + if (device) + *device = nmtconn->device; + if (specific_object) + *specific_object = NM_OBJECT (nmtconn->ap); + if (active) + *active = nmtconn->active; + + return TRUE; +} diff --git a/clients/tui/nmt-connect-connection-list.h b/clients/tui/nmt-connect-connection-list.h new file mode 100644 index 00000000..d4790043 --- /dev/null +++ b/clients/tui/nmt-connect-connection-list.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_CONNECT_CONNECTION_LIST_H +#define NMT_CONNECT_CONNECTION_LIST_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_CONNECT_CONNECTION_LIST (nmt_connect_connection_list_get_type ()) +#define NMT_CONNECT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionList)) +#define NMT_CONNECT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListClass)) +#define NMT_IS_CONNECT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST)) +#define NMT_IS_CONNECT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_CONNECT_CONNECTION_LIST)) +#define NMT_CONNECT_CONNECTION_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListClass)) + +typedef struct { + NmtNewtListbox parent; + +} NmtConnectConnectionList; + +typedef struct { + NmtNewtListboxClass parent; + +} NmtConnectConnectionListClass; + +GType nmt_connect_connection_list_get_type (void); + +NmtNewtWidget *nmt_connect_connection_list_new (void); + +gboolean nmt_connect_connection_list_get_connection (NmtConnectConnectionList *list, + const char *identifier, + NMConnection **connection, + NMDevice **device, + NMObject **specific_object, + NMActiveConnection **active); +gboolean nmt_connect_connection_list_get_selection (NmtConnectConnectionList *list, + NMConnection **connection, + NMDevice **device, + NMObject **specific_object, + NMActiveConnection **active); + +G_END_DECLS + +#endif /* NMT_CONNECT_CONNECTION_LIST_H */ diff --git a/clients/tui/nmt-device-entry.c b/clients/tui/nmt-device-entry.c new file mode 100644 index 00000000..2af5e584 --- /dev/null +++ b/clients/tui/nmt-device-entry.c @@ -0,0 +1,573 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-device-entry + * @short_description: #NmtNewtEntry for identifying a device + * + * #NmtDeviceEntry provides a widget for identifying a device, either + * by interface name or by hardware address. The user can enter either + * value, and the entry's #NmtDeviceEntry:interface-name or + * #NmtDeviceEntry:mac-address property will be set accordingly. If + * the entry recognizes the interface name or mac address typed in as + * matching a known #NMDevice, then it will also display the other + * property in parentheses. + * + * FIXME: #NmtDeviceEntry is currently an #NmtEditorGrid object, so that + * we can possibly eventually add a button to its "extra" field, that + * would pop up a form for selecting a device. But if we're not going + * to implement that then we should make it just an #NmtNewtEntry. + */ + +#include "config.h" + +#include <string.h> +#include <sys/socket.h> +#include <linux/if_arp.h> + +#include <glib/gi18n-lib.h> +#include <NetworkManager.h> + +#include "nmtui.h" +#include "nmt-device-entry.h" + +G_DEFINE_TYPE (NmtDeviceEntry, nmt_device_entry, NMT_TYPE_EDITOR_GRID) + +#define NMT_DEVICE_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryPrivate)) + +typedef struct { + GType hardware_type; + NmtDeviceEntryDeviceFilter device_filter; + gpointer device_filter_data; + int arptype; + + char *interface_name; + char *mac_address; + + char *label; + NmtNewtEntry *entry; + NmtNewtWidget *button; + + gboolean updating; +} NmtDeviceEntryPrivate; + +enum { + PROP_0, + PROP_LABEL, + PROP_WIDTH, + PROP_HARDWARE_TYPE, + PROP_INTERFACE_NAME, + PROP_MAC_ADDRESS, + + LAST_PROP +}; + +/** + * nmt_device_entry_new: + * @label: the label for the entry + * @width: the width of the entry + * @hardware_type: the type of #NMDevice to be selected, or + * %G_TYPE_NONE if this is for a virtual device type. + * + * Creates a new #NmtDeviceEntry, for identifying a device of type + * @hardware_type. If @hardware_type is %G_TYPE_NONE (and you do not + * set a #NmtDeviceEntryDeviceFilter), then this will only allow + * specifying an interface name, not a hardware address. + * + * Returns: a new #NmtDeviceEntry. + */ +NmtNewtWidget * +nmt_device_entry_new (const char *label, + int width, + GType hardware_type) +{ + return g_object_new (NMT_TYPE_DEVICE_ENTRY, + "label", label, + "width", width, + "hardware-type", hardware_type, + NULL); +} + +static gboolean +device_entry_parse (NmtDeviceEntry *deventry, + const char *text, + char **interface_name, + char **mac_address) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + char **words; + int len; + + *interface_name = *mac_address = NULL; + if (!*text) + return TRUE; + + if (priv->hardware_type == G_TYPE_NONE && !priv->device_filter) { + if (nm_utils_iface_valid_name (text)) { + *interface_name = g_strdup (text); + return TRUE; + } else + return FALSE; + } + + words = g_strsplit (text, " ", -1); + if (g_strv_length (words) > 2) { + g_strfreev (words); + return FALSE; + } + + if (words[1]) { + len = strlen (words[1]); + if (len < 3 || words[1][0] != '(' || words[1][len - 1] != ')') + goto fail; + + memmove (words[1], words[1] + 1, len - 2); + words[1][len - 2] = '\0'; + } + + len = nm_utils_hwaddr_len (priv->arptype); + if ( nm_utils_hwaddr_aton (words[0], buf, len) + && (!words[1] || nm_utils_iface_valid_name (words[1]))) { + *mac_address = words[0]; + *interface_name = NULL; + g_free (words); + return TRUE; + } else if ( nm_utils_iface_valid_name (words[0]) + && (!words[1] || nm_utils_hwaddr_aton (words[1], buf, len))) { + *interface_name = words[0]; + *mac_address = NULL; + g_free (words); + return TRUE; + } + + fail: + g_strfreev (words); + return FALSE; +} + +static gboolean +device_entry_validate (NmtNewtEntry *entry, + const char *text, + gpointer user_data) +{ + NmtDeviceEntry *deventry = user_data; + char *ifname, *mac; + + if (!device_entry_parse (deventry, text, &ifname, &mac)) + return FALSE; + + g_free (ifname); + g_free (mac); + return TRUE; +} + +static NMDevice * +find_device_by_interface_name (NmtDeviceEntry *deventry, + const char *interface_name) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + const GPtrArray *devices; + NMDevice *device = NULL; + int i; + + devices = nm_client_get_devices (nm_client); + for (i = 0; i < devices->len && !device; i++) { + NMDevice *candidate = devices->pdata[i]; + + if ( priv->hardware_type != G_TYPE_NONE + && !G_TYPE_CHECK_INSTANCE_TYPE (candidate, priv->hardware_type)) + continue; + + if ( priv->device_filter + && !priv->device_filter (deventry, candidate, priv->device_filter_data)) + continue; + + if (!g_strcmp0 (interface_name, nm_device_get_iface (candidate))) + device = candidate; + } + + return device; +} + +static NMDevice * +find_device_by_mac_address (NmtDeviceEntry *deventry, + const char *mac_address) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + const GPtrArray *devices; + NMDevice *device = NULL; + int i; + + devices = nm_client_get_devices (nm_client); + for (i = 0; i < devices->len && !device; i++) { + NMDevice *candidate = devices->pdata[i]; + char *hwaddr; + + if ( priv->hardware_type != G_TYPE_NONE + && !G_TYPE_CHECK_INSTANCE_TYPE (candidate, priv->hardware_type)) + continue; + + if ( priv->device_filter + && !priv->device_filter (deventry, candidate, priv->device_filter_data)) + continue; + + g_object_get (G_OBJECT (candidate), "hw-address", &hwaddr, NULL); + if (hwaddr && !g_ascii_strcasecmp (mac_address, hwaddr)) + device = candidate; + g_free (hwaddr); + } + + return device; +} + +static void +update_entry (NmtDeviceEntry *deventry) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + const char *ifname; + char *mac, *text; + NMDevice *ifname_device, *mac_device; + + if (priv->interface_name) { + ifname = priv->interface_name; + ifname_device = find_device_by_interface_name (deventry, priv->interface_name); + } else { + ifname = NULL; + ifname_device = NULL; + } + + if (priv->mac_address) { + mac = g_strdup (priv->mac_address); + mac_device = find_device_by_mac_address (deventry, priv->mac_address); + } else { + mac = NULL; + mac_device = NULL; + } + + if (!ifname && mac_device) + ifname = nm_device_get_iface (mac_device); + if (!mac && ifname_device && (priv->hardware_type != G_TYPE_NONE)) + g_object_get (G_OBJECT (ifname_device), "hw-address", &mac, NULL); + + if (ifname_device && mac_device && ifname_device != mac_device) { + /* Mismatch! */ + text = g_strdup_printf ("%s != %s", priv->interface_name, mac); + } else if (ifname && mac) { + if (ifname_device) + text = g_strdup_printf ("%s (%s)", ifname, mac); + else + text = g_strdup_printf ("%s (%s)", mac, ifname); + } else if (ifname) + text = g_strdup (ifname); + else if (mac) + text = g_strdup (mac); + else + text = g_strdup (""); + + priv->updating = TRUE; + g_object_set (G_OBJECT (priv->entry), "text", text, NULL); + priv->updating = FALSE; + g_free (text); + + g_free (mac); +} + +static gboolean +nmt_device_entry_set_interface_name (NmtDeviceEntry *deventry, + const char *interface_name) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + + if (g_strcmp0 (interface_name, priv->interface_name) != 0) { + g_free (priv->interface_name); + priv->interface_name = g_strdup (interface_name); + + g_object_notify (G_OBJECT (deventry), "interface-name"); + return TRUE; + } else + return FALSE; +} + +static gboolean +nmt_device_entry_set_mac_address (NmtDeviceEntry *deventry, + const char *mac_address) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + gboolean changed; + + if (mac_address && !priv->mac_address) { + priv->mac_address = g_strdup (mac_address); + changed = TRUE; + } else if (!mac_address && priv->mac_address) { + g_clear_pointer (&priv->mac_address, g_free); + changed = TRUE; + } else if ( mac_address && priv->mac_address + && !nm_utils_hwaddr_matches (mac_address, -1, priv->mac_address, -1)) { + g_free (priv->mac_address); + priv->mac_address = g_strdup (mac_address); + changed = TRUE; + } else + changed = FALSE; + + if (changed) + g_object_notify (G_OBJECT (deventry), "mac-address"); + return changed; +} + +static void +entry_text_changed (GObject *object, + GParamSpec *pspec, + gpointer deventry) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + const char *text; + char *ifname, *mac; + + if (priv->updating) + return; + + text = nmt_newt_entry_get_text (priv->entry); + if (!device_entry_parse (deventry, text, &ifname, &mac)) + return; + + nmt_device_entry_set_interface_name (deventry, ifname); + g_free (ifname); + + nmt_device_entry_set_mac_address (deventry, mac); + g_free (mac); +} + +static void +nmt_device_entry_init (NmtDeviceEntry *deventry) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + NmtNewtWidget *entry; + + priv->hardware_type = G_TYPE_NONE; + + entry = nmt_newt_entry_new (-1, 0); + priv->entry = NMT_NEWT_ENTRY (entry); + nmt_newt_entry_set_validator (priv->entry, device_entry_validate, deventry); + g_signal_connect (priv->entry, "notify::text", + G_CALLBACK (entry_text_changed), deventry); + +#if 0 + priv->button = nmt_newt_button_new (_("Select...")); + g_signal_connect (priv->button, "clicked", + G_CALLBACK (do_select_dialog), deventry); +#endif +} + +static void +nmt_device_entry_constructed (GObject *object) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object); + + nmt_editor_grid_append (NMT_EDITOR_GRID (object), priv->label, NMT_NEWT_WIDGET (priv->entry), NULL); + + G_OBJECT_CLASS (nmt_device_entry_parent_class)->constructed (object); +} + +static void +nmt_device_entry_finalize (GObject *object) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object); + + g_free (priv->interface_name); + g_free (priv->mac_address); + + G_OBJECT_CLASS (nmt_device_entry_parent_class)->finalize (object); +} + +/** + * NmtDeviceEntryDeviceFilter: + * @deventry: the #NmtDeviceEntry + * @device: an #NMDevice + * @user_data: user data + * + * Filter function for determining which devices can be specified + * on an entry. + * + * Returns: %TRUE if @device is acceptable for @deventry + */ + +/** + * nmt_device_entry_set_device_filter: + * @deventry: the #NmtDeviceEntry + * @filter: the filter + * @user_data: data for @filter + * + * Sets a device filter on @deventry. Only devices that pass @filter + * will be recognized by @deventry. + * + * If the entry's #NmtDeviceEntry:hardware-type is not %G_TYPE_NONE, + * then only devices that both match the hardware type and are + * accepted by the filter will be allowed. + */ +void +nmt_device_entry_set_device_filter (NmtDeviceEntry *deventry, + NmtDeviceEntryDeviceFilter filter, + gpointer user_data) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + + priv->device_filter = filter; + priv->device_filter_data = user_data; +} + +static void +nmt_device_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtDeviceEntry *deventry = NMT_DEVICE_ENTRY (object); + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + const char *interface_name; + const char *mac_address; + + switch (prop_id) { + case PROP_LABEL: + priv->label = g_value_dup_string (value); + break; + case PROP_WIDTH: + nmt_newt_entry_set_width (priv->entry, g_value_get_int (value)); + break; + case PROP_HARDWARE_TYPE: + priv->hardware_type = g_value_get_gtype (value); + priv->arptype = (priv->hardware_type == NM_TYPE_DEVICE_INFINIBAND) ? ARPHRD_INFINIBAND : ARPHRD_ETHER; + break; + case PROP_INTERFACE_NAME: + interface_name = g_value_get_string (value); + if (nmt_device_entry_set_interface_name (deventry, interface_name)) + update_entry (deventry); + break; + case PROP_MAC_ADDRESS: + mac_address = g_value_get_string (value); + if (nmt_device_entry_set_mac_address (deventry, mac_address)) + update_entry (deventry); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_device_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LABEL: + g_value_set_string (value, priv->label); + break; + case PROP_WIDTH: + g_value_set_int (value, nmt_newt_entry_get_width (priv->entry)); + break; + case PROP_HARDWARE_TYPE: + g_value_set_gtype (value, priv->hardware_type); + break; + case PROP_INTERFACE_NAME: + g_value_set_string (value, priv->interface_name); + break; + case PROP_MAC_ADDRESS: + g_value_set_string (value, priv->mac_address); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_device_entry_class_init (NmtDeviceEntryClass *deventry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (deventry_class); + + g_type_class_add_private (deventry_class, sizeof (NmtDeviceEntryPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_device_entry_constructed; + object_class->set_property = nmt_device_entry_set_property; + object_class->get_property = nmt_device_entry_get_property; + object_class->finalize = nmt_device_entry_finalize; + + /** + * NmtDeviceEntry:label: + * + * The entry's label + */ + g_object_class_install_property + (object_class, PROP_LABEL, + g_param_spec_string ("label", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:width: + * + * The entry's width in characters + */ + g_object_class_install_property + (object_class, PROP_WIDTH, + g_param_spec_int ("width", "", "", + -1, 80, -1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:hardware-type: + * + * The type of #NMDevice to limit the entry to, or %G_TYPE_NONE + * if the entry is for a virtual device and should not accept + * hardware addresses. + */ + g_object_class_install_property + (object_class, PROP_HARDWARE_TYPE, + g_param_spec_gtype ("hardware-type", "", "", + G_TYPE_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:interface-name: + * + * The interface name of the device identified by the entry. + */ + g_object_class_install_property + (object_class, PROP_INTERFACE_NAME, + g_param_spec_string ("interface-name", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:mac-address: + * + * The hardware address of the device identified by the entry. + */ + g_object_class_install_property + (object_class, PROP_MAC_ADDRESS, + g_param_spec_string ("mac-address", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-device-entry.h b/clients/tui/nmt-device-entry.h new file mode 100644 index 00000000..05d5d40f --- /dev/null +++ b/clients/tui/nmt-device-entry.h @@ -0,0 +1,60 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_DEVICE_ENTRY_H +#define NMT_DEVICE_ENTRY_H + +#include "nmt-editor-grid.h" + +#include <NetworkManager.h> + +G_BEGIN_DECLS + +#define NMT_TYPE_DEVICE_ENTRY (nmt_device_entry_get_type ()) +#define NMT_DEVICE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntry)) +#define NMT_DEVICE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryClass)) +#define NMT_IS_DEVICE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_DEVICE_ENTRY)) +#define NMT_IS_DEVICE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_DEVICE_ENTRY)) +#define NMT_DEVICE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryClass)) + +typedef struct { + NmtEditorGrid parent; + +} NmtDeviceEntry; + +typedef struct { + NmtEditorGridClass parent; + +} NmtDeviceEntryClass; + +GType nmt_device_entry_get_type (void); + +NmtNewtWidget *nmt_device_entry_new (const char *label, + int width, + GType hardware_type); + +typedef gboolean (*NmtDeviceEntryDeviceFilter) (NmtDeviceEntry *deventry, + NMDevice *device, + gpointer user_data); +void nmt_device_entry_set_device_filter (NmtDeviceEntry *deventry, + NmtDeviceEntryDeviceFilter filter, + gpointer user_data); + +G_END_DECLS + +#endif /* NMT_DEVICE_ENTRY_H */ diff --git a/clients/tui/nmt-edit-connection-list.c b/clients/tui/nmt-edit-connection-list.c new file mode 100644 index 00000000..e1b982dd --- /dev/null +++ b/clients/tui/nmt-edit-connection-list.c @@ -0,0 +1,554 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-edit-connection-list + * @short_description: Connection list for "nmtui edit" + * + * #NmtEditConnectionList is the list of connections displayed by + * "nmtui edit". + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmtui.h" +#include "nmtui-edit.h" +#include "nmt-edit-connection-list.h" +#include "nmt-editor.h" + +#include "nm-editor-utils.h" + +G_DEFINE_TYPE (NmtEditConnectionList, nmt_edit_connection_list, NMT_TYPE_NEWT_GRID) + +#define NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListPrivate)) + +typedef struct { + GSList *connections; + + gboolean grouped; + NmtEditConnectionListFilter connection_filter; + gpointer connection_filter_data; + + NmtNewtListbox *listbox; + NmtNewtButtonBox *buttons; + + NmtNewtWidget *add; + NmtNewtWidget *edit; + NmtNewtWidget *delete; + NmtNewtWidget *extra; +} NmtEditConnectionListPrivate; + +enum { + PROP_0, + + PROP_GROUPED, + PROP_CONNECTION_FILTER, + PROP_CONNECTION_FILTER_DATA, + PROP_EXTRA_WIDGET, + PROP_CONNECTIONS, + PROP_NUM_CONNECTIONS, + + LAST_PROP +}; + +enum { + ADD_CONNECTION, + EDIT_CONNECTION, + REMOVE_CONNECTION, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void add_clicked (NmtNewtButton *button, gpointer list); +static void edit_clicked (NmtNewtButton *button, gpointer list); +static void delete_clicked (NmtNewtButton *button, gpointer list); +static void listbox_activated (NmtNewtWidget *listbox, gpointer list); + +static void +nmt_edit_connection_list_init (NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + NmtNewtWidget *listbox, *buttons; + NmtNewtGrid *grid = NMT_NEWT_GRID (list); + + listbox = g_object_new (NMT_TYPE_NEWT_LISTBOX, + "flags", NMT_NEWT_LISTBOX_SCROLL | NMT_NEWT_LISTBOX_BORDER, + "skip-null-keys", TRUE, + NULL); + priv->listbox = NMT_NEWT_LISTBOX (listbox); + nmt_newt_grid_add (grid, listbox, 0, 0); + nmt_newt_grid_set_flags (grid, listbox, + NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | + NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_EXPAND_Y); + g_signal_connect (priv->listbox, "activated", G_CALLBACK (listbox_activated), list); + + buttons = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_VERTICAL); + priv->buttons = NMT_NEWT_BUTTON_BOX (buttons); + nmt_newt_grid_add (grid, buttons, 1, 0); + nmt_newt_widget_set_padding (buttons, 1, 1, 0, 1); + nmt_newt_grid_set_flags (grid, buttons, + NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | + NMT_NEWT_GRID_EXPAND_Y); + + priv->add = nmt_newt_button_box_add_start (priv->buttons, _("Add")); + g_signal_connect (priv->add, "clicked", G_CALLBACK (add_clicked), list); + + priv->edit = nmt_newt_button_box_add_start (priv->buttons, _("Edit...")); + g_signal_connect (priv->edit, "clicked", G_CALLBACK (edit_clicked), list); + + priv->delete = nmt_newt_button_box_add_start (priv->buttons, _("Delete")); + g_signal_connect (priv->delete, "clicked", G_CALLBACK (delete_clicked), list); +} + +static int +sort_by_timestamp (gconstpointer a, + gconstpointer b) +{ + NMSettingConnection *s_con_a, *s_con_b; + guint64 time_a, time_b; + + s_con_a = nm_connection_get_setting_connection ((NMConnection *) a); + s_con_b = nm_connection_get_setting_connection ((NMConnection *) b); + + time_a = nm_setting_connection_get_timestamp (s_con_a); + time_b = nm_setting_connection_get_timestamp (s_con_b); + + return (int) (time_b - time_a); +} + +static void nmt_edit_connection_list_rebuild (NmtEditConnectionList *list); + +static void +rebuild_on_connection_changed (NMRemoteConnection *connection, + gpointer list) +{ + nmt_edit_connection_list_rebuild (list); +} + +static void +free_connections (NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + NMConnection *conn; + GSList *iter; + + for (iter = priv->connections; iter; iter = iter->next) { + conn = iter->data; + + g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (rebuild_on_connection_changed), list); + g_object_unref (conn); + } + g_slist_free (priv->connections); + priv->connections = NULL; +} + +static void +nmt_edit_connection_list_rebuild (NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + const GPtrArray *connections; + GSList *iter; + gboolean did_header = FALSE, did_vpn = FALSE; + NMEditorConnectionTypeData **types; + NMConnection *conn, *selected_conn; + int i, row, selected_row; + + selected_row = nmt_newt_listbox_get_active (priv->listbox); + selected_conn = nmt_newt_listbox_get_active_key (priv->listbox); + + free_connections (list); + connections = nm_client_get_connections (nm_client); + for (i = 0; i < connections->len; i++) { + conn = connections->pdata[i]; + + if ( priv->connection_filter + && !priv->connection_filter (list, conn, priv->connection_filter_data)) + continue; + + g_signal_connect (conn, NM_CONNECTION_CHANGED, + G_CALLBACK (rebuild_on_connection_changed), list); + priv->connections = g_slist_prepend (priv->connections, g_object_ref (conn)); + } + priv->connections = g_slist_sort (priv->connections, sort_by_timestamp); + g_object_notify (G_OBJECT (list), "connections"); + g_object_notify (G_OBJECT (list), "num-connections"); + + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->edit), + priv->connections != NULL); + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->delete), + priv->connections != NULL); + + nmt_newt_listbox_clear (priv->listbox); + + if (!priv->grouped) { + /* Just add the connections in order */ + for (iter = priv->connections, row = 0; iter; iter = iter->next, row++) { + conn = iter->data; + nmt_newt_listbox_append (priv->listbox, nm_connection_get_id (conn), conn); + if (conn == selected_conn) + selected_row = row; + } + if (selected_row >= row) + selected_row = row - 1; + nmt_newt_listbox_set_active (priv->listbox, selected_row); + + return; + } + + types = nm_editor_utils_get_connection_type_list (); + for (i = row = 0; types[i]; i++) { + if (types[i]->setting_type == NM_TYPE_SETTING_VPN) { + if (did_vpn) + continue; + did_vpn = TRUE; + } + + did_header = FALSE; + + for (iter = priv->connections; iter; iter = iter->next) { + NMSetting *setting; + char *indented; + + conn = iter->data; + setting = nm_connection_get_setting (conn, types[i]->setting_type); + if (!setting) + continue; + if (!nm_connection_is_type (conn, nm_setting_get_name (setting))) + continue; + + if (!did_header) { + nmt_newt_listbox_append (priv->listbox, types[i]->name, NULL); + if (row == selected_row) + selected_row++; + row++; + did_header = TRUE; + } + + indented = g_strdup_printf (" %s", nm_connection_get_id (conn)); + nmt_newt_listbox_append (priv->listbox, indented, conn); + g_free (indented); + + if (conn == selected_conn) + selected_row = row; + row++; + } + } + + if (selected_row >= row) + selected_row = row - 1; + nmt_newt_listbox_set_active (priv->listbox, selected_row); +} + +static void +rebuild_on_connections_changed (GObject *object, + GParamSpec *pspec, + gpointer list) +{ + nmt_edit_connection_list_rebuild (list); +} + +static void +nmt_edit_connection_list_constructed (GObject *object) +{ + NmtEditConnectionList *list = NMT_EDIT_CONNECTION_LIST (object); + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + + if (priv->extra) + nmt_newt_button_box_add_widget_end (priv->buttons, priv->extra); + + g_signal_connect (nm_client, "notify::" NM_CLIENT_CONNECTIONS, + G_CALLBACK (rebuild_on_connections_changed), list); + + nmt_edit_connection_list_rebuild (list); + + G_OBJECT_CLASS (nmt_edit_connection_list_parent_class)->constructed (object); +} + +static void +add_clicked (NmtNewtButton *button, gpointer list) +{ + g_signal_emit (list, signals[ADD_CONNECTION], 0); +} + +static void +edit_clicked (NmtNewtButton *button, gpointer list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + NMConnection *connection; + + connection = nmt_newt_listbox_get_active_key (priv->listbox); + g_return_if_fail (connection != NULL); + + g_signal_emit (list, signals[EDIT_CONNECTION], 0, connection); +} + +static void +delete_clicked (NmtNewtButton *button, gpointer list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + NMRemoteConnection *connection; + + connection = nmt_newt_listbox_get_active_key (priv->listbox); + g_return_if_fail (connection != NULL); + + g_signal_emit (list, signals[REMOVE_CONNECTION], 0, connection); +} + +static void +listbox_activated (NmtNewtWidget *listbox, gpointer list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + + edit_clicked (NMT_NEWT_BUTTON (priv->edit), list); +} + +static void +nmt_edit_connection_list_finalize (GObject *object) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object); + + free_connections (NMT_EDIT_CONNECTION_LIST (object)); + g_clear_object (&priv->extra); + + G_OBJECT_CLASS (nmt_edit_connection_list_parent_class)->finalize (object); +} + +static void +nmt_edit_connection_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_GROUPED: + priv->grouped = g_value_get_boolean (value); + break; + case PROP_CONNECTION_FILTER: + priv->connection_filter = g_value_get_pointer (value); + break; + case PROP_CONNECTION_FILTER_DATA: + priv->connection_filter_data = g_value_get_pointer (value); + break; + case PROP_EXTRA_WIDGET: + priv->extra = g_value_get_object (value); + if (priv->extra) + g_object_ref_sink (priv->extra); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_edit_connection_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object); + GPtrArray *connections; + GSList *iter; + + switch (prop_id) { + case PROP_GROUPED: + g_value_set_boolean (value, priv->grouped); + break; + case PROP_CONNECTION_FILTER: + g_value_set_pointer (value, priv->connection_filter); + break; + case PROP_CONNECTION_FILTER_DATA: + g_value_set_pointer (value, priv->connection_filter_data); + break; + case PROP_EXTRA_WIDGET: + g_value_set_object (value, priv->extra); + break; + case PROP_CONNECTIONS: + connections = g_ptr_array_new_with_free_func (g_object_unref); + for (iter = priv->connections; iter; iter = iter->next) + g_ptr_array_add (connections, g_object_ref (iter->data)); + g_value_take_boxed (value, connections); + break; + case PROP_NUM_CONNECTIONS: + g_value_set_int (value, g_slist_length (priv->connections)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_edit_connection_list_class_init (NmtEditConnectionListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (list_class); + + g_type_class_add_private (list_class, sizeof (NmtEditConnectionListPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_edit_connection_list_constructed; + object_class->set_property = nmt_edit_connection_list_set_property; + object_class->get_property = nmt_edit_connection_list_get_property; + object_class->finalize = nmt_edit_connection_list_finalize; + + /* signals */ + + /** + * NmtEditConnectionList::add-connection: + * @list: the #NmtEditConnectionList + * + * Emitted when the user clicks the list's "Add" button. + */ + signals[ADD_CONNECTION] = + g_signal_new ("add-connection", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NmtEditConnectionListClass, add_connection), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /** + * NmtEditConnectionList::edit-connection: + * @list: the #NmtEditConnectionList + * @connection: the connection to edit + * + * Emitted when the user clicks the list's "Edit" button, or + * hits "Return" on the listbox. + */ + signals[EDIT_CONNECTION] = + g_signal_new ("edit-connection", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NmtEditConnectionListClass, edit_connection), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + NM_TYPE_CONNECTION); + + /** + * NmtEditConnectionList::remove-connection: + * @list: the #NmtEditConnectionList + * @connection: the connection to remove + * + * Emitted when the user clicks the list's "Delete" button. + */ + signals[REMOVE_CONNECTION] = + g_signal_new ("remove-connection", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NmtEditConnectionListClass, remove_connection), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + NM_TYPE_CONNECTION); + + /* properties */ + + /** + * NmtEditConnectionList:grouped: + * + * If %TRUE, connections should be grouped by type, with headers + * indicating the types (as in the main connection list). If %FALSE, + * they will not be grouped (as in slave connection lists). + */ + g_object_class_install_property + (object_class, PROP_GROUPED, + g_param_spec_boolean ("grouped", "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionListFilter: + * @list: the #NmtEditConnectionList + * @connection: an #NMConnection + * @user_data: the user data + * + * Decides whether @connection should be displayed in @list. + * + * Returns: %TRUE or %FALSE + */ + /** + * NmtEditConnectionList:connection-filter: + * + * A callback function for filtering which connections appear in + * the list. + */ + g_object_class_install_property + (object_class, PROP_CONNECTION_FILTER, + g_param_spec_pointer ("connection-filter", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtEditConnectionList:connection-filter-data: + * + * Data for the #NmtEditConnectionList:connection-filter. + */ + g_object_class_install_property + (object_class, PROP_CONNECTION_FILTER_DATA, + g_param_spec_pointer ("connection-filter-data", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionList:extra-widget: + * + * An extra button widget to display at the bottom of the button + * box. + */ + g_object_class_install_property + (object_class, PROP_EXTRA_WIDGET, + g_param_spec_object ("extra-widget", "", "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionList:connections: + * + * The list of connections in the widget. + * + * Element-Type: #NMConnection + */ + g_object_class_install_property + (object_class, PROP_CONNECTIONS, + g_param_spec_boxed ("connections", "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionList:num-connections: + * + * The number of connections in the widget. + */ + g_object_class_install_property + (object_class, PROP_NUM_CONNECTIONS, + g_param_spec_int ("num-connections", "", "", + 0, G_MAXINT, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-edit-connection-list.h b/clients/tui/nmt-edit-connection-list.h new file mode 100644 index 00000000..c31c0a8c --- /dev/null +++ b/clients/tui/nmt-edit-connection-list.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDIT_CONNECTION_LIST_H +#define NMT_EDIT_CONNECTION_LIST_H + +#include "nmt-newt.h" + +#include <NetworkManager.h> + +G_BEGIN_DECLS + +#define NMT_TYPE_EDIT_CONNECTION_LIST (nmt_edit_connection_list_get_type ()) +#define NMT_EDIT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionList)) +#define NMT_EDIT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListClass)) +#define NMT_IS_EDIT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDIT_CONNECTION_LIST)) +#define NMT_IS_EDIT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDIT_CONNECTION_LIST)) +#define NMT_EDIT_CONNECTION_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtEditConnectionList; + +typedef struct { + NmtNewtGridClass parent; + + /* signals */ + void (*add_connection) (NmtEditConnectionList *list); + void (*edit_connection) (NmtEditConnectionList *list, + NMConnection *connection); + void (*remove_connection) (NmtEditConnectionList *list, + NMRemoteConnection *connection); +} NmtEditConnectionListClass; + +GType nmt_edit_connection_list_get_type (void); + +typedef gboolean (*NmtEditConnectionListFilter) (NmtEditConnectionList *list, + NMConnection *connection, + gpointer user_data); + +G_END_DECLS + +#endif /* NMT_EDIT_CONNECTION_LIST_H */ diff --git a/clients/tui/nmt-editor-grid.c b/clients/tui/nmt-editor-grid.c new file mode 100644 index 00000000..118bf01d --- /dev/null +++ b/clients/tui/nmt-editor-grid.c @@ -0,0 +1,463 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor-grid + * @short_description: Grid widget for #NmtEditorPages + * + * #NmtEditorGrid is the layout grid used by #NmtEditorPages. It + * consists of a number of rows, each containing either a single + * widget that spans the entire width of the row, or else containing a + * label, a widget, and an optional extra widget. + * + * Each row of the grid can take up multiple on-screen rows, if + * its main widget is multiple rows high. The label and extra widgets + * will be top-aligned if the row is taller than they are. + * + * The #NmtEditorGrids in a form behave as though they are all in a + * "size group" together; they will all use the same column widths, + * which will be wide enough for the widest labels/widgets in any of + * the grids. #NmtEditorGrid is also specially aware of #NmtNewtSection, + * and grids inside sections will automatically take the size of the + * section border into account as well. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-editor-grid.h" + +G_DEFINE_TYPE (NmtEditorGrid, nmt_editor_grid, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_EDITOR_GRID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR_GRID, NmtEditorGridPrivate)) + +typedef struct { + GArray *rows; + int *row_heights; + int indent; +} NmtEditorGridPrivate; + +typedef struct { + NmtNewtWidget *label; + NmtNewtWidget *widget; + NmtNewtWidget *extra; + NmtEditorGridRowFlags flags; +} NmtEditorGridRow; + +typedef struct { + int col_widths[3]; +} NmtEditorGridFormState; + +/** + * nmt_editor_grid_new: + * + * Creates a new #NmtEditorGrid + * + * Returns: a new #NmtEditorGrid + */ +NmtNewtWidget * +nmt_editor_grid_new (void) +{ + return g_object_new (NMT_TYPE_EDITOR_GRID, + NULL); +} + +static void +nmt_editor_grid_init (NmtEditorGrid *grid) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (grid); + + priv->rows = g_array_new (FALSE, TRUE, sizeof (NmtEditorGridRow)); +} + +static void +nmt_editor_grid_finalize (GObject *object) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (object); + + g_array_unref (priv->rows); + g_clear_pointer (&priv->row_heights, g_free); + + G_OBJECT_CLASS (nmt_editor_grid_parent_class)->finalize (object); +} + +/** + * nmt_editor_grid_append: + * @grid: the #NmtEditorGrid + * @label: (allow-none): the label text for @widget, or %NULL + * @widget: (allow-none): the (main) widget + * @extra: (allow-none): optional extra widget + * + * Adds a row to @grid. + * + * If @label and @widget are both non-%NULL, this will add a three-column row, + * containing a right-aligned #NmtNewtLabel in the first column, @widget in the + * second column, and @extra (if non-%NULL) in the third column. + * + * If either @label or @widget is %NULL, then the other column will expand into + * it. + * + * See also nmt_editor_grid_set_row_flags(). + */ +void +nmt_editor_grid_append (NmtEditorGrid *grid, + const char *label, + NmtNewtWidget *widget, + NmtNewtWidget *extra) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (grid); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_editor_grid_parent_class); + NmtNewtContainer *container = NMT_NEWT_CONTAINER (grid); + NmtEditorGridRow row; + + g_return_if_fail (label != NULL || widget != NULL); + + memset (&row, 0, sizeof (row)); + + if (label && !widget) { + widget = nmt_newt_label_new (label); + label = NULL; + } + + if (label) { + row.label = nmt_newt_label_new (label); + parent_class->add (container, row.label); + } + + row.widget = widget; + parent_class->add (container, widget); + if (row.label) { + g_object_bind_property (row.widget, "valid", + row.label, "highlight", + G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE); + } + + if (extra) { + row.extra = extra; + parent_class->add (container, extra); + } + + g_array_append_val (priv->rows, row); +} + +static int +nmt_editor_grid_find_widget (NmtEditorGrid *grid, + NmtNewtWidget *widget) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (grid); + NmtEditorGridRow *rows = (NmtEditorGridRow *) priv->rows->data; + int i; + + for (i = 0; i < priv->rows->len; i++) { + if (rows[i].label == widget || rows[i].widget == widget || rows[i].extra == widget) + return i; + } + + return -1; +} + +/** + * NmtEditorGridRowFlags: + * @NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT: the row's label should be + * aligned left instead of right. + * @NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT: the row's extra widget + * should be aligned right instead of left. + * + * Flags to alter an #NmtEditorGrid row's layout. + */ + +/** + * nmt_editor_grid_set_row_flags: + * @grid: an #NmtEditorGrid + * @widget: the widget whose row you want to adjust + * @flags: the flags to set + * + * Sets flags to adjust the layout of @widget's row in @grid. + */ +void +nmt_editor_grid_set_row_flags (NmtEditorGrid *grid, + NmtNewtWidget *widget, + NmtEditorGridRowFlags flags) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (grid); + NmtEditorGridRow *rows = (NmtEditorGridRow *) priv->rows->data; + int i; + + i = nmt_editor_grid_find_widget (grid, widget); + if (i != -1) + rows[i].flags = flags; +} + +static void +nmt_editor_grid_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtEditorGrid *grid = NMT_EDITOR_GRID (container); + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (grid); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_editor_grid_parent_class); + NmtEditorGridRow *rows = (NmtEditorGridRow *) priv->rows->data; + int i; + + i = nmt_editor_grid_find_widget (grid, widget); + if (i != -1) { + if (rows[i].label) + parent_class->remove (container, rows[i].label); + parent_class->remove (container, rows[i].widget); + if (rows[i].extra) + parent_class->remove (container, rows[i].extra); + + g_array_remove_index (priv->rows, i); + return; + } + + // FIXME: shouldn't happen + parent_class->remove (container, widget); +} + +static newtComponent * +nmt_editor_grid_get_components (NmtNewtWidget *widget) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (widget); + NmtEditorGridRow *rows = (NmtEditorGridRow *) priv->rows->data; + newtComponent *child_cos; + GPtrArray *cos; + int i, c; + + cos = g_ptr_array_new (); + + for (i = 0; i < priv->rows->len; i++) { + if (!nmt_newt_widget_get_visible (rows[i].widget)) + continue; + + if (rows[i].label) { + child_cos = nmt_newt_widget_get_components (rows[i].label); + g_assert (child_cos[0] && !child_cos[1]); + g_ptr_array_add (cos, child_cos[0]); + g_free (child_cos); + } + + child_cos = nmt_newt_widget_get_components (rows[i].widget); + for (c = 0; child_cos[c]; c++) + g_ptr_array_add (cos, child_cos[c]); + g_free (child_cos); + + if (rows[i].extra) { + child_cos = nmt_newt_widget_get_components (rows[i].extra); + for (c = 0; child_cos[c]; c++) + g_ptr_array_add (cos, child_cos[c]); + g_free (child_cos); + } + } + + g_ptr_array_add (cos, NULL); + return (newtComponent *) g_ptr_array_free (cos, FALSE); +} + +static NmtEditorGridFormState * +get_form_state (NmtNewtWidget *widget) +{ + NmtNewtForm *form = nmt_newt_widget_get_form (widget); + NmtEditorGridFormState *state; + + if (!form) + return NULL; + + state = g_object_get_data (G_OBJECT (form), "NmtEditorGridFormState"); + if (state) + return state; + + state = g_new0 (NmtEditorGridFormState, 1); + g_object_set_data_full (G_OBJECT (form), "NmtEditorGridFormState", state, g_free); + return state; +} + +static void +nmt_editor_grid_realize (NmtNewtWidget *widget) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (widget); + NmtNewtWidget *parent; + + NMT_NEWT_WIDGET_CLASS (nmt_editor_grid_parent_class)->realize (widget); + + /* This is a hack, but it's the simplest way to make it work... */ + priv->indent = 0; + + parent = nmt_newt_widget_get_parent (widget); + while (parent) { + if (NMT_IS_NEWT_SECTION (parent)) { + priv->indent = 2; + break; + } + parent = nmt_newt_widget_get_parent (parent); + } +} + +static void +nmt_editor_grid_unrealize (NmtNewtWidget *widget) +{ + NmtEditorGridFormState *state = get_form_state (widget); + + if (state) + memset (state->col_widths, 0, sizeof (state->col_widths)); + + NMT_NEWT_WIDGET_CLASS (nmt_editor_grid_parent_class)->unrealize (widget); +} + +static void +nmt_editor_grid_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (widget); + NmtEditorGridRow *rows = (NmtEditorGridRow *) priv->rows->data; + NmtEditorGridFormState *state = get_form_state (widget); + gboolean add_padding = FALSE; + int i; + + g_free (priv->row_heights); + priv->row_heights = g_new0 (int, priv->rows->len); + + *height = 0; + for (i = 0; i < priv->rows->len; i++) { + int lwidth, lheight, wwidth, wheight, ewidth, eheight; + + if (!nmt_newt_widget_get_visible (rows[i].widget)) + continue; + + if (rows[i].label) { + nmt_newt_widget_size_request (rows[i].label, &lwidth, &lheight); + lwidth += priv->indent; + state->col_widths[0] = MAX (state->col_widths[0], lwidth); + + nmt_newt_widget_size_request (rows[i].widget, &wwidth, &wheight); + state->col_widths[1] = MAX (state->col_widths[1], wwidth); + priv->row_heights[i] = wheight; + + add_padding = TRUE; + } else { + nmt_newt_widget_size_request (rows[i].widget, &wwidth, &wheight); + priv->row_heights[i] = wheight; + } + + if (rows[i].extra) { + nmt_newt_widget_size_request (rows[i].extra, &ewidth, &eheight); + state->col_widths[2] = MAX (state->col_widths[2], ewidth); + priv->row_heights[i] = MAX (priv->row_heights[i], eheight); + } + + *height += priv->row_heights[i]; + } + + *width = state->col_widths[0] + state->col_widths[1] + state->col_widths[2]; + if (add_padding) + *width += 2; +} + + +static void +nmt_editor_grid_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (widget); + NmtEditorGridRow *rows = (NmtEditorGridRow *) priv->rows->data; + NmtEditorGridFormState *state = get_form_state (widget); + int col0_width, col1_width, col2_width; + int i, row; + + col0_width = state->col_widths[0] - priv->indent; + col1_width = state->col_widths[1]; + col2_width = state->col_widths[2]; + + for (i = row = 0; i < priv->rows->len; i++) { + if (!nmt_newt_widget_get_visible (rows[i].widget)) + continue; + + if (rows[i].label) { + int lwidth, lheight, lx; + + if (rows[i].flags & NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT) + lx = x; + else { + nmt_newt_widget_size_request (rows[i].label, &lwidth, &lheight); + lx = x + col0_width - lwidth; + } + + nmt_newt_widget_size_allocate (rows[i].label, + lx, + y + row, + col0_width, + priv->row_heights[i]); + + nmt_newt_widget_size_allocate (rows[i].widget, + x + col0_width + 1, + y + row, + col1_width, + priv->row_heights[i]); + } else { + nmt_newt_widget_size_allocate (rows[i].widget, + x, + y + row, + col0_width + col1_width + 1, + priv->row_heights[i]); + } + + if (rows[i].extra) { + int wwidth, wheight, ex; + + if (rows[i].flags & NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT) + ex = x + col0_width + col1_width + 2; + else { + nmt_newt_widget_size_request (rows[i].widget, &wwidth, &wheight); + ex = x + col0_width + wwidth + 2; + } + + nmt_newt_widget_size_allocate (rows[i].extra, + ex, + y + row, + col2_width, + priv->row_heights[i]); + } + + row += priv->row_heights[i]; + } +} + +static void +nmt_editor_grid_class_init (NmtEditorGridClass *grid_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (grid_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (grid_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (grid_class); + + g_type_class_add_private (grid_class, sizeof (NmtEditorGridPrivate)); + + /* virtual methods */ + object_class->finalize = nmt_editor_grid_finalize; + + widget_class->realize = nmt_editor_grid_realize; + widget_class->unrealize = nmt_editor_grid_unrealize; + widget_class->get_components = nmt_editor_grid_get_components; + widget_class->size_request = nmt_editor_grid_size_request; + widget_class->size_allocate = nmt_editor_grid_size_allocate; + + container_class->remove = nmt_editor_grid_remove; +} diff --git a/clients/tui/nmt-editor-grid.h b/clients/tui/nmt-editor-grid.h new file mode 100644 index 00000000..2ae01d71 --- /dev/null +++ b/clients/tui/nmt-editor-grid.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_GRID_H +#define NMT_EDITOR_GRID_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_EDITOR_GRID (nmt_editor_grid_get_type ()) +#define NMT_EDITOR_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_GRID, NmtEditorGrid)) +#define NMT_EDITOR_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_GRID, NmtEditorGridClass)) +#define NMT_IS_EDITOR_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR_GRID)) +#define NMT_IS_EDITOR_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR_GRID)) +#define NMT_EDITOR_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR_GRID, NmtEditorGridClass)) + +typedef struct { + NmtNewtContainer parent; + +} NmtEditorGrid; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtEditorGridClass; + +GType nmt_editor_grid_get_type (void); + +typedef enum { + NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT = (1 << 0), + NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT = (1 << 1) +} NmtEditorGridRowFlags; + +NmtNewtWidget *nmt_editor_grid_new (void); + +void nmt_editor_grid_append (NmtEditorGrid *grid, + const char *label, + NmtNewtWidget *widget, + NmtNewtWidget *extra); +void nmt_editor_grid_set_row_flags (NmtEditorGrid *grid, + NmtNewtWidget *widget, + NmtEditorGridRowFlags flags); + +G_END_DECLS + +#endif /* NMT_EDITOR_GRID_H */ diff --git a/clients/tui/nmt-editor-page-device.c b/clients/tui/nmt-editor-page-device.c new file mode 100644 index 00000000..0a9b18e0 --- /dev/null +++ b/clients/tui/nmt-editor-page-device.c @@ -0,0 +1,131 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor-page-device + * @short_description: Abstract base class for "device" editor pages + * + * #NmtEditorPageDevice is the base class for #NmtEditorPage subclasses + * representing device-type-specific data. (Eg, #NmtPageEthernet, + * #NmtPageVlan, etc). + * + * FIXME: rename to NmtEditorPageDevice, so it doesn't sound like it's + * an actual page type. + */ + +#include "config.h" + +#include "nmt-editor-page-device.h" + +G_DEFINE_TYPE (NmtEditorPageDevice, nmt_editor_page_device, NMT_TYPE_EDITOR_PAGE) + +#define NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDevicePrivate)) + +typedef struct { + NmtDeviceEntry *device_entry; + gboolean show_by_default; +} NmtEditorPageDevicePrivate; + +enum { + PROP_0, + + PROP_DEVICE_ENTRY, + + LAST_PROP +}; + +static void +nmt_editor_page_device_init (NmtEditorPageDevice *device) +{ +} + +static void +nmt_editor_page_device_finalize (GObject *object) +{ + NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE (object); + + g_clear_object (&priv->device_entry); + + G_OBJECT_CLASS (nmt_editor_page_device_parent_class)->finalize (object); +} + +NmtDeviceEntry * +nmt_editor_page_device_get_device_entry (NmtEditorPageDevice *page) +{ + NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE (page); + + return priv->device_entry; +} + +static void +nmt_editor_page_device_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DEVICE_ENTRY: + priv->device_entry = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_page_device_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DEVICE_ENTRY: + g_value_set_object (value, priv->device_entry); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_page_device_class_init (NmtEditorPageDeviceClass *page_device_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (page_device_class); + + g_type_class_add_private (page_device_class, sizeof (NmtEditorPageDevicePrivate)); + + /* virtual methods */ + object_class->set_property = nmt_editor_page_device_set_property; + object_class->get_property = nmt_editor_page_device_get_property; + object_class->finalize = nmt_editor_page_device_finalize; + + /* properties */ + g_object_class_install_property + (object_class, PROP_DEVICE_ENTRY, + g_param_spec_object ("device-entry", "", "", + NMT_TYPE_DEVICE_ENTRY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-editor-page-device.h b/clients/tui/nmt-editor-page-device.h new file mode 100644 index 00000000..f911602c --- /dev/null +++ b/clients/tui/nmt-editor-page-device.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_PAGE_DEVICE_H +#define NMT_EDITOR_PAGE_DEVICE_H + +#include "nmt-editor-page.h" +#include "nmt-device-entry.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_EDITOR_PAGE_DEVICE (nmt_editor_page_device_get_type ()) +#define NMT_EDITOR_PAGE_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDevice)) +#define NMT_EDITOR_PAGE_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDeviceClass)) +#define NMT_IS_EDITOR_PAGE_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR_PAGE_DEVICE)) +#define NMT_IS_EDITOR_PAGE_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR_PAGE_DEVICE)) +#define NMT_EDITOR_PAGE_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDeviceClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtEditorPageDevice; + +typedef struct { + NmtEditorPageClass parent; + +} NmtEditorPageDeviceClass; + +GType nmt_editor_page_device_get_type (void); + +NmtDeviceEntry *nmt_editor_page_device_get_device_entry (NmtEditorPageDevice *page); + +G_END_DECLS + +#endif /* NMT_EDITOR_PAGE_DEVICE_H */ diff --git a/clients/tui/nmt-editor-page.c b/clients/tui/nmt-editor-page.c new file mode 100644 index 00000000..a46d0ed4 --- /dev/null +++ b/clients/tui/nmt-editor-page.c @@ -0,0 +1,179 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor-page: + * @short_description: An #NmtEditor "page" + * + * #NmtEditorPage is the abstract base class for #NmtEditor "pages". + * A "page" is a set of related #NmtEditorSections. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-editor-page.h" + +G_DEFINE_ABSTRACT_TYPE (NmtEditorPage, nmt_editor_page, G_TYPE_OBJECT) + +#define NMT_EDITOR_PAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR_PAGE, NmtEditorPagePrivate)) + +typedef struct { + NMConnection *connection; + GSList *sections; + +} NmtEditorPagePrivate; + +enum { + PROP_0, + + PROP_CONNECTION, + + LAST_PROP +}; + +static void +nmt_editor_page_init (NmtEditorPage *page) +{ +} + +static void +nmt_editor_page_finalize (GObject *object) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (object); + + g_clear_object (&priv->connection); + g_slist_free_full (priv->sections, g_object_unref); + + G_OBJECT_CLASS (nmt_editor_page_parent_class)->finalize (object); +} + +/** + * nmt_editor_page_get_connection: + * @page: the #NmtEditorPage + * + * Gets the page's #NMConnection. + * + * Returns: (transfer none): the page's #NMConnection. + */ +NMConnection * +nmt_editor_page_get_connection (NmtEditorPage *page) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page); + + return priv->connection; +} + +/** + * nmt_editor_page_get_sections: + * @page: the #NmtEditorPage + * + * Gets the page's list of sections to display. + * + * Returns: (transfer none): the list of sections; this is the internal list + * used by the page and must not be modified or freed. + */ +GSList * +nmt_editor_page_get_sections (NmtEditorPage *page) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page); + + return priv->sections; +} + +/** + * nmt_editor_page_add_section: + * @page: the #NmtEditorPage + * @section: the #NmtEditorSection + * + * Adds a section to the page. This should only be called by #NmtEditorPage + * subclasses. + */ +void +nmt_editor_page_add_section (NmtEditorPage *page, + NmtEditorSection *section) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page); + + priv->sections = g_slist_append (priv->sections, g_object_ref_sink (section)); +} + +static void +nmt_editor_page_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CONNECTION: + priv->connection = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_page_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_object (value, priv->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_page_class_init (NmtEditorPageClass *page_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (page_class); + + g_type_class_add_private (page_class, sizeof (NmtEditorPagePrivate)); + + /* virtual methods */ + object_class->set_property = nmt_editor_page_set_property; + object_class->get_property = nmt_editor_page_get_property; + object_class->finalize = nmt_editor_page_finalize; + + /* properties */ + + /** + * NmtEditorPage:connection: + * + * The page's #NMConnection. + */ + g_object_class_install_property + (object_class, PROP_CONNECTION, + g_param_spec_object ("connection", "", "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-editor-page.h b/clients/tui/nmt-editor-page.h new file mode 100644 index 00000000..bbc0d9e3 --- /dev/null +++ b/clients/tui/nmt-editor-page.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013-2014 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_PAGE_H +#define NMT_EDITOR_PAGE_H + +#include <NetworkManager.h> + +#include "nmt-editor-grid.h" +#include "nmt-editor-section.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_EDITOR_PAGE (nmt_editor_page_get_type ()) +#define NMT_EDITOR_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_PAGE, NmtEditorPage)) +#define NMT_EDITOR_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_PAGE, NmtEditorPageClass)) +#define NMT_IS_EDITOR_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR_PAGE)) +#define NMT_IS_EDITOR_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR_PAGE)) +#define NMT_EDITOR_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR_PAGE, NmtEditorPageClass)) + +typedef struct { + GObject parent; + +} NmtEditorPage; + +typedef struct { + GObjectClass parent; + +} NmtEditorPageClass; + +GType nmt_editor_page_get_type (void); + +NMConnection *nmt_editor_page_get_connection (NmtEditorPage *page); + +GSList *nmt_editor_page_get_sections (NmtEditorPage *page); + +/*< protected >*/ +void nmt_editor_page_add_section (NmtEditorPage *page, + NmtEditorSection *section); + +G_END_DECLS + +#endif /* NMT_EDITOR_PAGE_H */ diff --git a/clients/tui/nmt-editor-section.c b/clients/tui/nmt-editor-section.c new file mode 100644 index 00000000..ff463f7f --- /dev/null +++ b/clients/tui/nmt-editor-section.c @@ -0,0 +1,282 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor-section: + * @short_description: A section of the #NmtEditor + * + * #NmtEditorSection is the abstract base class for #NmtEditor sections. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-editor-section.h" +#include "nmt-newt-toggle-button.h" + +G_DEFINE_TYPE (NmtEditorSection, nmt_editor_section, NMT_TYPE_NEWT_SECTION) + +#define NMT_EDITOR_SECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR_SECTION, NmtEditorSectionPrivate)) + +typedef struct { + NmtEditorGrid *header, *body; + char *title; + NmtNewtWidget *header_widget; + NmtNewtWidget *toggle; + + gboolean show_by_default; +} NmtEditorSectionPrivate; + +enum { + PROP_0, + + PROP_TITLE, + PROP_SHOW_BY_DEFAULT, + PROP_HEADER_WIDGET, + + LAST_PROP +}; + +/** + * nmt_editor_section_new: + * @title: the section title + * @header_widget: (allow-none): the widget to show next to the title + * @show_by_default: whether the section should be open by default + * + * Creates a new #NmtEditorSection. + * + * Returns: a new #NmtEditorSection + */ +NmtEditorSection * +nmt_editor_section_new (const char *title, + NmtNewtWidget *header_widget, + gboolean show_by_default) +{ + return g_object_new (NMT_TYPE_EDITOR_SECTION, + "title", title, + "header-widget", header_widget, + "show-by-default", show_by_default, + NULL); +} + +static void +rebuild_header (NmtEditorSection *section) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section); + + /* Removing any widget in an NmtEditorGrid removes its whole row, so we can + * remove the existing title/widget/toggle by asking to remove toggle. + */ + nmt_newt_container_remove (NMT_NEWT_CONTAINER (priv->header), priv->toggle); + + nmt_editor_grid_append (priv->header, + priv->title, + priv->header_widget, + priv->toggle); + nmt_editor_grid_set_row_flags (priv->header, + priv->toggle, + NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT | + NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT); +} + +static void +nmt_editor_section_init (NmtEditorSection *section) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section); + + priv->header = NMT_EDITOR_GRID (nmt_editor_grid_new ()); + priv->body = NMT_EDITOR_GRID (nmt_editor_grid_new ()); + priv->toggle = nmt_newt_toggle_button_new (_("Hide"), _("Show")); + g_object_ref_sink (priv->toggle); + + nmt_newt_section_set_header (NMT_NEWT_SECTION (section), NMT_NEWT_WIDGET (priv->header)); + nmt_newt_section_set_body (NMT_NEWT_SECTION (section), NMT_NEWT_WIDGET (priv->body)); + + g_object_bind_property (priv->toggle, "active", + section, "open", + G_BINDING_SYNC_CREATE); +} + +static void +nmt_editor_section_finalize (GObject *object) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (object); + + g_free (priv->title); + g_clear_object (&priv->header_widget); + g_clear_object (&priv->toggle); + + G_OBJECT_CLASS (nmt_editor_section_parent_class)->finalize (object); +} + +/** + * nmt_editor_section_get_header_widget: + * @section: the #NmtEditorSection + * + * Gets the section's header widget. + * + * Returns: the section's header widget. + */ +NmtNewtWidget * +nmt_editor_section_get_header_widget (NmtEditorSection *section) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section); + + return priv->header_widget; +} + +/** + * nmt_editor_section_get_body: + * @section: the #NmtEditorSection + * + * Gets the section's body grid, so that you can add things to it. + * + * Returns: the #NmtEditorGrid used for the section body + */ +NmtEditorGrid * +nmt_editor_section_get_body (NmtEditorSection *section) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section); + + return priv->body; +} + +/** + * nmt_editor_section_get_title: + * @section: the #NmtEditorSection + * + * Gets the section's title. + * + * Returns: the section's title + */ +const char * +nmt_editor_section_get_title (NmtEditorSection *section) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section); + + return priv->title; +} + +static void +nmt_editor_section_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtEditorSection *section = NMT_EDITOR_SECTION (object); + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section); + + switch (prop_id) { + case PROP_TITLE: + priv->title = g_value_dup_string (value); + rebuild_header (section); + break; + case PROP_SHOW_BY_DEFAULT: + priv->show_by_default = g_value_get_boolean (value); + nmt_newt_toggle_button_set_active (NMT_NEWT_TOGGLE_BUTTON (priv->toggle), + priv->show_by_default); + break; + case PROP_HEADER_WIDGET: + priv->header_widget = g_value_get_object (value); + if (priv->header_widget) + g_object_ref_sink (priv->header_widget); + rebuild_header (section); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_section_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_TITLE: + g_value_set_string (value, priv->title); + break; + case PROP_SHOW_BY_DEFAULT: + g_value_set_boolean (value, priv->show_by_default); + break; + case PROP_HEADER_WIDGET: + g_value_set_object (value, priv->header_widget); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_section_class_init (NmtEditorSectionClass *section_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (section_class); + + g_type_class_add_private (section_class, sizeof (NmtEditorSectionPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_editor_section_set_property; + object_class->get_property = nmt_editor_section_get_property; + object_class->finalize = nmt_editor_section_finalize; + + /* properties */ + + /** + * NmtEditorSection:title: + * + * The section's title. + */ + g_object_class_install_property + (object_class, PROP_TITLE, + g_param_spec_string ("title", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditorSection:show-by-default: + * + * Whether the section should be expanded by default. + */ + g_object_class_install_property + (object_class, PROP_SHOW_BY_DEFAULT, + g_param_spec_boolean ("show-by-default", "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditorSection:header-widget: + * + * The widget (if any) that appears between the section title and its toggle + * button. + */ + g_object_class_install_property + (object_class, PROP_HEADER_WIDGET, + g_param_spec_object ("header-widget", "", "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-editor-section.h b/clients/tui/nmt-editor-section.h new file mode 100644 index 00000000..67410c71 --- /dev/null +++ b/clients/tui/nmt-editor-section.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_SECTION_H +#define NMT_EDITOR_SECTION_H + +#include "nmt-newt-section.h" +#include "nmt-editor-grid.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_EDITOR_SECTION (nmt_editor_section_get_type ()) +#define NMT_EDITOR_SECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_SECTION, NmtEditorSection)) +#define NMT_EDITOR_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_SECTION, NmtEditorSectionClass)) +#define NMT_IS_EDITOR_SECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR_SECTION)) +#define NMT_IS_EDITOR_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR_SECTION)) +#define NMT_EDITOR_SECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR_SECTION, NmtEditorSectionClass)) + +typedef struct { + NmtNewtSection parent; + +} NmtEditorSection; + +typedef struct { + NmtNewtSectionClass parent; + +} NmtEditorSectionClass; + +GType nmt_editor_section_get_type (void); + +NmtEditorSection *nmt_editor_section_new (const char *title, + NmtNewtWidget *header_widget, + gboolean show_by_default); + +const char *nmt_editor_section_get_title (NmtEditorSection *section); +NmtNewtWidget *nmt_editor_section_get_header_widget (NmtEditorSection *section); +NmtEditorGrid *nmt_editor_section_get_body (NmtEditorSection *section); + +G_END_DECLS + +#endif /* NMT_EDITOR_SECTION_H */ diff --git a/clients/tui/nmt-editor.c b/clients/tui/nmt-editor.c new file mode 100644 index 00000000..11fe758d --- /dev/null +++ b/clients/tui/nmt-editor.c @@ -0,0 +1,520 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor + * @short_description: Connection editing form + * + * #NmtEditor is the top-level form for editing a connection. + */ + +#include "config.h" + +#include "nmt-editor.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <nm-utils.h> + +#include "nmtui.h" + +#include "nm-editor-utils.h" +#include "nmt-utils.h" + +#include "nmt-device-entry.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" + +#include "nmt-page-bond.h" +#include "nmt-page-bridge.h" +#include "nmt-page-bridge-port.h" +#include "nmt-page-dsl.h" +#include "nmt-page-ethernet.h" +#include "nmt-page-infiniband.h" +#include "nmt-page-ip4.h" +#include "nmt-page-ip6.h" +#include "nmt-page-ppp.h" +#include "nmt-page-team.h" +#include "nmt-page-team-port.h" +#include "nmt-page-vlan.h" +#include "nmt-page-wifi.h" + +G_DEFINE_TYPE (NmtEditor, nmt_editor, NMT_TYPE_NEWT_FORM) + +#define NMT_EDITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR, NmtEditorPrivate)) + +typedef struct { + NMConnection *orig_connection; + NMConnection *edit_connection; + + NMEditorConnectionTypeData *type_data; + + GSList *pages; + NmtNewtWidget *ok, *cancel; + gboolean running; +} NmtEditorPrivate; + +enum { + PROP_0, + PROP_CONNECTION, + PROP_TYPE_DATA, + + LAST_PROP +}; + +/** + * nmt_editor_new: + * @connection: the #NMConnection to edit + * + * Creates a new #NmtEditor to edit @connection. + * + * Returns: a new #NmtEditor + */ +NmtNewtForm * +nmt_editor_new (NMConnection *connection) +{ + NMEditorConnectionTypeData *type_data; + + type_data = nm_editor_utils_get_connection_type_data (connection); + if (!type_data) { + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (connection); + if (s_con) { + nmt_newt_message_dialog (_("Could not create editor for connection '%s' of type '%s'."), + nm_connection_get_id (connection), + nm_setting_connection_get_connection_type (s_con)); + } else { + nmt_newt_message_dialog (_("Could not create editor for invalid connection '%s'."), + nm_connection_get_id (connection)); + } + + return NULL; + } + + return g_object_new (NMT_TYPE_EDITOR, + "connection", connection, + "type-data", type_data, + "title", _("Edit Connection"), + "fullscreen-vertical", TRUE, + NULL); +} + +static void +nmt_editor_init (NmtEditor *entry) +{ +} + +static void +connection_updated (GObject *connection, + GAsyncResult *result, + gpointer op) +{ + GError *error = NULL; + + nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection), result, &error); + nmt_sync_op_complete_boolean (op, error == NULL, error); + g_clear_error (&error); +} + +static void +connection_added (GObject *client, + GAsyncResult *result, + gpointer op) +{ + NMRemoteConnection *connection; + GError *error = NULL; + + connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error); + nmt_sync_op_complete_boolean (op, error == NULL, error); + g_clear_object (&connection); + g_clear_error (&error); +} + +static void +save_connection_and_exit (NmtNewtButton *button, + gpointer user_data) +{ + NmtEditor *editor = user_data; + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (editor); + NmtSyncOp op; + GError *error = NULL; + + nm_connection_replace_settings_from_connection (priv->orig_connection, + priv->edit_connection); + + nmt_sync_op_init (&op); + if (NM_IS_REMOTE_CONNECTION (priv->orig_connection)) { + nm_remote_connection_commit_changes_async (NM_REMOTE_CONNECTION (priv->orig_connection), + TRUE, NULL, connection_updated, &op); + if (!nmt_sync_op_wait_boolean (&op, &error)) { + nmt_newt_message_dialog (_("Unable to save connection: %s"), + error->message); + g_error_free (error); + return; + } + + /* Clear secrets so they don't lay around in memory; they'll get + * requested again anyway next time the connection is edited. + */ + nm_connection_clear_secrets (priv->orig_connection); + } else { + nm_client_add_connection_async (nm_client, priv->orig_connection, TRUE, + NULL, connection_added, &op); + if (!nmt_sync_op_wait_boolean (&op, &error)) { + nmt_newt_message_dialog (_("Unable to add new connection: %s"), + error->message); + g_error_free (error); + return; + } + } + + nmt_newt_form_quit (NMT_NEWT_FORM (editor)); +} + +static void +got_secrets (GObject *object, + GAsyncResult *result, + gpointer op) +{ + GVariant *secrets; + GError *error = NULL; + + secrets = nm_remote_connection_get_secrets_finish (NM_REMOTE_CONNECTION (object), + result, &error); + if (secrets) + g_variant_ref (secrets); + nmt_sync_op_complete_pointer (op, secrets, error); + g_clear_error (&error); +} + +static NMConnection * +build_edit_connection (NMConnection *orig_connection) +{ + NMConnection *edit_connection; + GVariant *settings, *secrets; + GVariantIter iter; + const char *setting_name; + NmtSyncOp op; + + edit_connection = nm_simple_connection_new_clone (orig_connection); + + if (!NM_IS_REMOTE_CONNECTION (orig_connection)) + return edit_connection; + + settings = nm_connection_to_dbus (orig_connection, NM_CONNECTION_SERIALIZE_NO_SECRETS); + g_variant_iter_init (&iter, settings); + while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL)) { + nmt_sync_op_init (&op); + nm_remote_connection_get_secrets_async (NM_REMOTE_CONNECTION (orig_connection), + setting_name, NULL, got_secrets, &op); + /* FIXME: error handling */ + secrets = nmt_sync_op_wait_pointer (&op, NULL); + if (secrets) { + (void) nm_connection_update_secrets (edit_connection, setting_name, secrets, NULL); + g_variant_unref (secrets); + } + } + g_variant_unref (settings); + + return edit_connection; +} + +static gboolean +permissions_transform_to_allusers (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + char **perms = g_value_get_boxed (source_value); + + g_value_set_boolean (target_value, g_strv_length (perms) == 0); + return TRUE; +} + +static gboolean +permissions_transform_from_allusers (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + gboolean allusers = g_value_get_boolean (source_value); + char **perms = NULL; + + if (allusers) { + perms = g_new (char *, 2); + + perms[0] = g_strdup_printf ("user:%s:", g_get_user_name ()); + perms[1] = NULL; + } + g_value_take_boxed (target_value, perms); + return TRUE; +} + +static NmtNewtWidget * +add_sections_for_page (NmtEditor *editor, NmtEditorGrid *grid, NmtEditorPage *page) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (editor); + NmtNewtWidget *first_section = NULL; + const GSList *sections, *iter; + + g_return_val_if_fail (NMT_IS_EDITOR_PAGE (page), NULL); + + priv->pages = g_slist_prepend (priv->pages, page); + + sections = nmt_editor_page_get_sections (page); + for (iter = sections; iter; iter = iter->next) { + if (!first_section) + first_section = iter->data; + nmt_editor_grid_append (grid, NULL, iter->data, NULL); + } + + return first_section; +} + +static void +nmt_editor_constructed (GObject *object) +{ + NmtEditor *editor = NMT_EDITOR (object); + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (editor); + NMSettingConnection *s_con; + NmtNewtWidget *vbox, *widget, *buttons; + NmtEditorGrid *grid; + const char *deventry_label; + NmtDeviceEntry *deventry; + GType hardware_type; + const char *slave_type; + NmtEditorPage *page; + + if (G_OBJECT_CLASS (nmt_editor_parent_class)->constructed) + G_OBJECT_CLASS (nmt_editor_parent_class)->constructed (object); + + priv->edit_connection = build_edit_connection (priv->orig_connection); + + vbox = nmt_newt_grid_new (); + + s_con = nm_connection_get_setting_connection (priv->edit_connection); + + grid = NMT_EDITOR_GRID (nmt_editor_grid_new ()); + nmt_newt_grid_add (NMT_NEWT_GRID (vbox), NMT_NEWT_WIDGET (grid), 0, 0); + + /* Add the top widgets */ + + widget = nmt_newt_entry_new (40, NMT_NEWT_ENTRY_NONEMPTY); + g_object_bind_property (s_con, NM_SETTING_CONNECTION_ID, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Profile name"), widget, NULL); + + if (priv->type_data->virtual) + hardware_type = G_TYPE_NONE; + else + hardware_type = priv->type_data->device_type; + + /* For connections involving multiple network devices, clarify which one + * NMSettingConnection:interface-name refers to. + */ + if (nm_connection_is_type (priv->edit_connection, NM_SETTING_PPPOE_SETTING_NAME)) + deventry_label = _("Ethernet device"); + else + deventry_label = _("Device"); + + widget = nmt_device_entry_new (deventry_label, 40, hardware_type); + nmt_editor_grid_append (grid, NULL, widget, NULL); + deventry = NMT_DEVICE_ENTRY (widget); + g_object_bind_property (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, + deventry, "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + /* Now add the various pages... */ + + if (nm_connection_is_type (priv->edit_connection, NM_SETTING_BOND_SETTING_NAME)) + page = nmt_page_bond_new (priv->edit_connection, deventry); + else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_BRIDGE_SETTING_NAME)) + page = nmt_page_bridge_new (priv->edit_connection, deventry); + else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_INFINIBAND_SETTING_NAME)) + page = nmt_page_infiniband_new (priv->edit_connection, deventry); + else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_PPPOE_SETTING_NAME)) + page = nmt_page_dsl_new (priv->edit_connection, deventry); + else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_TEAM_SETTING_NAME)) + page = nmt_page_team_new (priv->edit_connection, deventry); + else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_VLAN_SETTING_NAME)) + page = nmt_page_vlan_new (priv->edit_connection, deventry); + else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_WIRED_SETTING_NAME)) + page = nmt_page_ethernet_new (priv->edit_connection, deventry); + else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_WIRELESS_SETTING_NAME)) + page = nmt_page_wifi_new (priv->edit_connection, deventry); + else + g_assert_not_reached (); + + add_sections_for_page (editor, grid, page); + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + slave_type = nm_setting_connection_get_slave_type (s_con); + if (slave_type) { + if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME)) + add_sections_for_page (editor, grid, nmt_page_bridge_port_new (priv->edit_connection)); + else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME)) + add_sections_for_page (editor, grid, nmt_page_team_port_new (priv->edit_connection)); + } else { + NmtNewtWidget *section; + + section = add_sections_for_page (editor, grid, nmt_page_ip4_new (priv->edit_connection)); + + /* Add a separator between ip4 and ip6 that's only visible if ip4 is open */ + widget = nmt_newt_separator_new (); + g_object_bind_property (section, "open", widget, "visible", G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + add_sections_for_page (editor, grid, nmt_page_ip6_new (priv->edit_connection)); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + } + + /* And finally the bottom widgets */ + + widget = nmt_newt_checkbox_new (_("Automatically connect")); + g_object_bind_property (s_con, NM_SETTING_CONNECTION_AUTOCONNECT, + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new (_("Available to all users")); + g_object_bind_property_full (s_con, NM_SETTING_CONNECTION_PERMISSIONS, + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + permissions_transform_to_allusers, + permissions_transform_from_allusers, + NULL, NULL); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + /* And the button box */ + + buttons = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add (NMT_NEWT_GRID (vbox), buttons, 0, 1); + nmt_newt_widget_set_padding (buttons, 0, 1, 0, 0); + + priv->cancel = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (buttons), _("Cancel")); + nmt_newt_widget_set_exit_on_activate (priv->cancel, TRUE); + + priv->ok = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (buttons), _("OK")); + g_signal_connect (priv->ok, "clicked", G_CALLBACK (save_connection_and_exit), editor); + g_object_bind_property (NMT_NEWT_WIDGET (grid), "valid", + priv->ok, "sensitive", + G_BINDING_SYNC_CREATE); + + nmt_newt_form_set_content (NMT_NEWT_FORM (editor), vbox); +} + +static void +nmt_editor_finalize (GObject *object) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object); + + g_clear_object (&priv->orig_connection); + g_clear_object (&priv->edit_connection); + + g_slist_free_full (priv->pages, g_object_unref); + + g_clear_object (&priv->ok); + g_clear_object (&priv->cancel); + + G_OBJECT_CLASS (nmt_editor_parent_class)->finalize (object); +} + +static void +nmt_editor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CONNECTION: + priv->orig_connection = g_value_dup_object (value); + break; + case PROP_TYPE_DATA: + priv->type_data = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_object (value, priv->orig_connection); + break; + case PROP_TYPE_DATA: + g_value_set_pointer (value, priv->type_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_class_init (NmtEditorClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtEditorPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_editor_constructed; + object_class->set_property = nmt_editor_set_property; + object_class->get_property = nmt_editor_get_property; + object_class->finalize = nmt_editor_finalize; + + /** + * NmtEditor:connection: + * + * The connection being edited. + */ + g_object_class_install_property + (object_class, PROP_CONNECTION, + g_param_spec_object ("connection", "", "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtEditor:type-data: + * + * The #NmEditorConnectionTypeData for #NmtEditor:connection. + */ + g_object_class_install_property + (object_class, PROP_TYPE_DATA, + g_param_spec_pointer ("type-data", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-editor.h b/clients/tui/nmt-editor.h new file mode 100644 index 00000000..db10b82a --- /dev/null +++ b/clients/tui/nmt-editor.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_H +#define NMT_EDITOR_H + +#include <NetworkManager.h> + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_EDITOR (nmt_editor_get_type ()) +#define NMT_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR, NmtEditor)) +#define NMT_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR, NmtEditorClass)) +#define NMT_IS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR)) +#define NMT_IS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR)) +#define NMT_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR, NmtEditorClass)) + +typedef struct { + NmtNewtForm parent; + +} NmtEditor; + +typedef struct { + NmtNewtFormClass parent; + +} NmtEditorClass; + +GType nmt_editor_get_type (void); + +NmtNewtForm *nmt_editor_new (NMConnection *connection); + +G_END_DECLS + +#endif /* NMT_EDITOR_H */ diff --git a/clients/tui/nmt-ip-entry.c b/clients/tui/nmt-ip-entry.c new file mode 100644 index 00000000..30c7e2e4 --- /dev/null +++ b/clients/tui/nmt-ip-entry.c @@ -0,0 +1,268 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-ip-entry + * @short_description: #NmtNewtEntry for IP address entry + * + * #NmtIPEntry is an #NmtNewtEntry for entering IP addresses, or IP + * address/prefix combination. It will only allow typing characters + * that are valid in an IP address, and will set its + * #NmtNewtWidget:valid property depending on whether it currently + * contains a valid IP address. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include <glib/gi18n-lib.h> + +#include "nmt-ip-entry.h" + +G_DEFINE_TYPE (NmtIPEntry, nmt_ip_entry, NMT_TYPE_NEWT_ENTRY) + +#define NMT_IP_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_IP_ENTRY, NmtIPEntryPrivate)) + +typedef struct { + int family; + gboolean prefix; + gboolean optional; + +} NmtIPEntryPrivate; + +enum { + PROP_0, + PROP_FAMILY, + PROP_PREFIX, + PROP_OPTIONAL, + + LAST_PROP +}; + +/** + * nmt_ip_entry_new: + * @width: the width of the entry + * @family: the IP address family. Eg, %AF_INET + * @prefix: whether to require a trailing "/prefix" + * @optional: whether the address is optional + * + * Creates a new #NmtIPEntry, to accept IP addresses in the indicated + * @family, or (if @prefix is %TRUE), to accept IP address/prefix combos. + * + * If @optional is %TRUE then the address is considered optional, and + * so will still be #NmtNewtWidget:valid even when it is empty. If + * @optional is %FALSE, the entry will be invalid when it is empty. + */ +NmtNewtWidget * +nmt_ip_entry_new (int width, + int family, + gboolean prefix, + gboolean optional) +{ + return g_object_new (NMT_TYPE_IP_ENTRY, + "width", width, + "family", family, + "prefix", prefix, + "optional", optional, + NULL); +} + +static gboolean +ip_entry_filter (NmtNewtEntry *entry, + const char *text, + int ch, + int position, + gpointer user_data) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (entry); + const char *slash; + gboolean inaddr; + + if (g_ascii_isdigit (ch)) + return TRUE; + + slash = strchr (text, '/'); + if (ch == '/') + return priv->prefix && slash == NULL; + + inaddr = !slash || (position <= (slash - text)); + + if (priv->family == AF_INET) { + if (ch == '.') + return inaddr; + else + return FALSE; + } else if (priv->family == AF_INET6) { + if (g_ascii_isxdigit (ch) || ch == ':') + return inaddr; + else + return FALSE; + } else + g_return_val_if_reached (FALSE); +} + +static gboolean +ip_entry_validate (NmtNewtEntry *entry, + const char *text, + gpointer user_data) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (entry); + guchar buf[16]; + guint32 prefix; + const char *slash; + char *addrstr, *end; + gboolean valid; + + if (!*text) + return priv->optional; + + slash = strchr (text, '/'); + + if (slash) { + if (!priv->prefix) + return FALSE; + addrstr = g_strndup (text, slash - text); + } else + addrstr = g_strdup (text); + valid = (inet_pton (priv->family, addrstr, buf) == 1); + g_free (addrstr); + + if (!valid) + return FALSE; + + if (slash) { + prefix = strtoul (slash + 1, &end, 10); + if ( *end + || prefix == 0 + || (priv->family == AF_INET && prefix > 32) + || (priv->family == AF_INET6 && prefix > 128)) + valid = FALSE; + } + + return valid; +} + +static void +nmt_ip_entry_init (NmtIPEntry *entry) +{ + nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), ip_entry_filter, NULL); + nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), ip_entry_validate, NULL); +} + +static void +nmt_ip_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + priv->family = g_value_get_int (value); + break; + case PROP_PREFIX: + priv->prefix = g_value_get_boolean (value); + break; + case PROP_OPTIONAL: + priv->optional = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_ip_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int (value, priv->family); + break; + case PROP_PREFIX: + g_value_set_boolean (value, priv->prefix); + break; + case PROP_OPTIONAL: + g_value_set_boolean (value, priv->optional); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_ip_entry_class_init (NmtIPEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtIPEntryPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_ip_entry_set_property; + object_class->get_property = nmt_ip_entry_get_property; + + /** + * NmtIPEntry:family: + * + * The address family. Eg, %AF_INET + */ + g_object_class_install_property + (object_class, PROP_FAMILY, + g_param_spec_int ("family", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtIPEntry:prefix: + * + * If %TRUE, the entry accepts address/prefix combinations. If + * %FALSE it accepts just addresses. + */ + g_object_class_install_property + (object_class, PROP_PREFIX, + g_param_spec_boolean ("prefix", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtIPEntry:optional: + * + * If %TRUE, the entry will be #NmtNewtWidget:valid when it is + * empty. If %FALSE, it will only be valid when it contains a + * valid address or address/prefix. + */ + g_object_class_install_property + (object_class, PROP_OPTIONAL, + g_param_spec_boolean ("optional", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-ip-entry.h b/clients/tui/nmt-ip-entry.h new file mode 100644 index 00000000..ceb355ef --- /dev/null +++ b/clients/tui/nmt-ip-entry.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_IP_ENTRY_H +#define NMT_IP_ENTRY_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_IP_ENTRY (nmt_ip_entry_get_type ()) +#define NMT_IP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_IP_ENTRY, NmtIPEntry)) +#define NMT_IP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_IP_ENTRY, NmtIPEntryClass)) +#define NMT_IS_IP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_IP_ENTRY)) +#define NMT_IS_IP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_IP_ENTRY)) +#define NMT_IP_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_IP_ENTRY, NmtIPEntryClass)) + +typedef struct { + NmtNewtEntry parent; + +} NmtIPEntry; + +typedef struct { + NmtNewtEntryClass parent; + +} NmtIPEntryClass; + +GType nmt_ip_entry_get_type (void); + +NmtNewtWidget *nmt_ip_entry_new (int width, + int family, + gboolean prefix, + gboolean optional); + +G_END_DECLS + +#endif /* NMT_IP_ENTRY_H */ diff --git a/clients/tui/nmt-mac-entry.c b/clients/tui/nmt-mac-entry.c new file mode 100644 index 00000000..6501f09a --- /dev/null +++ b/clients/tui/nmt-mac-entry.c @@ -0,0 +1,227 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-mac-entry + * @short_description: #NmtNewtEntry for hardware address entry + * + * #NmtMacEntry is an #NmtNewtEntry for entering hardware addresses. + * It will only allow typing characters that are valid in a hardware + * address, and will set its #NmtNewtWidget:valid property depending + * on whether it currently contains a valid hardware address. + */ + +#include "config.h" + +#include <string.h> + +#include <NetworkManager.h> + +#include "nmt-mac-entry.h" + +G_DEFINE_TYPE (NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY) + +#define NMT_MAC_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_MAC_ENTRY, NmtMacEntryPrivate)) + +typedef struct { + int mac_length; + int mac_str_length; + +} NmtMacEntryPrivate; + +enum { + PROP_0, + PROP_MAC_LENGTH, + PROP_MAC_ADDRESS, + + LAST_PROP +}; + +/** + * nmt_mac_entry_new: + * @width: the width in characters of the entry + * @mac_length: the length in bytes of the hardware address + * (either %ETH_ALEN or %INFINIBAND_ALEN) + * + * Creates a new #NmtMacEntry. + * + * Returns: a new #NmtMacEntry. + */ +NmtNewtWidget * +nmt_mac_entry_new (int width, + int mac_length) +{ + return g_object_new (NMT_TYPE_MAC_ENTRY, + "width", width, + "mac-length", mac_length, + NULL); +} + +static gboolean +mac_filter (NmtNewtEntry *entry, + const char *text, + int ch, + int position, + gpointer user_data) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry); + + if (position >= priv->mac_str_length) + return FALSE; + + return g_ascii_isxdigit (ch) || ch == ':'; +} + +static gboolean +mac_validator (NmtNewtEntry *entry, + const char *text, + gpointer user_data) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry); + const char *p; + + if (!*text) + return TRUE; + + p = text; + while ( g_ascii_isxdigit (p[0]) + && g_ascii_isxdigit (p[1]) + && p[2] == ':') + p += 3; + + if ( !g_ascii_isxdigit (p[0]) + || !g_ascii_isxdigit (p[1])) + return FALSE; + p += 2; + + if (!*p) + return (p - text == priv->mac_str_length); + + if (g_ascii_isxdigit (p[0]) && !p[1]) { + char *fixed = g_strdup_printf ("%.*s:%c", (int)(p - text), text, *p); + + nmt_newt_entry_set_text (entry, fixed); + g_free (fixed); + + /* FIXME: NmtNewtEntry doesn't correctly deal with us calling set_text() + * from inside the validator. + */ + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry)); + } + + return FALSE; +} + +static void +nmt_mac_entry_init (NmtMacEntry *entry) +{ + nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), mac_filter, NULL); + nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), mac_validator, NULL); +} + +static void +nmt_mac_entry_notify (GObject *object, + GParamSpec *pspec) +{ + if (G_OBJECT_CLASS (nmt_mac_entry_parent_class)->notify) + G_OBJECT_CLASS (nmt_mac_entry_parent_class)->notify (object, pspec); + + if (pspec->owner_type == NMT_TYPE_NEWT_ENTRY && !strcmp (pspec->name, "text")) + g_object_notify (object, "mac-address"); +} + +static void +nmt_mac_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MAC_LENGTH: + priv->mac_length = g_value_get_int (value); + priv->mac_str_length = priv->mac_length * 3 - 1; + break; + case PROP_MAC_ADDRESS: + nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_mac_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MAC_LENGTH: + g_value_set_int (value, priv->mac_length); + break; + case PROP_MAC_ADDRESS: + g_value_set_string (value, nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_mac_entry_class_init (NmtMacEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtMacEntryPrivate)); + + /* virtual methods */ + object_class->notify = nmt_mac_entry_notify; + object_class->set_property = nmt_mac_entry_set_property; + object_class->get_property = nmt_mac_entry_get_property; + + /** + * NmtMacEntry:mac-length: + * + * The length in bytes of the hardware address type the entry + * accepts: either %ETH_ALEN or %INFINIBAND_ALEN. + */ + g_object_class_install_property + (object_class, PROP_MAC_LENGTH, + g_param_spec_int ("mac-length", "", "", + 0, INFINIBAND_ALEN, ETH_ALEN, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtMacEntry:mac-address: + * + * The MAC address, as a string (as with the various #NMSetting + * "mac-address" properties). + */ + g_object_class_install_property + (object_class, PROP_MAC_ADDRESS, + g_param_spec_string ("mac-address", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-mac-entry.h b/clients/tui/nmt-mac-entry.h new file mode 100644 index 00000000..2e3e6cd1 --- /dev/null +++ b/clients/tui/nmt-mac-entry.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_MAC_ENTRY_H +#define NMT_MAC_ENTRY_H + +#include "nm-utils.h" +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_MAC_ENTRY (nmt_mac_entry_get_type ()) +#define NMT_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntry)) +#define NMT_MAC_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass)) +#define NMT_IS_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_MAC_ENTRY)) +#define NMT_IS_MAC_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_MAC_ENTRY)) +#define NMT_MAC_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass)) + +typedef struct { + NmtNewtEntry parent; + +} NmtMacEntry; + +typedef struct { + NmtNewtEntryClass parent; + +} NmtMacEntryClass; + +GType nmt_mac_entry_get_type (void); + +NmtNewtWidget *nmt_mac_entry_new (int width, + int mac_length); + +G_END_DECLS + +#endif /* NMT_MAC_ENTRY_H */ diff --git a/clients/tui/nmt-mtu-entry.c b/clients/tui/nmt-mtu-entry.c new file mode 100644 index 00000000..f599557a --- /dev/null +++ b/clients/tui/nmt-mtu-entry.c @@ -0,0 +1,192 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-mtu-entry + * @short_description: #NmtNewtEntry for MTU entry + * + * #NmtMtuEntry is an #NmtNewtEntry for entering MTU values. It will + * only allow typing numeric characters, and will set its + * #NmtNewtWidget:valid property depending on whether it currently + * contains a valid MTU. + * + * The entry also has an attached #NmtNewtLabel. When the entry value + * is "0", the label will read "(default)". Otherwise it reads "bytes", + * indicating the units used by the entry. + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib/gi18n-lib.h> + +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE (NmtMtuEntry, nmt_mtu_entry, NMT_TYPE_NEWT_GRID) + +#define NMT_MTU_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_MTU_ENTRY, NmtMtuEntryPrivate)) + +typedef struct { + int mtu; + + NmtNewtEntry *entry; + NmtNewtLabel *label; + +} NmtMtuEntryPrivate; + +enum { + PROP_0, + PROP_MTU, + + LAST_PROP +}; + +/** + * nmt_mtu_entry_new: + * + * Creates a new #NmtMtuEntry + * + * Returns: a new #NmtMtuEntry + */ +NmtNewtWidget * +nmt_mtu_entry_new (void) +{ + return g_object_new (NMT_TYPE_MTU_ENTRY, NULL); +} + +static gboolean +mtu_validator (NmtNewtEntry *entry, + const char *text, + gpointer user_data) +{ + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (user_data); + + if (*text && !atoi (text)) { + nmt_newt_entry_set_text (entry, ""); + text = ""; + } + + if (!*text) + nmt_newt_label_set_text (priv->label, _("(default)")); + else + nmt_newt_label_set_text (priv->label, _("bytes")); + + return TRUE; +} + +static gboolean +mtu_transform_to_text (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + int mtu = g_value_get_int (source_value); + + if (mtu) + g_value_transform (source_value, target_value); + else + g_value_set_string (target_value, ""); + return TRUE; +} + +static void +nmt_mtu_entry_init (NmtMtuEntry *entry) +{ + + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (entry); + NmtNewtGrid *grid = NMT_NEWT_GRID (entry); + NmtNewtWidget *real_entry, *label; + + real_entry = nmt_newt_entry_numeric_new (10, 0, 65535); + priv->entry = NMT_NEWT_ENTRY (real_entry); + + label = nmt_newt_label_new (_("bytes")); + priv->label = NMT_NEWT_LABEL (label); + + nmt_newt_grid_add (grid, real_entry, 0, 0); + nmt_newt_grid_add (grid, label, 1, 0); + nmt_newt_widget_set_padding (label, 1, 0, 0, 0); + + nmt_newt_entry_set_validator (priv->entry, mtu_validator, entry); + g_object_bind_property_full (entry, "mtu", real_entry, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + mtu_transform_to_text, + NULL, + NULL, NULL); +} + +static void +nmt_mtu_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MTU: + priv->mtu = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_mtu_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MTU: + g_value_set_int (value, priv->mtu); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_mtu_entry_class_init (NmtMtuEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtMtuEntryPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_mtu_entry_set_property; + object_class->get_property = nmt_mtu_entry_get_property; + + /** + * NmtMtuEntry:mtu: + * + * The contents of the entry, as a number. + */ + g_object_class_install_property + (object_class, PROP_MTU, + g_param_spec_int ("mtu", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-mtu-entry.h b/clients/tui/nmt-mtu-entry.h new file mode 100644 index 00000000..dfe416d0 --- /dev/null +++ b/clients/tui/nmt-mtu-entry.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_MTU_ENTRY_H +#define NMT_MTU_ENTRY_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_MTU_ENTRY (nmt_mtu_entry_get_type ()) +#define NMT_MTU_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntry)) +#define NMT_MTU_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass)) +#define NMT_IS_MTU_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_MTU_ENTRY)) +#define NMT_IS_MTU_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_MTU_ENTRY)) +#define NMT_MTU_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtMtuEntry; + +typedef struct { + NmtNewtGridClass parent; + +} NmtMtuEntryClass; + +GType nmt_mtu_entry_get_type (void); + +NmtNewtWidget *nmt_mtu_entry_new (void); + +G_END_DECLS + +#endif /* NMT_MTU_ENTRY_H */ diff --git a/clients/tui/nmt-page-bond.c b/clients/tui/nmt-page-bond.c new file mode 100644 index 00000000..b043b513 --- /dev/null +++ b/clients/tui/nmt-page-bond.c @@ -0,0 +1,437 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-bond + * @short_description: The editor page for Bond connections + * + * Note that this is fairly different from most of the other editor + * pages, because #NMSettingBond doesn't have properties, so we + * can't just use #GBinding. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-bond.h" + +#include "nmt-address-list.h" +#include "nmt-slave-list.h" + +G_DEFINE_TYPE (NmtPageBond, nmt_page_bond, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_BOND, NmtPageBondPrivate)) + +typedef enum { + NMT_PAGE_BOND_MONITORING_UNKNOWN = -1, + NMT_PAGE_BOND_MONITORING_MII = 0, + NMT_PAGE_BOND_MONITORING_ARP = 1, +} NmtPageBondMonitoringMode; + +typedef struct { + NmtSlaveList *slaves; + + NmtNewtPopup *mode; + NmtNewtEntry *primary; + NmtNewtPopup *monitoring; + NmtNewtEntry *miimon; + NmtNewtEntry *updelay; + NmtNewtEntry *downdelay; + NmtNewtEntry *arp_interval; + NmtAddressList *arp_ip_target; + + NmtPageBondMonitoringMode monitoring_mode; + + NMSettingBond *s_bond; + GType slave_type; + gboolean updating; +} NmtPageBondPrivate; + +NmtEditorPage * +nmt_page_bond_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_BOND, + "connection", conn, + "device-entry", deventry, + NULL); +} + +static void +nmt_page_bond_init (NmtPageBond *bond) +{ + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + + priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_UNKNOWN; + priv->slave_type = G_TYPE_NONE; +} + +static NmtNewtPopupEntry bond_mode[] = { + { N_("Round-robin"), "balance-rr" }, + { N_("Active Backup"), "active-backup" }, + { N_("XOR"), "balance-xor" }, + { N_("Broadcast"), "broadcast" }, + { N_("802.3ad"), "802.3ad" }, + { N_("Adaptive Transmit Load Balancing (tlb)"), "balance-tlb" }, + { N_("Adaptive Load Balancing (alb)"), "balance-alb" }, + { NULL, NULL } +}; + +/* NB: the ordering/numbering here corresponds to NmtPageBondMonitoringMode */ +static NmtNewtPopupEntry bond_monitoring[] = { + { N_("MII (recommended)"), "mii" }, + { N_("ARP"), "arp" }, + { NULL, NULL } +}; + +static void +bond_options_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMSettingBond *s_bond = NM_SETTING_BOND (object); + NmtPageBond *bond = NMT_PAGE_BOND (user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + const char *val; + char **ips; + + if (priv->updating) + return; + + priv->updating = TRUE; + + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE); + nmt_newt_popup_set_active_id (priv->mode, val); + + if (!strcmp (val, "active-backup")) { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_PRIMARY); + nmt_newt_entry_set_text (priv->primary, val); + } else + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), FALSE); + + if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_UNKNOWN) { + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + if (val && strcmp (val, "0") != 0) + priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_ARP; + else + priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_MII; + } + nmt_newt_popup_set_active (priv->monitoring, priv->monitoring_mode); + + if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_MII) { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MIIMON); + nmt_newt_entry_set_text (priv->miimon, val); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_UPDELAY); + nmt_newt_entry_set_text (priv->updelay, val); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY); + nmt_newt_entry_set_text (priv->downdelay, val); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), FALSE); + } else { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + nmt_newt_entry_set_text (priv->arp_interval, val); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); + ips = g_strsplit (val, ",", -1); + g_object_set (G_OBJECT (priv->arp_ip_target), + "strings", ips, + NULL); + g_strfreev (ips); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), FALSE); + } + + priv->updating = FALSE; +} + +static void +slaves_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtPageBond *bond = NMT_PAGE_BOND (user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + GPtrArray *slaves; + + g_object_get (object, "connections", &slaves, NULL); + if (slaves->len == 0) { + if (priv->slave_type == G_TYPE_NONE) + return; + priv->slave_type = G_TYPE_NONE; + } else { + NMConnection *slave = slaves->pdata[0]; + + if (priv->slave_type != G_TYPE_NONE) + return; + + if (nm_connection_is_type (slave, NM_SETTING_INFINIBAND_SETTING_NAME)) + priv->slave_type = NM_TYPE_SETTING_INFINIBAND; + else + priv->slave_type = NM_TYPE_SETTING_WIRED; + } + + if (priv->slave_type == NM_TYPE_SETTING_INFINIBAND) { + nmt_newt_popup_set_active_id (priv->mode, "active-backup"); + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->mode), FALSE); + } else + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->mode), TRUE); +} + +#define WIDGET_CHANGED_FUNC(widget, func, option) \ +static void \ +widget ## _widget_changed (GObject *object, \ + GParamSpec *pspec, \ + gpointer user_data) \ +{ \ + NmtPageBond *bond = NMT_PAGE_BOND (user_data); \ + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); \ + \ + if (priv->updating) \ + return; \ + \ + priv->updating = TRUE; \ + nm_setting_bond_add_option (priv->s_bond, option, func (priv->widget)); \ + priv->updating = FALSE; \ +} + +WIDGET_CHANGED_FUNC (primary, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_PRIMARY) +WIDGET_CHANGED_FUNC (miimon, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_MIIMON) +WIDGET_CHANGED_FUNC (updelay, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_UPDELAY) +WIDGET_CHANGED_FUNC (downdelay, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_DOWNDELAY) +WIDGET_CHANGED_FUNC (arp_interval, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_ARP_INTERVAL) + +static void +mode_widget_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtPageBond *bond = NMT_PAGE_BOND (user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + const char *mode; + + if (priv->updating) + return; + + mode = nmt_newt_popup_get_active_id (priv->mode); + priv->updating = TRUE; + nm_setting_bond_add_option (priv->s_bond, NM_SETTING_BOND_OPTION_MODE, mode); + priv->updating = FALSE; + + if (!strcmp (mode, "balance-tlb") || !strcmp (mode, "balance-alb")) { + nmt_newt_popup_set_active (priv->monitoring, NMT_PAGE_BOND_MONITORING_MII); + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->monitoring), FALSE); + } else + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->monitoring), TRUE); + + if (!strcmp (mode, "active-backup")) { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), TRUE); + nm_setting_bond_add_option (priv->s_bond, NM_SETTING_BOND_OPTION_PRIMARY, + nmt_newt_entry_get_text (priv->primary)); + } else { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), FALSE); + nm_setting_bond_remove_option (priv->s_bond, NM_SETTING_BOND_OPTION_PRIMARY); + } +} + +static void +monitoring_widget_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtPageBond *bond = NMT_PAGE_BOND (user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + + if (priv->updating) + return; + + priv->monitoring_mode = nmt_newt_popup_get_active (priv->monitoring); + if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_MII) { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), TRUE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), TRUE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), TRUE); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), FALSE); + } else { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), TRUE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), TRUE); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), FALSE); + } +} + +static void +arp_ip_target_widget_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtPageBond *bond = NMT_PAGE_BOND (user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + char **ips, *target; + + if (priv->updating) + return; + + g_object_get (G_OBJECT (priv->arp_ip_target), + "strings", &ips, + NULL); + target = g_strjoinv (",", ips); + + priv->updating = TRUE; + nm_setting_bond_add_option (priv->s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, target); + priv->updating = FALSE; + + g_free (target); + g_strfreev (ips); +} + +static gboolean +bond_connection_type_filter (GType connection_type, + gpointer user_data) +{ + NmtPageBond *bond = user_data; + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + + if ( priv->slave_type != NM_TYPE_SETTING_WIRED + && connection_type == NM_TYPE_SETTING_INFINIBAND) + return TRUE; + if ( priv->slave_type != NM_TYPE_SETTING_INFINIBAND + && connection_type == NM_TYPE_SETTING_WIRED) + return TRUE; + + return FALSE; +} + +static void +nmt_page_bond_constructed (GObject *object) +{ + NmtPageBond *bond = NMT_PAGE_BOND (object); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + NmtEditorSection *section; + NmtEditorGrid *grid; + NMSettingBond *s_bond; + NmtNewtWidget *widget, *label; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (bond)); + s_bond = nm_connection_get_setting_bond (conn); + if (!s_bond) { + nm_connection_add_setting (conn, nm_setting_bond_new ()); + s_bond = nm_connection_get_setting_bond (conn); + } + priv->s_bond = s_bond; + + section = nmt_editor_section_new (_("BOND"), NULL, TRUE); + grid = nmt_editor_section_get_body (section); + + widget = nmt_newt_separator_new (); + nmt_editor_grid_append (grid, _("Slaves"), widget, NULL); + nmt_editor_grid_set_row_flags (grid, widget, NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT); + + widget = nmt_slave_list_new (conn, bond_connection_type_filter, bond); + g_signal_connect (widget, "notify::connections", + G_CALLBACK (slaves_changed), bond); + nmt_editor_grid_append (grid, NULL, widget, NULL); + priv->slaves = NMT_SLAVE_LIST (widget); + + widget = nmt_newt_popup_new (bond_mode); + g_signal_connect (widget, "notify::active-id", + G_CALLBACK (mode_widget_changed), bond); + nmt_editor_grid_append (grid, _("Mode"), widget, NULL); + priv->mode = NMT_NEWT_POPUP (widget); + + widget = nmt_newt_entry_new (40, 0); + g_signal_connect (widget, "notify::text", + G_CALLBACK (primary_widget_changed), bond); + nmt_editor_grid_append (grid, _("Primary"), widget, NULL); + priv->primary = NMT_NEWT_ENTRY (widget); + + widget = nmt_newt_popup_new (bond_monitoring); + g_signal_connect (widget, "notify::active", + G_CALLBACK (monitoring_widget_changed), bond); + nmt_editor_grid_append (grid, _("Link monitoring"), widget, NULL); + priv->monitoring = NMT_NEWT_POPUP (widget); + + widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT); + g_signal_connect (widget, "notify::text", + G_CALLBACK (miimon_widget_changed), bond); + label = nmt_newt_label_new (C_("milliseconds", "ms")); + nmt_editor_grid_append (grid, _("Monitoring frequency"), widget, label); + priv->miimon = NMT_NEWT_ENTRY (widget); + + widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT); + g_signal_connect (widget, "notify::text", + G_CALLBACK (updelay_widget_changed), bond); + label = nmt_newt_label_new (C_("milliseconds", "ms")); + nmt_editor_grid_append (grid, _("Link up delay"), widget, label); + priv->updelay = NMT_NEWT_ENTRY (widget); + + widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT); + g_signal_connect (widget, "notify::text", + G_CALLBACK (downdelay_widget_changed), bond); + label = nmt_newt_label_new (C_("milliseconds", "ms")); + nmt_editor_grid_append (grid, _("Link down delay"), widget, label); + priv->downdelay = NMT_NEWT_ENTRY (widget); + + widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT); + g_signal_connect (widget, "notify::text", + G_CALLBACK (arp_interval_widget_changed), bond); + label = nmt_newt_label_new (C_("milliseconds", "ms")); + nmt_editor_grid_append (grid, _("Monitoring frequency"), widget, label); + priv->arp_interval = NMT_NEWT_ENTRY (widget); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4); + g_signal_connect (widget, "notify::strings", + G_CALLBACK (arp_ip_target_widget_changed), bond); + nmt_editor_grid_append (grid, _("ARP targets"), widget, NULL); + priv->arp_ip_target = NMT_ADDRESS_LIST (widget); + + g_signal_connect (s_bond, "notify::" NM_SETTING_BOND_OPTIONS, + G_CALLBACK (bond_options_changed), bond); + bond_options_changed (G_OBJECT (s_bond), NULL, bond); + slaves_changed (G_OBJECT (priv->slaves), NULL, bond); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (bond), section); + + G_OBJECT_CLASS (nmt_page_bond_parent_class)->constructed (object); +} + +static void +nmt_page_bond_class_init (NmtPageBondClass *bond_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (bond_class); + + g_type_class_add_private (bond_class, sizeof (NmtPageBondPrivate)); + + object_class->constructed = nmt_page_bond_constructed; +} diff --git a/clients/tui/nmt-page-bond.h b/clients/tui/nmt-page-bond.h new file mode 100644 index 00000000..b77477db --- /dev/null +++ b/clients/tui/nmt-page-bond.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_BOND_H +#define NMT_PAGE_BOND_H + +#include "nmt-editor-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_BOND (nmt_page_bond_get_type ()) +#define NMT_PAGE_BOND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BOND, NmtPageBond)) +#define NMT_PAGE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BOND, NmtPageBondClass)) +#define NMT_IS_PAGE_BOND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_BOND)) +#define NMT_IS_PAGE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_BOND)) +#define NMT_PAGE_BOND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_BOND, NmtPageBondClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageBond; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageBondClass; + +GType nmt_page_bond_get_type (void); + +NmtEditorPage *nmt_page_bond_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_BOND_H */ diff --git a/clients/tui/nmt-page-bridge-port.c b/clients/tui/nmt-page-bridge-port.c new file mode 100644 index 00000000..c9ef14ed --- /dev/null +++ b/clients/tui/nmt-page-bridge-port.c @@ -0,0 +1,95 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-bridge-port + * @short_description: The editor page for Bridge ports + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-bridge-port.h" + +G_DEFINE_TYPE (NmtPageBridgePort, nmt_page_bridge_port, NMT_TYPE_EDITOR_PAGE) + +NmtEditorPage * +nmt_page_bridge_port_new (NMConnection *conn) +{ + return g_object_new (NMT_TYPE_PAGE_BRIDGE_PORT, + "connection", conn, + NULL); +} + +static void +nmt_page_bridge_port_init (NmtPageBridgePort *bridge) +{ +} + +static void +nmt_page_bridge_port_constructed (GObject *object) +{ + NmtPageBridgePort *bridge = NMT_PAGE_BRIDGE_PORT (object); + NmtEditorSection *section; + NmtEditorGrid *grid; + NMSettingBridgePort *s_port; + NmtNewtWidget *widget; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (bridge)); + s_port = nm_connection_get_setting_bridge_port (conn); + if (!s_port) { + nm_connection_add_setting (conn, nm_setting_bridge_port_new ()); + s_port = nm_connection_get_setting_bridge_port (conn); + } + + section = nmt_editor_section_new (_("BRIDGE PORT"), NULL, TRUE); + grid = nmt_editor_section_get_body (section); + + widget = nmt_newt_entry_numeric_new (10, 0, 63); + g_object_bind_property (s_port, NM_SETTING_BRIDGE_PORT_PRIORITY, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Priority"), widget, NULL); + + widget = nmt_newt_entry_numeric_new (10, 1, 65535); + g_object_bind_property (s_port, NM_SETTING_BRIDGE_PORT_PATH_COST, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Path cost"), widget, NULL); + + widget = nmt_newt_checkbox_new (_("Hairpin mode")); + g_object_bind_property (s_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (bridge), section); + + G_OBJECT_CLASS (nmt_page_bridge_port_parent_class)->constructed (object); +} + +static void +nmt_page_bridge_port_class_init (NmtPageBridgePortClass *bridge_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (bridge_class); + + object_class->constructed = nmt_page_bridge_port_constructed; +} diff --git a/clients/tui/nmt-page-bridge-port.h b/clients/tui/nmt-page-bridge-port.h new file mode 100644 index 00000000..c566dbc0 --- /dev/null +++ b/clients/tui/nmt-page-bridge-port.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_BRIDGE_PORT_H +#define NMT_PAGE_BRIDGE_PORT_H + +#include "nmt-editor-page.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_BRIDGE_PORT (nmt_page_bridge_port_get_type ()) +#define NMT_PAGE_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePort)) +#define NMT_PAGE_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass)) +#define NMT_IS_PAGE_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_BRIDGE_PORT)) +#define NMT_IS_PAGE_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_BRIDGE_PORT)) +#define NMT_PAGE_BRIDGE_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageBridgePort; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageBridgePortClass; + +GType nmt_page_bridge_port_get_type (void); + +NmtEditorPage *nmt_page_bridge_port_new (NMConnection *conn); + +G_END_DECLS + +#endif /* NMT_PAGE_BRIDGE_PORT_H */ diff --git a/clients/tui/nmt-page-bridge.c b/clients/tui/nmt-page-bridge.c new file mode 100644 index 00000000..460be60b --- /dev/null +++ b/clients/tui/nmt-page-bridge.c @@ -0,0 +1,150 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-bridge + * @short_description: The editor page for Bridge connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-bridge.h" + +#include "nmt-address-list.h" +#include "nmt-slave-list.h" + +G_DEFINE_TYPE (NmtPageBridge, nmt_page_bridge, NMT_TYPE_EDITOR_PAGE_DEVICE) + +NmtEditorPage * +nmt_page_bridge_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_BRIDGE, + "connection", conn, + "device-entry", deventry, + NULL); +} + +static void +nmt_page_bridge_init (NmtPageBridge *bridge) +{ +} + +static gboolean +bridge_connection_type_filter (GType connection_type, + gpointer user_data) +{ + return ( connection_type == NM_TYPE_SETTING_WIRED + || connection_type == NM_TYPE_SETTING_WIRELESS + || connection_type == NM_TYPE_SETTING_VLAN); +} + +static void +nmt_page_bridge_constructed (GObject *object) +{ + NmtPageBridge *bridge = NMT_PAGE_BRIDGE (object); + NmtEditorSection *section; + NmtEditorGrid *grid; + NMSettingBridge *s_bridge; + NmtNewtWidget *widget, *label, *stp; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (bridge)); + s_bridge = nm_connection_get_setting_bridge (conn); + if (!s_bridge) { + nm_connection_add_setting (conn, nm_setting_bridge_new ()); + s_bridge = nm_connection_get_setting_bridge (conn); + } + + section = nmt_editor_section_new (_("BRIDGE"), NULL, TRUE); + grid = nmt_editor_section_get_body (section); + + widget = nmt_newt_separator_new (); + nmt_editor_grid_append (grid, _("Slaves"), widget, NULL); + nmt_editor_grid_set_row_flags (grid, widget, NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT); + + widget = nmt_slave_list_new (conn, bridge_connection_type_filter, bridge); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + widget = nmt_newt_entry_numeric_new (10, 0, 1000000); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new (_("seconds")); + nmt_editor_grid_append (grid, _("Aging time"), widget, label); + + widget = stp = nmt_newt_checkbox_new (_("Enable STP (Spanning Tree Protocol)")); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP, + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_PRIORITY, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP, + widget, "sensitive", + G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Priority"), widget, NULL); + + widget = nmt_newt_entry_numeric_new (10, 2, 30); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP, + widget, "sensitive", + G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new (_("seconds")); + nmt_editor_grid_append (grid, _("Forward delay"), widget, label); + + widget = nmt_newt_entry_numeric_new (10, 1, 10); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP, + widget, "sensitive", + G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new (_("seconds")); + nmt_editor_grid_append (grid, _("Hello time"), widget, label); + + widget = nmt_newt_entry_numeric_new (10, 6, 40); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP, + widget, "sensitive", + G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new (_("seconds")); + nmt_editor_grid_append (grid, _("Max age"), widget, label); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (bridge), section); + + G_OBJECT_CLASS (nmt_page_bridge_parent_class)->constructed (object); +} + +static void +nmt_page_bridge_class_init (NmtPageBridgeClass *bridge_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (bridge_class); + + object_class->constructed = nmt_page_bridge_constructed; +} diff --git a/clients/tui/nmt-page-bridge.h b/clients/tui/nmt-page-bridge.h new file mode 100644 index 00000000..60cbdea2 --- /dev/null +++ b/clients/tui/nmt-page-bridge.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_BRIDGE_H +#define NMT_PAGE_BRIDGE_H + +#include "nmt-editor-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_BRIDGE (nmt_page_bridge_get_type ()) +#define NMT_PAGE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BRIDGE, NmtPageBridge)) +#define NMT_PAGE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BRIDGE, NmtPageBridgeClass)) +#define NMT_IS_PAGE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_BRIDGE)) +#define NMT_IS_PAGE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_BRIDGE)) +#define NMT_PAGE_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_BRIDGE, NmtPageBridgeClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageBridge; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageBridgeClass; + +GType nmt_page_bridge_get_type (void); + +NmtEditorPage *nmt_page_bridge_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_BRIDGE_H */ diff --git a/clients/tui/nmt-page-dsl.c b/clients/tui/nmt-page-dsl.c new file mode 100644 index 00000000..e8894d1a --- /dev/null +++ b/clients/tui/nmt-page-dsl.c @@ -0,0 +1,141 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2014 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-dsl + * @short_description: The editor page for DSL connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-dsl.h" +#include "nmt-page-ethernet.h" +#include "nmt-page-ppp.h" +#include "nmt-password-fields.h" + +G_DEFINE_TYPE (NmtPageDsl, nmt_page_dsl, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_DSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_DSL, NmtPageDslPrivate)) + +typedef struct { + NmtEditorPage *ethernet_page, *ppp_page; + +} NmtPageDslPrivate; + +NmtEditorPage * +nmt_page_dsl_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_DSL, + "connection", conn, + "device-entry", deventry, + NULL); +} + +static void +nmt_page_dsl_init (NmtPageDsl *dsl) +{ +} + +static NmtEditorSection * +build_dsl_section (NmtPageDsl *dsl, NMSettingPppoe *s_pppoe) +{ + NmtEditorSection *section; + NmtEditorGrid *grid; + NmtNewtWidget *widget; + + section = nmt_editor_section_new (_("DSL"), NULL, TRUE); + grid = nmt_editor_section_get_body (section); + + widget = nmt_newt_entry_new (40, 0); + nmt_editor_grid_append (grid, _("Username"), widget, NULL); + g_object_bind_property (s_pppoe, NM_SETTING_PPPOE_USERNAME, + widget, "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + widget = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + g_object_bind_property (s_pppoe, NM_SETTING_PPPOE_PASSWORD, + widget, "password", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append (grid, _("Password"), widget, NULL); + + widget = nmt_newt_entry_new (40, 0); + nmt_editor_grid_append (grid, _("Service"), widget, NULL); + g_object_bind_property (s_pppoe, NM_SETTING_PPPOE_SERVICE, + widget, "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + return section; +} + +static void +nmt_page_dsl_constructed (GObject *object) +{ + NmtPageDsl *dsl = NMT_PAGE_DSL (object); + NmtPageDslPrivate *priv = NMT_PAGE_DSL_GET_PRIVATE (dsl); + NMConnection *conn; + NMSettingPppoe *s_pppoe; + NmtEditorSection *section; + const GSList *sections, *iter; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (dsl)); + s_pppoe = nm_connection_get_setting_pppoe (conn); + if (!s_pppoe) { + nm_connection_add_setting (conn, nm_setting_pppoe_new ()); + s_pppoe = nm_connection_get_setting_pppoe (conn); + } + + section = build_dsl_section (dsl, s_pppoe); + nmt_editor_page_add_section (NMT_EDITOR_PAGE (dsl), section); + + priv->ethernet_page = nmt_page_ethernet_new (conn, nmt_editor_page_device_get_device_entry (NMT_EDITOR_PAGE_DEVICE (dsl))); + sections = nmt_editor_page_get_sections (priv->ethernet_page); + for (iter = sections; iter; iter = iter->next) + nmt_editor_page_add_section (NMT_EDITOR_PAGE (dsl), iter->data); + + priv->ppp_page = nmt_page_ppp_new (conn); + sections = nmt_editor_page_get_sections (priv->ppp_page); + for (iter = sections; iter; iter = iter->next) + nmt_editor_page_add_section (NMT_EDITOR_PAGE (dsl), iter->data); + + G_OBJECT_CLASS (nmt_page_dsl_parent_class)->constructed (object); +} + +static void +nmt_page_dsl_finalize (GObject *object) +{ + NmtPageDsl *dsl = NMT_PAGE_DSL (object); + NmtPageDslPrivate *priv = NMT_PAGE_DSL_GET_PRIVATE (dsl); + + g_clear_object (&priv->ethernet_page); + g_clear_object (&priv->ppp_page); + + G_OBJECT_CLASS (nmt_page_dsl_parent_class)->finalize (object); +} + +static void +nmt_page_dsl_class_init (NmtPageDslClass *dsl_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (dsl_class); + + object_class->constructed = nmt_page_dsl_constructed; + object_class->finalize = nmt_page_dsl_finalize; +} diff --git a/clients/tui/nmt-page-dsl.h b/clients/tui/nmt-page-dsl.h new file mode 100644 index 00000000..b938ead2 --- /dev/null +++ b/clients/tui/nmt-page-dsl.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_DSL_H +#define NMT_PAGE_DSL_H + +#include "nmt-editor-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_DSL (nmt_page_dsl_get_type ()) +#define NMT_PAGE_DSL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_DSL, NmtPageDsl)) +#define NMT_PAGE_DSL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_DSL, NmtPageDslClass)) +#define NMT_IS_PAGE_DSL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_DSL)) +#define NMT_IS_PAGE_DSL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_DSL)) +#define NMT_PAGE_DSL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_DSL, NmtPageDslClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageDsl; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageDslClass; + +GType nmt_page_dsl_get_type (void); + +NmtEditorPage *nmt_page_dsl_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_DSL_H */ diff --git a/clients/tui/nmt-page-ethernet.c b/clients/tui/nmt-page-ethernet.c new file mode 100644 index 00000000..cf4ef2c4 --- /dev/null +++ b/clients/tui/nmt-page-ethernet.c @@ -0,0 +1,99 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ethernet + * @short_description: The editor page for Ethernet connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-ethernet.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE (NmtPageEthernet, nmt_page_ethernet, NMT_TYPE_EDITOR_PAGE_DEVICE) + +NmtEditorPage * +nmt_page_ethernet_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_ETHERNET, + "connection", conn, + "device-entry", deventry, + NULL); +} + +static void +nmt_page_ethernet_init (NmtPageEthernet *ethernet) +{ +} + +static void +nmt_page_ethernet_constructed (GObject *object) +{ + NmtPageEthernet *ethernet = NMT_PAGE_ETHERNET (object); + NmtDeviceEntry *deventry; + NmtEditorSection *section; + NmtEditorGrid *grid; + NMSettingWired *s_wired; + NmtNewtWidget *widget; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ethernet)); + s_wired = nm_connection_get_setting_wired (conn); + if (!s_wired) { + nm_connection_add_setting (conn, nm_setting_wired_new ()); + s_wired = nm_connection_get_setting_wired (conn); + } + + deventry = nmt_editor_page_device_get_device_entry (NMT_EDITOR_PAGE_DEVICE (object)); + g_object_bind_property (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, + deventry, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + section = nmt_editor_section_new (_("ETHERNET"), NULL, FALSE); + grid = nmt_editor_section_get_body (section); + + widget = nmt_mac_entry_new (40, ETH_ALEN); + g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + widget, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Cloned MAC address"), widget, NULL); + + widget = nmt_mtu_entry_new (); + g_object_bind_property (s_wired, NM_SETTING_WIRED_MTU, + widget, "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("MTU"), widget, NULL); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (ethernet), section); + + G_OBJECT_CLASS (nmt_page_ethernet_parent_class)->constructed (object); +} + +static void +nmt_page_ethernet_class_init (NmtPageEthernetClass *ethernet_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ethernet_class); + + object_class->constructed = nmt_page_ethernet_constructed; +} diff --git a/clients/tui/nmt-page-ethernet.h b/clients/tui/nmt-page-ethernet.h new file mode 100644 index 00000000..bdf3d677 --- /dev/null +++ b/clients/tui/nmt-page-ethernet.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_ETHERNET_H +#define NMT_PAGE_ETHERNET_H + +#include "nmt-editor-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_ETHERNET (nmt_page_ethernet_get_type ()) +#define NMT_PAGE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernet)) +#define NMT_PAGE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernetClass)) +#define NMT_IS_PAGE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_ETHERNET)) +#define NMT_IS_PAGE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_ETHERNET)) +#define NMT_PAGE_ETHERNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernetClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageEthernet; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageEthernetClass; + +GType nmt_page_ethernet_get_type (void); + +NmtEditorPage *nmt_page_ethernet_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_ETHERNET_H */ diff --git a/clients/tui/nmt-page-infiniband.c b/clients/tui/nmt-page-infiniband.c new file mode 100644 index 00000000..f39b0b84 --- /dev/null +++ b/clients/tui/nmt-page-infiniband.c @@ -0,0 +1,110 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-infiniband + * @short_description: The editor page for InfiniBand connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-infiniband.h" +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE (NmtPageInfiniband, nmt_page_infiniband, NMT_TYPE_EDITOR_PAGE_DEVICE) + +NmtEditorPage * +nmt_page_infiniband_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_INFINIBAND, + "connection", conn, + "device-entry", deventry, + NULL); +} + +static void +nmt_page_infiniband_init (NmtPageInfiniband *infiniband) +{ +} + +static NmtNewtPopupEntry transport_mode[] = { + { N_("Datagram"), "datagram" }, + { N_("Connected"), "connected" }, + { NULL, NULL } +}; + +static void +nmt_page_infiniband_constructed (GObject *object) +{ + NmtPageInfiniband *infiniband = NMT_PAGE_INFINIBAND (object); + NmtDeviceEntry *deventry; + NmtEditorSection *section; + NmtEditorGrid *grid; + NMSettingInfiniband *s_ib; + NmtNewtWidget *widget; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (infiniband)); + s_ib = nm_connection_get_setting_infiniband (conn); + if (!s_ib) { + nm_connection_add_setting (conn, nm_setting_infiniband_new ()); + s_ib = nm_connection_get_setting_infiniband (conn); + } + /* initialize 'transport-mode' if it is NULL */ + if (!nm_setting_infiniband_get_transport_mode (s_ib)) { + g_object_set (G_OBJECT (s_ib), + NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", + NULL); + } + + deventry = nmt_editor_page_device_get_device_entry (NMT_EDITOR_PAGE_DEVICE (object)); + g_object_bind_property (s_ib, NM_SETTING_INFINIBAND_MAC_ADDRESS, + deventry, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + section = nmt_editor_section_new (_("INFINIBAND"), NULL, TRUE); + grid = nmt_editor_section_get_body (section); + + widget = nmt_newt_popup_new (transport_mode); + g_object_bind_property (s_ib, NM_SETTING_INFINIBAND_TRANSPORT_MODE, + widget, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Transport mode"), widget, NULL); + + widget = nmt_mtu_entry_new (); + g_object_bind_property (s_ib, NM_SETTING_INFINIBAND_MTU, + widget, "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("MTU"), widget, NULL); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (infiniband), section); + + G_OBJECT_CLASS (nmt_page_infiniband_parent_class)->constructed (object); +} + +static void +nmt_page_infiniband_class_init (NmtPageInfinibandClass *infiniband_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (infiniband_class); + + object_class->constructed = nmt_page_infiniband_constructed; +} diff --git a/clients/tui/nmt-page-infiniband.h b/clients/tui/nmt-page-infiniband.h new file mode 100644 index 00000000..f51e0874 --- /dev/null +++ b/clients/tui/nmt-page-infiniband.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_INFINIBAND_H +#define NMT_PAGE_INFINIBAND_H + +#include "nmt-editor-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_INFINIBAND (nmt_page_infiniband_get_type ()) +#define NMT_PAGE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfiniband)) +#define NMT_PAGE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfinibandClass)) +#define NMT_IS_PAGE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_INFINIBAND)) +#define NMT_IS_PAGE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_INFINIBAND)) +#define NMT_PAGE_INFINIBAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfinibandClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageInfiniband; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageInfinibandClass; + +GType nmt_page_infiniband_get_type (void); + +NmtEditorPage *nmt_page_infiniband_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_INFINIBAND_H */ diff --git a/clients/tui/nmt-page-ip4.c b/clients/tui/nmt-page-ip4.c new file mode 100644 index 00000000..df3f0b64 --- /dev/null +++ b/clients/tui/nmt-page-ip4.c @@ -0,0 +1,202 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ip4 + * @short_description: The editor page for IP4 configuration + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-ip4.h" +#include "nmt-ip-entry.h" +#include "nmt-address-list.h" +#include "nmt-route-editor.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE (NmtPageIP4, nmt_page_ip4, NMT_TYPE_EDITOR_PAGE) + +static NmtNewtPopupEntry ip4methods[] = { + { N_("Disabled"), NM_SETTING_IP4_CONFIG_METHOD_DISABLED }, + { N_("Automatic"), NM_SETTING_IP4_CONFIG_METHOD_AUTO }, + { N_("Link-Local"), NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL }, + { N_("Manual"), NM_SETTING_IP4_CONFIG_METHOD_MANUAL }, + { N_("Shared"), NM_SETTING_IP4_CONFIG_METHOD_SHARED }, + { NULL, NULL } +}; + +NmtEditorPage * +nmt_page_ip4_new (NMConnection *conn) +{ + return g_object_new (NMT_TYPE_PAGE_IP4, + "connection", conn, + NULL); +} + +static void +nmt_page_ip4_init (NmtPageIP4 *ip4) +{ +} + +static void +edit_routes (NmtNewtButton *button, + gpointer user_data) +{ + NMSetting *s_ip4 = user_data; + NmtNewtForm *form; + + form = nmt_route_editor_new (s_ip4); + nmt_newt_form_run_sync (form); + g_object_unref (form); +} + +static gboolean +ip4_routes_transform_to_description (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *routes; + char *text; + + routes = g_value_get_boxed (source_value); + if (!routes || !routes->len) + text = g_strdup (_("(No custom routes)")); + else { + text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, + "One custom route", + "%d custom routes", + routes->len), + routes->len); + } + + g_value_take_string (target_value, text); + return TRUE; +} + +static void +nmt_page_ip4_constructed (GObject *object) +{ + NmtPageIP4 *ip4 = NMT_PAGE_IP4 (object); + gboolean show_by_default; + NmtEditorSection *section; + NmtEditorGrid *grid; + NMSettingIPConfig *s_ip4; + NmtNewtWidget *widget, *button; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ip4)); + s_ip4 = nm_connection_get_setting_ip4_config (conn); + if (!s_ip4) { + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + g_object_set (G_OBJECT (s_ip4), + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + nm_connection_add_setting (conn, (NMSetting *) s_ip4); + } + + widget = nmt_newt_popup_new (ip4methods); + g_object_bind_property (s_ip4, NM_SETTING_IP_CONFIG_METHOD, + widget, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + if (!g_strcmp0 (nm_setting_ip_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + show_by_default = TRUE; + else if (nm_setting_ip_config_get_num_addresses (s_ip4)) + show_by_default = TRUE; + else + show_by_default = FALSE; + + section = nmt_editor_section_new (_("IPv4 CONFIGURATION"), widget, show_by_default); + grid = nmt_editor_section_get_body (section); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4_WITH_PREFIX); + nm_editor_bind_ip_addresses_with_prefix_to_strv (AF_INET, + s_ip4, NM_SETTING_IP_CONFIG_ADDRESSES, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Addresses"), widget, NULL); + + widget = nmt_ip_entry_new (25, AF_INET, FALSE, TRUE); + nm_editor_bind_ip_gateway_to_string (AF_INET, + s_ip4, + widget, "text", "sensitive", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Gateway"), widget, NULL); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4); + nm_editor_bind_ip_addresses_to_strv (AF_INET, + s_ip4, NM_SETTING_IP_CONFIG_DNS, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("DNS servers"), widget, NULL); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_HOSTNAME); + g_object_bind_property (s_ip4, NM_SETTING_IP_CONFIG_DNS_SEARCH, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Search domains"), widget, NULL); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = g_object_new (NMT_TYPE_NEWT_LABEL, + "text", "", + "style", NMT_NEWT_LABEL_PLAIN, + NULL); + g_object_bind_property_full (s_ip4, NM_SETTING_IP_CONFIG_ROUTES, + widget, "text", + G_BINDING_SYNC_CREATE, + ip4_routes_transform_to_description, + NULL, NULL, NULL); + button = nmt_newt_button_new (_("Edit...")); + g_signal_connect (button, "clicked", G_CALLBACK (edit_routes), s_ip4); + nmt_editor_grid_append (grid, _("Routing"), widget, button); + + widget = nmt_newt_checkbox_new (_("Never use this network for default route")); + g_object_bind_property (s_ip4, NM_SETTING_IP_CONFIG_NEVER_DEFAULT, + widget, "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_newt_checkbox_new (_("Require IPv4 addressing for this connection")); + g_object_bind_property (s_ip4, NM_SETTING_IP_CONFIG_MAY_FAIL, + widget, "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL | + G_BINDING_INVERT_BOOLEAN); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (ip4), section); + + G_OBJECT_CLASS (nmt_page_ip4_parent_class)->constructed (object); +} + +static void +nmt_page_ip4_class_init (NmtPageIP4Class *ip4_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ip4_class); + + object_class->constructed = nmt_page_ip4_constructed; +} diff --git a/clients/tui/nmt-page-ip4.h b/clients/tui/nmt-page-ip4.h new file mode 100644 index 00000000..e3c5a003 --- /dev/null +++ b/clients/tui/nmt-page-ip4.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_IP4_H +#define NMT_PAGE_IP4_H + +#include "nmt-editor-page.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_IP4 (nmt_page_ip4_get_type ()) +#define NMT_PAGE_IP4(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_IP4, NmtPageIP4)) +#define NMT_PAGE_IP4_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_IP4, NmtPageIP4Class)) +#define NMT_IS_PAGE_IP4(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_IP4)) +#define NMT_IS_PAGE_IP4_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_IP4)) +#define NMT_PAGE_IP4_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_IP4, NmtPageIP4Class)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageIP4; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageIP4Class; + +GType nmt_page_ip4_get_type (void); + +NmtEditorPage *nmt_page_ip4_new (NMConnection *conn); + +G_END_DECLS + +#endif /* NMT_PAGE_IP4_H */ diff --git a/clients/tui/nmt-page-ip6.c b/clients/tui/nmt-page-ip6.c new file mode 100644 index 00000000..1513dbfa --- /dev/null +++ b/clients/tui/nmt-page-ip6.c @@ -0,0 +1,200 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ip6 + * @short_description: The editor page for IP6 configuration + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-ip6.h" +#include "nmt-ip-entry.h" +#include "nmt-address-list.h" +#include "nmt-route-editor.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE (NmtPageIP6, nmt_page_ip6, NMT_TYPE_EDITOR_PAGE) + +static NmtNewtPopupEntry ip6methods[] = { + { N_("Ignore"), NM_SETTING_IP6_CONFIG_METHOD_IGNORE }, + { N_("Automatic"), NM_SETTING_IP6_CONFIG_METHOD_AUTO }, + { N_("Automatic (DHCP-only)"), NM_SETTING_IP6_CONFIG_METHOD_DHCP }, + { N_("Link-Local"), NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL }, + { N_("Manual"), NM_SETTING_IP6_CONFIG_METHOD_MANUAL }, + { NULL, NULL } +}; + +NmtEditorPage * +nmt_page_ip6_new (NMConnection *conn) +{ + return g_object_new (NMT_TYPE_PAGE_IP6, + "connection", conn, + NULL); +} + +static void +nmt_page_ip6_init (NmtPageIP6 *ip6) +{ +} + +static void +edit_routes (NmtNewtButton *button, + gpointer user_data) +{ + NMSetting *s_ip6 = user_data; + NmtNewtForm *form; + + form = nmt_route_editor_new (s_ip6); + nmt_newt_form_run_sync (form); + g_object_unref (form); +} + +static gboolean +ip6_routes_transform_to_description (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *routes; + char *text; + + routes = g_value_get_boxed (source_value); + if (!routes || !routes->len) + text = g_strdup (_("(No custom routes)")); + else { + text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, + "One custom route", + "%d custom routes", + routes->len), + routes->len); + } + + g_value_take_string (target_value, text); + return TRUE; +} + +static void +nmt_page_ip6_constructed (GObject *object) +{ + NmtPageIP6 *ip6 = NMT_PAGE_IP6 (object); + gboolean show_by_default; + NmtEditorSection *section; + NmtEditorGrid *grid; + NMSettingIPConfig *s_ip6; + NmtNewtWidget *widget, *button; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ip6)); + s_ip6 = nm_connection_get_setting_ip6_config (conn); + if (!s_ip6) { + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + g_object_set (G_OBJECT (s_ip6), + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + nm_connection_add_setting (conn, (NMSetting *) s_ip6); + } + + widget = nmt_newt_popup_new (ip6methods); + g_object_bind_property (s_ip6, NM_SETTING_IP_CONFIG_METHOD, + widget, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + if (!g_strcmp0 (nm_setting_ip_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) + show_by_default = TRUE; + else if (nm_setting_ip_config_get_num_addresses (s_ip6)) + show_by_default = TRUE; + else + show_by_default = FALSE; + + section = nmt_editor_section_new (_("IPv6 CONFIGURATION"), widget, show_by_default); + grid = nmt_editor_section_get_body (section); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP6_WITH_PREFIX); + nm_editor_bind_ip_addresses_with_prefix_to_strv (AF_INET6, + s_ip6, NM_SETTING_IP_CONFIG_ADDRESSES, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Addresses"), widget, NULL); + + widget = nmt_ip_entry_new (25, AF_INET6, FALSE, TRUE); + nm_editor_bind_ip_gateway_to_string (AF_INET6, + s_ip6, + widget, "text", "sensitive", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Gateway"), widget, NULL); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP6); + nm_editor_bind_ip_addresses_to_strv (AF_INET6, + s_ip6, NM_SETTING_IP_CONFIG_DNS, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("DNS servers"), widget, NULL); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_HOSTNAME); + g_object_bind_property (s_ip6, NM_SETTING_IP_CONFIG_DNS_SEARCH, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Search domains"), widget, NULL); + + widget = g_object_new (NMT_TYPE_NEWT_LABEL, + "text", "", + "style", NMT_NEWT_LABEL_PLAIN, + NULL); + g_object_bind_property_full (s_ip6, NM_SETTING_IP_CONFIG_ROUTES, + widget, "text", + G_BINDING_SYNC_CREATE, + ip6_routes_transform_to_description, + NULL, NULL, NULL); + button = nmt_newt_button_new (_("Edit...")); + g_signal_connect (button, "clicked", G_CALLBACK (edit_routes), s_ip6); + nmt_editor_grid_append (grid, _("Routing"), widget, button); + + widget = nmt_newt_checkbox_new (_("Never use this network for default route")); + g_object_bind_property (s_ip6, NM_SETTING_IP_CONFIG_NEVER_DEFAULT, + widget, "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_newt_checkbox_new (_("Require IPv6 addressing for this connection")); + g_object_bind_property (s_ip6, NM_SETTING_IP_CONFIG_MAY_FAIL, + widget, "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL | + G_BINDING_INVERT_BOOLEAN); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (ip6), section); + + G_OBJECT_CLASS (nmt_page_ip6_parent_class)->constructed (object); +} + +static void +nmt_page_ip6_class_init (NmtPageIP6Class *ip6_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ip6_class); + + object_class->constructed = nmt_page_ip6_constructed; +} diff --git a/clients/tui/nmt-page-ip6.h b/clients/tui/nmt-page-ip6.h new file mode 100644 index 00000000..498261e1 --- /dev/null +++ b/clients/tui/nmt-page-ip6.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_IP6_H +#define NMT_PAGE_IP6_H + +#include "nmt-editor-page.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_IP6 (nmt_page_ip6_get_type ()) +#define NMT_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_IP6, NmtPageIP6)) +#define NMT_PAGE_IP6_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_IP6, NmtPageIP6Class)) +#define NMT_IS_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_IP6)) +#define NMT_IS_PAGE_IP6_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_IP6)) +#define NMT_PAGE_IP6_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_IP6, NmtPageIP6Class)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageIP6; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageIP6Class; + +GType nmt_page_ip6_get_type (void); + +NmtEditorPage *nmt_page_ip6_new (NMConnection *conn); + +G_END_DECLS + +#endif /* NMT_PAGE_IP6_H */ diff --git a/clients/tui/nmt-page-ppp.c b/clients/tui/nmt-page-ppp.c new file mode 100644 index 00000000..cec36549 --- /dev/null +++ b/clients/tui/nmt-page-ppp.c @@ -0,0 +1,288 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2014 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ppp + * @short_description: The editor page for PPP configuration + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-ppp.h" +#include "nmt-newt-section.h" +#include "nmt-newt-separator.h" + +G_DEFINE_TYPE (NmtPagePpp, nmt_page_ppp, NMT_TYPE_EDITOR_PAGE) + +typedef struct { + guint32 lcp_echo_failure; + guint32 lcp_echo_interval; +} NmtPagePppPrivate; + +#define NMT_PAGE_PPP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_PPP, NmtPagePppPrivate)) + +NmtEditorPage * +nmt_page_ppp_new (NMConnection *conn) +{ + return g_object_new (NMT_TYPE_PAGE_PPP, + "connection", conn, + NULL); +} + +static void +nmt_page_ppp_init (NmtPagePpp *ppp) +{ +} + +static gboolean +transform_lcp_echo_properties_to_checkbox (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data) +{ + NMSettingPpp *s_ppp = NM_SETTING_PPP (g_binding_get_source (binding)); + + if ( nm_setting_ppp_get_lcp_echo_interval (s_ppp) != 0 + && nm_setting_ppp_get_lcp_echo_failure (s_ppp) != 0) + g_value_set_boolean (to_value, TRUE); + else + g_value_set_boolean (to_value, FALSE); + + return TRUE; +} + +static gboolean +transform_checkbox_to_lcp_echo_interval (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data) +{ + NmtPagePpp *ppp = user_data; + NmtPagePppPrivate *priv = NMT_PAGE_PPP_GET_PRIVATE (ppp); + + if (g_value_get_boolean (from_value)) + g_value_set_uint (to_value, priv->lcp_echo_interval); + else + g_value_set_uint (to_value, 0); + + return TRUE; +} + +static gboolean +transform_checkbox_to_lcp_echo_failure (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data) +{ + NmtPagePpp *ppp = user_data; + NmtPagePppPrivate *priv = NMT_PAGE_PPP_GET_PRIVATE (ppp); + + if (g_value_get_boolean (from_value)) + g_value_set_uint (to_value, priv->lcp_echo_failure); + else + g_value_set_uint (to_value, 0); + + return TRUE; +} + +static void +nmt_page_ppp_constructed (GObject *object) +{ + NmtPagePpp *ppp = NMT_PAGE_PPP (object); + NmtPagePppPrivate *priv = NMT_PAGE_PPP_GET_PRIVATE (ppp); + NmtEditorSection *section; + NmtEditorGrid *grid; + NMSettingPpp *s_ppp; + NmtNewtWidget *widget, *use_mppe; + NmtNewtGrid *auth_grid, *mppe_grid; + NmtNewtSection *auth_section, *mppe_section; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ppp)); + s_ppp = nm_connection_get_setting_ppp (conn); + if (s_ppp) { + priv->lcp_echo_interval = nm_setting_ppp_get_lcp_echo_interval (s_ppp); + priv->lcp_echo_failure = nm_setting_ppp_get_lcp_echo_failure (s_ppp); + } else { + s_ppp = (NMSettingPpp *) nm_setting_ppp_new (); + nm_connection_add_setting (conn, (NMSetting *) s_ppp); + + priv->lcp_echo_interval = 30; + priv->lcp_echo_failure = 5; + } + + section = nmt_editor_section_new (_("PPP CONFIGURATION"), NULL, TRUE); + grid = nmt_editor_section_get_body (section); + + /* Auth methods */ + widget = nmt_newt_section_new (FALSE); + auth_section = NMT_NEWT_SECTION (widget); + g_object_set (auth_section, "open", TRUE, NULL); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + widget = nmt_newt_label_new (_("Allowed authentication methods:")); + nmt_newt_section_set_header (auth_section, widget); + + widget = nmt_newt_grid_new (); + auth_grid = NMT_NEWT_GRID (widget); + nmt_newt_section_set_body (auth_section, widget); + + widget = nmt_newt_checkbox_new (_("EAP")); + g_object_bind_property (s_ppp, NM_SETTING_PPP_REFUSE_EAP, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_INVERT_BOOLEAN | + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (auth_grid, widget, 0, 0); + + widget = nmt_newt_checkbox_new (_("PAP")); + g_object_bind_property (s_ppp, NM_SETTING_PPP_REFUSE_PAP, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_INVERT_BOOLEAN | + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (auth_grid, widget, 0, 1); + + widget = nmt_newt_checkbox_new (_("CHAP")); + g_object_bind_property (s_ppp, NM_SETTING_PPP_REFUSE_CHAP, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_INVERT_BOOLEAN | + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (auth_grid, widget, 0, 2); + + widget = nmt_newt_checkbox_new (_("MSCHAPv2")); + g_object_bind_property (s_ppp, NM_SETTING_PPP_REFUSE_MSCHAPV2, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_INVERT_BOOLEAN | + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (auth_grid, widget, 0, 3); + + widget = nmt_newt_checkbox_new (_("MSCHAP")); + g_object_bind_property (s_ppp, NM_SETTING_PPP_REFUSE_MSCHAP, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_INVERT_BOOLEAN | + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (auth_grid, widget, 0, 4); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + /* MPPE */ + widget = nmt_newt_section_new (FALSE); + mppe_section = NMT_NEWT_SECTION (widget); + g_object_set (mppe_section, "open", TRUE, NULL); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new (_("Use point-to-point encryption (MPPE)")); + g_object_bind_property (s_ppp, NM_SETTING_PPP_REQUIRE_MPPE, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE); + use_mppe = widget; + nmt_newt_section_set_header (mppe_section, widget); + + widget = nmt_newt_grid_new (); + mppe_grid = NMT_NEWT_GRID (widget); + nmt_newt_section_set_body (mppe_section, widget); + + widget = nmt_newt_checkbox_new (_("Require 128-bit encryption")); + g_object_bind_property (use_mppe, "active", + widget, "sensitive", + G_BINDING_SYNC_CREATE); + g_object_bind_property (s_ppp, NM_SETTING_PPP_REQUIRE_MPPE_128, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (mppe_grid, widget, 0, 0); + + widget = nmt_newt_checkbox_new (_("Use stateful MPPE")); + g_object_bind_property (use_mppe, "active", + widget, "sensitive", + G_BINDING_SYNC_CREATE); + g_object_bind_property (s_ppp, NM_SETTING_PPP_MPPE_STATEFUL, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (mppe_grid, widget, 0, 1); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_newt_checkbox_new (_("Allow BSD data compression")); + g_object_bind_property (s_ppp, NM_SETTING_PPP_NOBSDCOMP, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_INVERT_BOOLEAN | + G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new (_("Allow Deflate data compression")); + g_object_bind_property (s_ppp, NM_SETTING_PPP_NODEFLATE, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_INVERT_BOOLEAN | + G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new (_("Use TCP header compression")); + g_object_bind_property (s_ppp, NM_SETTING_PPP_NO_VJ_COMP, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_INVERT_BOOLEAN | + G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_newt_checkbox_new (_("Send PPP echo packets")); + g_object_bind_property_full (s_ppp, NM_SETTING_PPP_LCP_ECHO_INTERVAL, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE, + transform_lcp_echo_properties_to_checkbox, + transform_checkbox_to_lcp_echo_interval, + ppp, NULL); + g_object_bind_property_full (s_ppp, NM_SETTING_PPP_LCP_ECHO_FAILURE, + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE, + transform_lcp_echo_properties_to_checkbox, + transform_checkbox_to_lcp_echo_failure, + ppp, NULL); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (ppp), section); + + G_OBJECT_CLASS (nmt_page_ppp_parent_class)->constructed (object); +} + +static void +nmt_page_ppp_class_init (NmtPagePppClass *ppp_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ppp_class); + + g_type_class_add_private (object_class, sizeof (NmtPagePppPrivate)); + + object_class->constructed = nmt_page_ppp_constructed; +} diff --git a/clients/tui/nmt-page-ppp.h b/clients/tui/nmt-page-ppp.h new file mode 100644 index 00000000..79103232 --- /dev/null +++ b/clients/tui/nmt-page-ppp.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2014 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_PPP_H +#define NMT_PAGE_PPP_H + +#include "nmt-editor-page.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_PPP (nmt_page_ppp_get_type ()) +#define NMT_PAGE_PPP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_PPP, NmtPagePpp)) +#define NMT_PAGE_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_PPP, NmtPagePppClass)) +#define NMT_IS_PAGE_PPP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_PPP)) +#define NMT_IS_PAGE_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_PPP)) +#define NMT_PAGE_PPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_PPP, NmtPagePppClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtPagePpp; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPagePppClass; + +GType nmt_page_ppp_get_type (void); + +NmtEditorPage *nmt_page_ppp_new (NMConnection *conn); + +G_END_DECLS + +#endif /* NMT_PAGE_PPP_H */ diff --git a/clients/tui/nmt-page-team-port.c b/clients/tui/nmt-page-team-port.c new file mode 100644 index 00000000..645dc9d3 --- /dev/null +++ b/clients/tui/nmt-page-team-port.c @@ -0,0 +1,129 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-team-port + * @short_description: The editor page for Team ports. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-team-port.h" + +G_DEFINE_TYPE (NmtPageTeamPort, nmt_page_team_port, NMT_TYPE_EDITOR_PAGE) + +#define NMT_PAGE_TEAM_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortPrivate)) + +typedef struct { + NMSettingTeamPort *s_port; + +} NmtPageTeamPortPrivate; + +NmtEditorPage * +nmt_page_team_port_new (NMConnection *conn) +{ + return g_object_new (NMT_TYPE_PAGE_TEAM_PORT, + "connection", conn, + NULL); +} + +static void +nmt_page_team_port_init (NmtPageTeamPort *team) +{ +} + +static void +edit_clicked (NmtNewtButton *button, + gpointer user_data) +{ + NmtPageTeamPort *team = user_data; + NmtPageTeamPortPrivate *priv = NMT_PAGE_TEAM_PORT_GET_PRIVATE (team); + const char *config; + char *new_config; + + config = nm_setting_team_port_get_config (priv->s_port); + if (!config) + config = ""; + + new_config = nmt_newt_edit_string (config); + + if (new_config && !*new_config) + g_clear_pointer (&new_config, g_free); + g_object_set (G_OBJECT (priv->s_port), + NM_SETTING_TEAM_PORT_CONFIG, new_config, + NULL); + g_free (new_config); +} + +static void +nmt_page_team_port_constructed (GObject *object) +{ + NmtPageTeamPort *team = NMT_PAGE_TEAM_PORT (object); + NmtPageTeamPortPrivate *priv = NMT_PAGE_TEAM_PORT_GET_PRIVATE (team); + NmtEditorSection *section; + NmtNewtGrid *grid; + NMSettingTeamPort *s_port; + NmtNewtWidget *widget; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (team)); + s_port = nm_connection_get_setting_team_port (conn); + if (!s_port) { + nm_connection_add_setting (conn, nm_setting_team_port_new ()); + s_port = nm_connection_get_setting_team_port (conn); + } + priv->s_port = s_port; + + section = nmt_editor_section_new (_("TEAM PORT"), NULL, TRUE); + + widget = nmt_newt_grid_new (); + nmt_editor_grid_append (nmt_editor_section_get_body (section), NULL, widget, NULL); + + grid = NMT_NEWT_GRID (widget); + + widget = nmt_newt_label_new (_("JSON configuration")); + nmt_newt_grid_add (grid, widget, 0, 2); + + widget = nmt_newt_textbox_new (NMT_NEWT_TEXTBOX_SCROLLABLE | NMT_NEWT_TEXTBOX_SET_BACKGROUND, 60); + g_object_bind_property (s_port, NM_SETTING_TEAM_PORT_CONFIG, + widget, "text", + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (grid, widget, 0, 3); + nmt_newt_widget_set_padding (widget, 2, 0, 2, 1); + + widget = nmt_newt_button_new (_("Edit...")); + g_signal_connect (widget, "clicked", G_CALLBACK (edit_clicked), team); + nmt_newt_grid_add (grid, widget, 0, 4); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (team), section); + + G_OBJECT_CLASS (nmt_page_team_port_parent_class)->constructed (object); +} + +static void +nmt_page_team_port_class_init (NmtPageTeamPortClass *team_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (team_class); + + g_type_class_add_private (team_class, sizeof (NmtPageTeamPortPrivate)); + + object_class->constructed = nmt_page_team_port_constructed; +} diff --git a/clients/tui/nmt-page-team-port.h b/clients/tui/nmt-page-team-port.h new file mode 100644 index 00000000..2d8fa121 --- /dev/null +++ b/clients/tui/nmt-page-team-port.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_TEAM_PORT_H +#define NMT_PAGE_TEAM_PORT_H + +#include "nmt-editor-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_TEAM_PORT (nmt_page_team_port_get_type ()) +#define NMT_PAGE_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPort)) +#define NMT_PAGE_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortClass)) +#define NMT_IS_PAGE_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_TEAM_PORT)) +#define NMT_IS_PAGE_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_TEAM_PORT)) +#define NMT_PAGE_TEAM_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageTeamPort; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageTeamPortClass; + +GType nmt_page_team_port_get_type (void); + +NmtEditorPage *nmt_page_team_port_new (NMConnection *conn); + +G_END_DECLS + +#endif /* NMT_PAGE_TEAM_PORT_H */ diff --git a/clients/tui/nmt-page-team.c b/clients/tui/nmt-page-team.c new file mode 100644 index 00000000..e520b02a --- /dev/null +++ b/clients/tui/nmt-page-team.c @@ -0,0 +1,193 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-team + * @short_description: The editor page for Team connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-team.h" + +#include "nmt-slave-list.h" + +G_DEFINE_TYPE (NmtPageTeam, nmt_page_team, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_TEAM, NmtPageTeamPrivate)) + +typedef struct { + NmtSlaveList *slaves; + + NMSettingTeam *s_team; + GType slave_type; + +} NmtPageTeamPrivate; + +NmtEditorPage * +nmt_page_team_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_TEAM, + "connection", conn, + "device-entry", deventry, + NULL); +} + +static void +nmt_page_team_init (NmtPageTeam *team) +{ + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team); + + priv->slave_type = G_TYPE_NONE; +} + +static void +slaves_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtPageTeam *team = NMT_PAGE_TEAM (user_data); + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team); + GPtrArray *slaves; + + g_object_get (object, "connections", &slaves, NULL); + if (slaves->len == 0) { + priv->slave_type = G_TYPE_NONE; + } else if (priv->slave_type == G_TYPE_NONE) { + NMConnection *slave = slaves->pdata[0]; + + if (nm_connection_is_type (slave, NM_SETTING_INFINIBAND_SETTING_NAME)) + priv->slave_type = NM_TYPE_SETTING_INFINIBAND; + else + priv->slave_type = NM_TYPE_SETTING_WIRED; + } +} + +static gboolean +team_connection_type_filter (GType connection_type, + gpointer user_data) +{ + NmtPageTeam *team = user_data; + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team); + + if (priv->slave_type != NM_TYPE_SETTING_WIRED) { + if (connection_type == NM_TYPE_SETTING_INFINIBAND) + return TRUE; + } + if (priv->slave_type != NM_TYPE_SETTING_INFINIBAND) { + if ( connection_type == NM_TYPE_SETTING_WIRED + || connection_type == NM_TYPE_SETTING_WIRELESS + || connection_type == NM_TYPE_SETTING_VLAN) + return TRUE; + } + + return FALSE; +} + +static void +edit_clicked (NmtNewtButton *button, + gpointer user_data) +{ + NmtPageTeam *team = user_data; + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team); + const char *config; + char *new_config; + + config = nm_setting_team_get_config (priv->s_team); + if (!config) + config = ""; + + new_config = nmt_newt_edit_string (config); + + if (new_config && !*new_config) + g_clear_pointer (&new_config, g_free); + g_object_set (G_OBJECT (priv->s_team), + NM_SETTING_TEAM_CONFIG, new_config, + NULL); + g_free (new_config); +} + +static void +nmt_page_team_constructed (GObject *object) +{ + NmtPageTeam *team = NMT_PAGE_TEAM (object); + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team); + NmtEditorSection *section; + NmtNewtGrid *grid; + NMSettingTeam *s_team; + NmtNewtWidget *widget; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (team)); + s_team = nm_connection_get_setting_team (conn); + if (!s_team) { + nm_connection_add_setting (conn, nm_setting_team_new ()); + s_team = nm_connection_get_setting_team (conn); + } + priv->s_team = s_team; + + section = nmt_editor_section_new (_("TEAM"), NULL, TRUE); + + widget = nmt_newt_grid_new (); + nmt_editor_grid_append (nmt_editor_section_get_body (section), NULL, widget, NULL); + + grid = NMT_NEWT_GRID (widget); + + widget = nmt_newt_label_new (_("Slaves")); + nmt_newt_grid_add (grid, widget, 0, 0); + + widget = nmt_slave_list_new (conn, team_connection_type_filter, team); + g_signal_connect (widget, "notify::connections", + G_CALLBACK (slaves_changed), team); + nmt_newt_grid_add (grid, widget, 0, 1); + nmt_newt_widget_set_padding (widget, 0, 0, 0, 1); + priv->slaves = NMT_SLAVE_LIST (widget); + slaves_changed (G_OBJECT (priv->slaves), NULL, team); + + widget = nmt_newt_label_new (_("JSON configuration")); + nmt_newt_grid_add (grid, widget, 0, 2); + + widget = nmt_newt_textbox_new (NMT_NEWT_TEXTBOX_SCROLLABLE | NMT_NEWT_TEXTBOX_SET_BACKGROUND, 60); + g_object_bind_property (s_team, NM_SETTING_TEAM_CONFIG, + widget, "text", + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (grid, widget, 0, 3); + nmt_newt_widget_set_padding (widget, 2, 0, 2, 1); + + widget = nmt_newt_button_new (_("Edit...")); + g_signal_connect (widget, "clicked", G_CALLBACK (edit_clicked), team); + nmt_newt_grid_add (grid, widget, 0, 4); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (team), section); + + G_OBJECT_CLASS (nmt_page_team_parent_class)->constructed (object); +} + +static void +nmt_page_team_class_init (NmtPageTeamClass *team_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (team_class); + + g_type_class_add_private (team_class, sizeof (NmtPageTeamPrivate)); + + object_class->constructed = nmt_page_team_constructed; +} diff --git a/clients/tui/nmt-page-team.h b/clients/tui/nmt-page-team.h new file mode 100644 index 00000000..2d8bf07a --- /dev/null +++ b/clients/tui/nmt-page-team.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_TEAM_H +#define NMT_PAGE_TEAM_H + +#include "nmt-editor-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_TEAM (nmt_page_team_get_type ()) +#define NMT_PAGE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_TEAM, NmtPageTeam)) +#define NMT_PAGE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_TEAM, NmtPageTeamClass)) +#define NMT_IS_PAGE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_TEAM)) +#define NMT_IS_PAGE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_TEAM)) +#define NMT_PAGE_TEAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_TEAM, NmtPageTeamClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageTeam; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageTeamClass; + +GType nmt_page_team_get_type (void); + +NmtEditorPage *nmt_page_team_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_TEAM_H */ diff --git a/clients/tui/nmt-page-vlan.c b/clients/tui/nmt-page-vlan.c new file mode 100644 index 00000000..9b9c8f52 --- /dev/null +++ b/clients/tui/nmt-page-vlan.c @@ -0,0 +1,158 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-vlan + * @short_description: The editor page for VLAN connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nm-editor-bindings.h" + +#include "nmt-page-vlan.h" +#include "nmt-device-entry.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE (NmtPageVlan, nmt_page_vlan, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_VLAN, NmtPageVlanPrivate)) + +typedef struct { + NMSettingWired *s_wired; + +} NmtPageVlanPrivate; + +NmtEditorPage * +nmt_page_vlan_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_VLAN, + "connection", conn, + "device-entry", deventry, + NULL); +} + +static void +nmt_page_vlan_init (NmtPageVlan *vlan) +{ +} + +static gboolean +vlan_device_filter (NmtDeviceEntry *deventry, + NMDevice *device, + gpointer user_data) +{ + // FIXME + return NM_IS_DEVICE_ETHERNET (device); +} + +static void +nmt_page_vlan_constructed (GObject *object) +{ + NmtPageVlan *vlan = NMT_PAGE_VLAN (object); + NmtPageVlanPrivate *priv = NMT_PAGE_VLAN_GET_PRIVATE (vlan); + NmtEditorSection *section; + NmtEditorGrid *grid; + NMSettingWired *s_wired; + NMSettingVlan *s_vlan; + NmtNewtWidget *widget, *parent, *id_entry; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (vlan)); + s_vlan = nm_connection_get_setting_vlan (conn); + if (!s_vlan) { + nm_connection_add_setting (conn, nm_setting_vlan_new ()); + s_vlan = nm_connection_get_setting_vlan (conn); + } + s_wired = nm_connection_get_setting_wired (conn); + if (!s_wired) { + /* It makes things simpler if we always have a NMSettingWired; + * we'll hold a ref on one, and add it to and remove it from + * the connection as needed. + */ + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + } + priv->s_wired = g_object_ref_sink (s_wired); + + section = nmt_editor_section_new (_("VLAN"), NULL, TRUE); + grid = nmt_editor_section_get_body (section); + + nm_editor_bind_vlan_name (s_vlan, nm_connection_get_setting_connection (conn)); + + widget = parent = nmt_device_entry_new (_("Parent"), 40, G_TYPE_NONE); + nmt_device_entry_set_device_filter (NMT_DEVICE_ENTRY (widget), + vlan_device_filter, vlan); + g_object_bind_property (s_vlan, NM_SETTING_VLAN_PARENT, + widget, "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, + widget, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, NULL, widget, NULL); + + widget = id_entry = nmt_newt_entry_numeric_new (8, 0, 4095); + g_object_bind_property (s_vlan, NM_SETTING_VLAN_ID, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("VLAN id"), widget, NULL); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_mac_entry_new (40, ETH_ALEN); + g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + widget, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Cloned MAC address"), widget, NULL); + + widget = nmt_mtu_entry_new (); + g_object_bind_property (s_wired, NM_SETTING_WIRED_MTU, + widget, "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("MTU"), widget, NULL); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (vlan), section); + + G_OBJECT_CLASS (nmt_page_vlan_parent_class)->constructed (object); +} + +static void +nmt_page_vlan_finalize (GObject *object) +{ + NmtPageVlanPrivate *priv = NMT_PAGE_VLAN_GET_PRIVATE (object); + + g_clear_object (&priv->s_wired); + + G_OBJECT_CLASS (nmt_page_vlan_parent_class)->finalize (object); +} + +static void +nmt_page_vlan_class_init (NmtPageVlanClass *vlan_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (vlan_class); + + g_type_class_add_private (vlan_class, sizeof (NmtPageVlanPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_page_vlan_constructed; + object_class->finalize = nmt_page_vlan_finalize; +} diff --git a/clients/tui/nmt-page-vlan.h b/clients/tui/nmt-page-vlan.h new file mode 100644 index 00000000..db7c4098 --- /dev/null +++ b/clients/tui/nmt-page-vlan.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_VLAN_H +#define NMT_PAGE_VLAN_H + +#include "nmt-editor-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_VLAN (nmt_page_vlan_get_type ()) +#define NMT_PAGE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_VLAN, NmtPageVlan)) +#define NMT_PAGE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_VLAN, NmtPageVlanClass)) +#define NMT_IS_PAGE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_VLAN)) +#define NMT_IS_PAGE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_VLAN)) +#define NMT_PAGE_VLAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_VLAN, NmtPageVlanClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageVlan; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageVlanClass; + +GType nmt_page_vlan_get_type (void); + +NmtEditorPage *nmt_page_vlan_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_VLAN_H */ diff --git a/clients/tui/nmt-page-wifi.c b/clients/tui/nmt-page-wifi.c new file mode 100644 index 00000000..9cd20abf --- /dev/null +++ b/clients/tui/nmt-page-wifi.c @@ -0,0 +1,400 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-wifi + * @short_description: The editor page for Wi-Fi connections + * + * #NmtPageWifi is the editor page for Wi-Fi connections, which + * includes both #NMSettingWireless and #NMSettingWirelessSecurity + * properties. + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-wifi.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" +#include "nmt-password-fields.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE (NmtPageWifi, nmt_page_wifi, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_WIFI, NmtPageWifiPrivate)) + +typedef struct { + NMSettingWirelessSecurity *s_wsec; + +} NmtPageWifiPrivate; + +NmtEditorPage * +nmt_page_wifi_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_WIFI, + "connection", conn, + "device-entry", deventry, + NULL); +} + +static void +nmt_page_wifi_init (NmtPageWifi *wifi) +{ +} + +static NmtNewtPopupEntry wifi_mode[] = { + { NC_("Wi-Fi", "Client"), NM_SETTING_WIRELESS_MODE_INFRA }, + { N_("Access Point"), NM_SETTING_WIRELESS_MODE_AP }, + { N_("Ad-Hoc Network"), NM_SETTING_WIRELESS_MODE_ADHOC }, + { NULL, NULL } +}; + +static NmtNewtPopupEntry wifi_band[] = { + { NC_("Wi-Fi", "Automatic"), NULL }, + /* 802.11a Wi-Fi network */ + { N_("A (5 GHz)"), "a" }, + /* 802.11b / 802.11g Wi-Fi network */ + { N_("B/G (2.4 GHz)"), "bg" }, + { NULL, NULL } +}; + +static NmtNewtPopupEntry wifi_security[] = { + { NC_("Wi-Fi security", "None"), "none" }, + { N_("WPA & WPA2 Personal"), "wpa-personal" }, + { N_("WPA & WPA2 Enterprise"), "wpa-enterprise" }, + { N_("WEP 40/128-bit Key (Hex or ASCII)"), "wep-key" }, + { N_("WEP 128-bit Passphrase"), "wep-passphrase" }, + { N_("Dynamic WEP (802.1x)"), "dynamic-wep" }, + { N_("LEAP"), "leap" }, + { NULL, NULL } +}; + +static NmtNewtPopupEntry wep_index[] = { + { NC_("WEP key index", "1 (Default)"), "1" }, + { NC_("WEP key index", "2"), "2" }, + { NC_("WEP key index", "3"), "3" }, + { NC_("WEP key index", "4"), "4" }, + { NULL, NULL } +}; + +static NmtNewtPopupEntry wep_auth[] = { + { N_("Open System"), "open" }, + { N_("Shared Key"), "shared" }, + { NULL, NULL } +}; + +static gboolean +mode_transform_to_band_visibility (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + if (!g_strcmp0 (g_value_get_string (source_value), NM_SETTING_WIRELESS_MODE_INFRA)) + g_value_set_boolean (target_value, FALSE); + else + g_value_set_boolean (target_value, TRUE); + return TRUE; +} + +static gboolean +band_transform_to_channel_visibility (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + g_value_set_boolean (target_value, g_value_get_string (source_value) != NULL); + return TRUE; +} + +static gboolean +ssid_transform_to_entry (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GBytes *ssid; + char *utf8; + + ssid = g_value_get_boxed (source_value); + if (ssid) + utf8 = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + else + utf8 = g_strdup (""); + g_value_take_string (target_value, utf8); + return TRUE; +} + +static gboolean +ssid_transform_from_entry (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMSettingWireless *s_wireless = user_data; + const char *text; + GBytes *old_ssid, *ssid; + char *utf8; + + text = g_value_get_string (source_value); + + old_ssid = nm_setting_wireless_get_ssid (s_wireless); + if (old_ssid) + utf8 = nm_utils_ssid_to_utf8 (g_bytes_get_data (old_ssid, NULL), + g_bytes_get_size (old_ssid)); + else + utf8 = g_strdup (""); + + if (!g_strcmp0 (text, utf8)) { + g_free (utf8); + return FALSE; + } + g_free (utf8); + + ssid = g_bytes_new (text, strlen (text)); + g_value_take_boxed (target_value, ssid); + return TRUE; +} + +static void +nmt_page_wifi_constructed (GObject *object) +{ + NmtPageWifiPrivate *priv = NMT_PAGE_WIFI_GET_PRIVATE (object); + NmtPageWifi *wifi = NMT_PAGE_WIFI (object); + NmtDeviceEntry *deventry; + NmtEditorSection *section; + NmtEditorGrid *grid; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wsec; + NmtNewtWidget *widget, *hbox, *subgrid; + NmtNewtWidget *mode, *band, *security, *entry; + NmtNewtStack *stack; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (wifi)); + s_wireless = nm_connection_get_setting_wireless (conn); + if (!s_wireless) { + nm_connection_add_setting (conn, nm_setting_wireless_new ()); + s_wireless = nm_connection_get_setting_wireless (conn); + } + + s_wsec = nm_connection_get_setting_wireless_security (conn); + if (!s_wsec) { + /* It makes things simpler if we always have a + * NMSettingWirelessSecurity; we'll hold a ref on one, and add + * it to and remove it from the connection as needed. + */ + s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); + } + priv->s_wsec = g_object_ref_sink (s_wsec); + + deventry = nmt_editor_page_device_get_device_entry (NMT_EDITOR_PAGE_DEVICE (object)); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS, + deventry, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + section = nmt_editor_section_new (_("WI-FI"), NULL, TRUE); + grid = nmt_editor_section_get_body (section); + + widget = nmt_newt_entry_new (40, NMT_NEWT_ENTRY_NONEMPTY); + g_object_bind_property_full (s_wireless, NM_SETTING_WIRELESS_SSID, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + ssid_transform_to_entry, + ssid_transform_from_entry, + s_wireless, NULL); + nmt_editor_grid_append (grid, _("SSID"), widget, NULL); + + widget = nmt_newt_popup_new (wifi_mode); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MODE, + widget, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Mode"), widget, NULL); + mode = widget; + + hbox = nmt_newt_grid_new (); + widget = nmt_newt_popup_new (wifi_band); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_BAND, + widget, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (NMT_NEWT_GRID (hbox), widget, 0, 0); + band = widget; + + widget = nmt_newt_entry_numeric_new (10, 0, 255); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_CHANNEL, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (NMT_NEWT_GRID (hbox), widget, 1, 0); + nmt_newt_widget_set_padding (widget, 1, 0, 0, 0); + + g_object_bind_property_full (band, "active-id", widget, "visible", + G_BINDING_SYNC_CREATE, + band_transform_to_channel_visibility, + NULL, NULL, NULL); + g_object_bind_property_full (mode, "active-id", hbox, "visible", + G_BINDING_SYNC_CREATE, + mode_transform_to_band_visibility, + NULL, NULL, NULL); + nmt_editor_grid_append (grid, _("Channel"), hbox, NULL); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_newt_popup_new (wifi_security); + nmt_editor_grid_append (grid, _("Security"), widget, NULL); + security = widget; + + widget = nmt_newt_stack_new (); + stack = NMT_NEWT_STACK (widget); + + /* none */ + subgrid = nmt_editor_grid_new (); + nmt_newt_stack_add (stack, "none", subgrid); + + /* wpa-personal */ + subgrid = nmt_editor_grid_new (); + widget = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + g_object_bind_property (s_wsec, NM_SETTING_WIRELESS_SECURITY_PSK, + widget, "password", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Password"), widget, NULL); + nmt_newt_stack_add (stack, "wpa-personal", subgrid); + + /* "wpa-enterprise" */ + // FIXME + widget = nmt_newt_label_new (_("(No support for wpa-enterprise yet...)")); + nmt_newt_stack_add (stack, "wpa-enterprise", widget); + + /* wep-key */ + subgrid = nmt_editor_grid_new (); + + widget = entry = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Key"), widget, NULL); + + widget = nmt_newt_popup_new (wep_index); + nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("WEP index"), widget, NULL); + + nm_editor_bind_wireless_security_wep_key (s_wsec, + entry, "password", + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + widget = nmt_newt_popup_new (wep_auth); + nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Authentication"), widget, NULL); + + nmt_newt_stack_add (stack, "wep-key", subgrid); + + /* wep-passphrase */ + subgrid = nmt_editor_grid_new (); + + widget = entry = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Password"), widget, NULL); + + widget = nmt_newt_popup_new (wep_index); + nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("WEP index"), widget, NULL); + + nm_editor_bind_wireless_security_wep_key (s_wsec, + entry, "password", + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + widget = nmt_newt_popup_new (wep_auth); + nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Authentication"), widget, NULL); + + nmt_newt_stack_add (stack, "wep-passphrase", subgrid); + + /* "dynamic-wep" */ + // FIXME + widget = nmt_newt_label_new (_("(No support for dynamic-wep yet...)")); + nmt_newt_stack_add (stack, "dynamic-wep", widget); + + /* leap */ + subgrid = nmt_editor_grid_new (); + + widget = nmt_newt_entry_new (40, NMT_NEWT_ENTRY_NONEMPTY); + nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Username"), widget, NULL); + g_object_bind_property (s_wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, + widget, "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + widget = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + g_object_bind_property (s_wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + widget, "password", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Password"), widget, NULL); + + nmt_newt_stack_add (stack, "leap", subgrid); + + nmt_editor_grid_append (grid, NULL, NMT_NEWT_WIDGET (stack), NULL); + g_object_bind_property (security, "active-id", + stack, "active-id", + G_BINDING_SYNC_CREATE); + nm_editor_bind_wireless_security_method (conn, s_wsec, security, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_mac_entry_new (40, ETH_ALEN); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_BSSID, + widget, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("BSSID"), widget, NULL); + + widget = nmt_mac_entry_new (40, ETH_ALEN); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, + widget, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("Cloned MAC address"), widget, NULL); + + widget = nmt_mtu_entry_new (); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MTU, + widget, "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append (grid, _("MTU"), widget, NULL); + + nmt_editor_page_add_section (NMT_EDITOR_PAGE (wifi), section); + + G_OBJECT_CLASS (nmt_page_wifi_parent_class)->constructed (object); +} + +static void +nmt_page_wifi_finalize (GObject *object) +{ + NmtPageWifiPrivate *priv = NMT_PAGE_WIFI_GET_PRIVATE (object); + + g_clear_object (&priv->s_wsec); + + G_OBJECT_CLASS (nmt_page_wifi_parent_class)->finalize (object); +} + + +static void +nmt_page_wifi_class_init (NmtPageWifiClass *wifi_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (wifi_class); + + g_type_class_add_private (wifi_class, sizeof (NmtPageWifiPrivate)); + + object_class->constructed = nmt_page_wifi_constructed; + object_class->finalize = nmt_page_wifi_finalize; +} diff --git a/clients/tui/nmt-page-wifi.h b/clients/tui/nmt-page-wifi.h new file mode 100644 index 00000000..7ce50584 --- /dev/null +++ b/clients/tui/nmt-page-wifi.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_WIFI_H +#define NMT_PAGE_WIFI_H + +#include "nmt-editor-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_WIFI (nmt_page_wifi_get_type ()) +#define NMT_PAGE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_WIFI, NmtPageWifi)) +#define NMT_PAGE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_WIFI, NmtPageWifiClass)) +#define NMT_IS_PAGE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_WIFI)) +#define NMT_IS_PAGE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_WIFI)) +#define NMT_PAGE_WIFI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_WIFI, NmtPageWifiClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageWifi; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageWifiClass; + +GType nmt_page_wifi_get_type (void); + +NmtEditorPage *nmt_page_wifi_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_WIFI_H */ diff --git a/clients/tui/nmt-password-dialog.c b/clients/tui/nmt-password-dialog.c new file mode 100644 index 00000000..aeece121 --- /dev/null +++ b/clients/tui/nmt-password-dialog.c @@ -0,0 +1,345 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-password-dialog + * @short_description: A password dialog + * + * #NmtPasswordDialog is the password dialog used to get connection + * secrets when activating a connection. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-password-dialog.h" +#include "nm-secret-agent-simple.h" +#include "nmtui.h" + +G_DEFINE_TYPE (NmtPasswordDialog, nmt_password_dialog, NMT_TYPE_NEWT_FORM) + +#define NMT_PASSWORD_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogPrivate)) + +typedef struct { + char *request_id; + char *prompt; + GPtrArray *secrets; + GPtrArray *entries; + + NmtNewtWidget *ok, *cancel; + NmtNewtWidget *last_entry; + NmtNewtWidget *secret_grid; + + gboolean succeeded; +} NmtPasswordDialogPrivate; + +enum { + PROP_0, + PROP_REQUEST_ID, + PROP_PROMPT, + PROP_SECRETS, + + LAST_PROP +}; + +/** + * nmt_password_dialog_new: + * @request_id: the request ID from the #NMSecretAgentSimple + * @title: the dialog title + * @prompt: the prompt text to display + * @secrets: (element-type #NMSecretAgentSimpleSecret): the secrets requested + * + * Creates a new #NmtPasswordDialog to request passwords from + * the user. + * + * Returns: a new #NmtPasswordDialog. + */ +NmtNewtForm * +nmt_password_dialog_new (const char *request_id, + const char *title, + const char *prompt, + GPtrArray *secrets) +{ + return g_object_new (NMT_TYPE_PASSWORD_DIALOG, + "request-id", request_id, + "title", title, + "prompt", prompt, + "secrets", secrets, + "escape-exits", TRUE, + NULL); +} + +static void +nmt_password_dialog_init (NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + + priv->entries = g_ptr_array_new (); +} + +static void +maybe_save_input_and_exit (NmtNewtWidget *widget, + gpointer dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + int i; + + /* This gets invoked when the user types Return in the final entry, + * but the form may not be fully valid in that case. + */ + if (!nmt_newt_widget_get_valid (priv->secret_grid)) + return; + + priv->succeeded = TRUE; + + for (i = 0; i < priv->secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = priv->secrets->pdata[i]; + + g_free (secret->value); + g_object_get (priv->entries->pdata[i], "text", &secret->value, NULL); + } + + nmt_newt_form_quit (nmt_newt_widget_get_form (widget)); +} + +static void +nmt_password_dialog_constructed (GObject *object) +{ + NmtPasswordDialog *dialog = NMT_PASSWORD_DIALOG (object); + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + NmtNewtWidget *widget; + NmtNewtGrid *grid, *secret_grid; + NmtNewtButtonBox *bbox; + int i; + + widget = nmt_newt_grid_new (); + nmt_newt_form_set_content (NMT_NEWT_FORM (dialog), widget); + grid = NMT_NEWT_GRID (widget); + + widget = nmt_newt_textbox_new (0, 60); + nmt_newt_textbox_set_text (NMT_NEWT_TEXTBOX (widget), priv->prompt); + nmt_newt_grid_add (grid, widget, 0, 0); + + widget = nmt_newt_grid_new (); + nmt_newt_grid_add (grid, widget, 0, 1); + nmt_newt_widget_set_padding (widget, 0, 1, 0, 1); + priv->secret_grid = widget; + secret_grid = NMT_NEWT_GRID (widget); + + for (i = 0; i < priv->secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = priv->secrets->pdata[i]; + NmtNewtEntryFlags flags; + + widget = nmt_newt_label_new (secret->name); + nmt_newt_grid_add (secret_grid, widget, 0, i); + nmt_newt_widget_set_padding (widget, 4, 0, 1, 0); + + flags = NMT_NEWT_ENTRY_NONEMPTY; + if (secret->password) + flags |= NMT_NEWT_ENTRY_PASSWORD; + widget = nmt_newt_entry_new (30, flags); + nmt_newt_grid_add (secret_grid, widget, 1, i); + g_ptr_array_add (priv->entries, widget); + + if (i == priv->secrets->len - 1) { + priv->last_entry = widget; + g_signal_connect (widget, "activated", + G_CALLBACK (maybe_save_input_and_exit), dialog); + } + } + + widget = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add (grid, widget, 0, 2); + bbox = NMT_NEWT_BUTTON_BOX (widget); + + priv->cancel = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("Cancel")); + nmt_newt_widget_set_exit_on_activate (priv->cancel, TRUE); + + priv->ok = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("OK")); + g_signal_connect (priv->ok, "activated", + G_CALLBACK (maybe_save_input_and_exit), dialog); + g_object_bind_property (priv->secret_grid, "valid", + priv->ok, "sensitive", + G_BINDING_SYNC_CREATE); + + G_OBJECT_CLASS (nmt_password_dialog_parent_class)->constructed (object); +} + +static void +nmt_password_dialog_finalize (GObject *object) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (object); + + g_free (priv->request_id); + g_free (priv->prompt); + g_clear_pointer (&priv->entries, g_ptr_array_unref); + g_clear_pointer (&priv->secrets, g_ptr_array_unref); + + G_OBJECT_CLASS (nmt_password_dialog_parent_class)->finalize (object); +} + +static void +nmt_password_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_REQUEST_ID: + priv->request_id = g_value_dup_string (value); + break; + case PROP_PROMPT: + priv->prompt = g_value_dup_string (value); + break; + case PROP_SECRETS: + priv->secrets = g_value_dup_boxed (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_password_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_REQUEST_ID: + g_value_set_string (value, priv->request_id); + break; + case PROP_PROMPT: + g_value_set_string (value, priv->prompt); + break; + case PROP_SECRETS: + g_value_set_boxed (value, priv->secrets); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_password_dialog_class_init (NmtPasswordDialogClass *dialog_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (dialog_class); + + g_type_class_add_private (dialog_class, sizeof (NmtPasswordDialogPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_password_dialog_constructed; + object_class->set_property = nmt_password_dialog_set_property; + object_class->get_property = nmt_password_dialog_get_property; + object_class->finalize = nmt_password_dialog_finalize; + + /** + * NmtPasswordDialog:request-id: + * + * The request ID from the #NMSecretAgentSimple + */ + g_object_class_install_property + (object_class, PROP_REQUEST_ID, + g_param_spec_string ("request-id", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordDialog:prompt: + * + * The prompt text. + */ + g_object_class_install_property + (object_class, PROP_PROMPT, + g_param_spec_string ("prompt", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordDialog:secrets: + * + * The array of request secrets + * + * Element-Type: #NMSecretAgentSimpleSecret. + */ + g_object_class_install_property + (object_class, PROP_SECRETS, + g_param_spec_boxed ("secrets", "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +/** + * nmt_password_dialog_succeeded: + * @dialog: the #NmtPasswordDialog + * + * After the dialog has exited, returns %TRUE if the user clicked + * "OK", %FALSE if "Cancel". + * + * Returns: whether the dialog succeeded. + */ +gboolean +nmt_password_dialog_succeeded (NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + + return priv->succeeded; +} + +/** + * nmt_password_dialog_get_request_id: + * @dialog: the #NmtPasswordDialog + * + * Gets the dialog's request ID. + * + * Returns: the dialog's request ID. + */ +const char * +nmt_password_dialog_get_request_id (NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + + return priv->request_id; +} + +/** + * nmt_password_dialog_get_secrets: + * @dialog: the #NmtPasswordDialog + * + * Gets the dialog's secrets array. + * + * Returns: (transfer none): the dialog's secrets array. + */ +GPtrArray * +nmt_password_dialog_get_secrets (NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + + return priv->secrets; +} diff --git a/clients/tui/nmt-password-dialog.h b/clients/tui/nmt-password-dialog.h new file mode 100644 index 00000000..36c9f51e --- /dev/null +++ b/clients/tui/nmt-password-dialog.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PASSWORD_DIALOG_H +#define NMT_PASSWORD_DIALOG_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PASSWORD_DIALOG (nmt_password_dialog_get_type ()) +#define NMT_PASSWORD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialog)) +#define NMT_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass)) +#define NMT_IS_PASSWORD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PASSWORD_DIALOG)) +#define NMT_IS_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PASSWORD_DIALOG)) +#define NMT_PASSWORD_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass)) + +typedef struct { + NmtNewtForm parent; + +} NmtPasswordDialog; + +typedef struct { + NmtNewtFormClass parent; + +} NmtPasswordDialogClass; + +GType nmt_password_dialog_get_type (void); + +NmtNewtForm *nmt_password_dialog_new (const char *request_id, + const char *title, + const char *prompt, + GPtrArray *secrets); + +gboolean nmt_password_dialog_succeeded (NmtPasswordDialog *dialog); + +const char *nmt_password_dialog_get_request_id (NmtPasswordDialog *dialog); +GPtrArray *nmt_password_dialog_get_secrets (NmtPasswordDialog *dialog); + +G_END_DECLS + +#endif /* NMT_PASSWORD_DIALOG_H */ diff --git a/clients/tui/nmt-password-fields.c b/clients/tui/nmt-password-fields.c new file mode 100644 index 00000000..7ef037b9 --- /dev/null +++ b/clients/tui/nmt-password-fields.c @@ -0,0 +1,319 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-password-fields + * @short_description: Widgets for password-related data + * + * #NmtPasswordFields provides an entry to type a password into, followed + * optionally by an "Ask for this password every time" checkbox and/or a + * "Show password" checkbox that toggles whether the password is visible. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-password-fields.h" + +G_DEFINE_TYPE (NmtPasswordFields, nmt_password_fields, NMT_TYPE_NEWT_GRID) + +#define NMT_PASSWORD_FIELDS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsPrivate)) + +typedef struct { + NmtPasswordFieldsExtras extras; + + NmtNewtEntry *entry; + NmtNewtCheckbox *always_ask; + NmtNewtCheckbox *show_password; + + char *init_password; + +} NmtPasswordFieldsPrivate; + +enum { + PROP_0, + PROP_WIDTH, + PROP_EXTRAS, + PROP_PASSWORD, + PROP_ALWAYS_ASK, + PROP_SHOW_PASSWORD, + + LAST_PROP +}; + +/** + * NmtPasswordFieldsExtras: + * @NMT_PASSWORD_FIELDS_ALWAYS_ASK: show an "Always ask" checkbox + * @NMT_PASSWORD_FIELDS_SHOW_PASSWORD: show a "Show password" checkbox + * + * Extra widgets to include in an #NmtPasswordFields + */ + +/** + * nmt_password_fields_new: + * @width: width in characters of the password entry + * @extras: extra widgets to show + * + * Creates a new #NmtPasswordFields + * + * Returns: a new #NmtPasswordFields + */ +NmtNewtWidget * +nmt_password_fields_new (int width, + NmtPasswordFieldsExtras extras) +{ + return g_object_new (NMT_TYPE_PASSWORD_FIELDS, + "width", width, + "extras", extras, + NULL); +} + +static void +nmt_password_fields_set_password (NmtPasswordFields *fields, + const char *password) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields); + + if (!g_strcmp0 (password, nmt_newt_entry_get_text (priv->entry))) + return; + + nmt_newt_entry_set_text (priv->entry, password); + g_object_notify (G_OBJECT (fields), "password"); +} + +static const char * +nmt_password_fields_get_password (NmtPasswordFields *fields) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields); + + return nmt_newt_entry_get_text (priv->entry); +} + +static void +always_ask_changed (GObject *object, + GParamSpec *pspec, + gpointer fields) +{ + g_object_notify (fields, "always-ask"); +} + +static void +show_password_changed (GObject *object, + GParamSpec *pspec, + gpointer fields) +{ + g_object_notify (fields, "show-password"); +} + +static void +nmt_password_fields_init (NmtPasswordFields *fields) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields); + + priv->entry = NMT_NEWT_ENTRY (nmt_newt_entry_new (-1, 0)); + priv->always_ask = NMT_NEWT_CHECKBOX (nmt_newt_checkbox_new (_("Ask for this password every time"))); + priv->show_password = NMT_NEWT_CHECKBOX (nmt_newt_checkbox_new (_("Show password"))); +} + +static void +nmt_password_fields_constructed (GObject *object) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (object); + NmtNewtGrid *grid = NMT_NEWT_GRID (object); + + nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (priv->entry), 0, 0); + + if (priv->extras & NMT_PASSWORD_FIELDS_ALWAYS_ASK) { + nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (priv->always_ask), 0, 1); + g_signal_connect (priv->always_ask, "notify::active", + G_CALLBACK (always_ask_changed), object); + } else + g_clear_object (&priv->always_ask); + + if (priv->extras & NMT_PASSWORD_FIELDS_SHOW_PASSWORD) { + nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (priv->show_password), 0, 2); + g_signal_connect (priv->show_password, "notify::active", + G_CALLBACK (show_password_changed), object); + g_object_bind_property (priv->show_password, "active", + priv->entry, "password", + G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE); + } else + g_clear_object (&priv->show_password); + + g_object_bind_property (priv->entry, "text", + object, "password", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + G_OBJECT_CLASS (nmt_password_fields_parent_class)->constructed (object); +} + +static void +nmt_password_fields_finalize (GObject *object) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (object); + + if (priv->always_ask) { + g_signal_handlers_disconnect_by_func (priv->always_ask, + G_CALLBACK (always_ask_changed), object); + } + if (priv->show_password) { + g_signal_handlers_disconnect_by_func (priv->show_password, + G_CALLBACK (show_password_changed), object); + } + + G_OBJECT_CLASS (nmt_password_fields_parent_class)->finalize (object); +} + +static void +nmt_password_fields_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtPasswordFields *fields = NMT_PASSWORD_FIELDS (object); + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields); + + switch (prop_id) { + case PROP_WIDTH: + nmt_newt_entry_set_width (priv->entry, g_value_get_int (value)); + break; + case PROP_EXTRAS: + priv->extras = g_value_get_uint (value); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (fields)); + break; + case PROP_PASSWORD: + nmt_password_fields_set_password (fields, g_value_get_string (value)); + break; + case PROP_ALWAYS_ASK: + if (priv->always_ask) + nmt_newt_checkbox_set_active (priv->always_ask, g_value_get_boolean (value)); + break; + case PROP_SHOW_PASSWORD: + if (priv->show_password) + nmt_newt_checkbox_set_active (priv->show_password, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_password_fields_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtPasswordFields *entry = NMT_PASSWORD_FIELDS (object); + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (entry); + + switch (prop_id) { + case PROP_WIDTH: + g_value_set_int (value, nmt_newt_entry_get_width (priv->entry)); + break; + case PROP_EXTRAS: + g_value_set_uint (value, priv->extras); + break; + case PROP_PASSWORD: + g_value_set_string (value, nmt_password_fields_get_password (entry)); + break; + case PROP_ALWAYS_ASK: + if (priv->always_ask) + g_value_set_boolean (value, nmt_newt_checkbox_get_active (priv->always_ask)); + break; + case PROP_SHOW_PASSWORD: + if (priv->show_password) + g_value_set_boolean (value, nmt_newt_checkbox_get_active (priv->show_password)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_password_fields_class_init (NmtPasswordFieldsClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtPasswordFieldsPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_password_fields_constructed; + object_class->set_property = nmt_password_fields_set_property; + object_class->get_property = nmt_password_fields_get_property; + object_class->finalize = nmt_password_fields_finalize; + + /** + * NmtPasswordFields:width: + * + * The width in characters of the password entry + */ + g_object_class_install_property + (object_class, PROP_WIDTH, + g_param_spec_int ("width", "", "", + -1, 80, -1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:extras: + * + * The extra widgets to show + */ + g_object_class_install_property + (object_class, PROP_EXTRAS, + g_param_spec_uint ("extras", "", "", + 0, 0xFFFF, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:password: + * + * The entered password. + */ + g_object_class_install_property + (object_class, PROP_PASSWORD, + g_param_spec_string ("password", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:always-ask: + * + * The current state of the "Always ask" checkbox. + */ + g_object_class_install_property + (object_class, PROP_ALWAYS_ASK, + g_param_spec_boolean ("always-ask", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:show-password: + * + * The current state of the "Show password" checkbox. + */ + g_object_class_install_property + (object_class, PROP_SHOW_PASSWORD, + g_param_spec_boolean ("show-password", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-password-fields.h b/clients/tui/nmt-password-fields.h new file mode 100644 index 00000000..2ef830aa --- /dev/null +++ b/clients/tui/nmt-password-fields.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PASSWORD_FIELDS_H +#define NMT_PASSWORD_FIELDS_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PASSWORD_FIELDS (nmt_password_fields_get_type ()) +#define NMT_PASSWORD_FIELDS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFields)) +#define NMT_PASSWORD_FIELDS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass)) +#define NMT_IS_PASSWORD_FIELDS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PASSWORD_FIELDS)) +#define NMT_IS_PASSWORD_FIELDS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PASSWORD_FIELDS)) +#define NMT_PASSWORD_FIELDS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtPasswordFields; + +typedef struct { + NmtNewtGridClass parent; + +} NmtPasswordFieldsClass; + +GType nmt_password_fields_get_type (void); + +typedef enum { + NMT_PASSWORD_FIELDS_ALWAYS_ASK = (1 << 0), + NMT_PASSWORD_FIELDS_SHOW_PASSWORD = (1 << 1), +} NmtPasswordFieldsExtras; + +NmtNewtWidget *nmt_password_fields_new (int width, + NmtPasswordFieldsExtras extras); + +G_END_DECLS + +#endif /* NMT_PASSWORD_FIELDS_H */ diff --git a/clients/tui/nmt-route-editor.c b/clients/tui/nmt-route-editor.c new file mode 100644 index 00000000..77bbec76 --- /dev/null +++ b/clients/tui/nmt-route-editor.c @@ -0,0 +1,210 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-route-editor + * @short_description: Route editing dialog + * + * #NmtRouteEditor implements a form for editing IPv4 or IPv6 routes. + * This was implemented as a separate dialog because it seemed too + * wide to fit into the main window. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-route-editor.h" +#include "nmt-route-table.h" + +G_DEFINE_TYPE (NmtRouteEditor, nmt_route_editor, NMT_TYPE_NEWT_FORM) + +#define NMT_ROUTE_EDITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorPrivate)) + +typedef struct { + NMSetting *orig_setting; + NMSetting *edit_setting; + +} NmtRouteEditorPrivate; + +enum { + PROP_0, + PROP_SETTING, + + LAST_PROP +}; + +/** + * nmt_route_editor_new: + * @setting: the #NMSettingIP4Config or #NMSettingIP6Config to edit + * + * Creates a new #NmtRouteEditor to edit the routes in @setting + * + * Returns: a new #NmtRouteEditor + */ +NmtNewtForm * +nmt_route_editor_new (NMSetting *setting) +{ + return g_object_new (NMT_TYPE_ROUTE_EDITOR, + "setting", setting, + NULL); +} + +static void +nmt_route_editor_init (NmtRouteEditor *entry) +{ +} + +static void +save_routes_and_exit (NmtNewtButton *button, + gpointer user_data) +{ + NmtRouteEditor *editor = user_data; + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (editor); + GPtrArray *routes; + + g_object_get (priv->edit_setting, + NM_SETTING_IP_CONFIG_ROUTES, &routes, + NULL); + g_object_set (priv->orig_setting, + NM_SETTING_IP_CONFIG_ROUTES, routes, + NULL); + g_ptr_array_unref (routes); + + nmt_newt_form_quit (NMT_NEWT_FORM (editor)); +} + +static void +nmt_route_editor_constructed (GObject *object) +{ + NmtRouteEditor *editor = NMT_ROUTE_EDITOR (object); + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (editor); + NmtNewtWidget *vbox, *routes, *buttons, *ok, *cancel; + + if (G_OBJECT_CLASS (nmt_route_editor_parent_class)->constructed) + G_OBJECT_CLASS (nmt_route_editor_parent_class)->constructed (object); + + if (NM_IS_SETTING_IP4_CONFIG (priv->edit_setting)) + routes = nmt_route_table_new (AF_INET); + else + routes = nmt_route_table_new (AF_INET6); + g_object_bind_property (priv->edit_setting, NM_SETTING_IP_CONFIG_ROUTES, + routes, "routes", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + vbox = nmt_newt_grid_new (); + nmt_newt_grid_add (NMT_NEWT_GRID (vbox), routes, 0, 0); + + buttons = nmt_newt_grid_new (); + nmt_newt_grid_add (NMT_NEWT_GRID (vbox), buttons, 0, 1); + nmt_newt_widget_set_padding (buttons, 0, 1, 0, 0); + + cancel = g_object_ref_sink (nmt_newt_button_new (_("Cancel"))); + nmt_newt_widget_set_exit_on_activate (cancel, TRUE); + nmt_newt_grid_add (NMT_NEWT_GRID (buttons), cancel, 0, 0); + nmt_newt_grid_set_flags (NMT_NEWT_GRID (buttons), cancel, + NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_ANCHOR_RIGHT | + NMT_NEWT_GRID_FILL_Y); + + ok = g_object_ref_sink (nmt_newt_button_new (_("OK"))); + g_signal_connect (ok, "clicked", G_CALLBACK (save_routes_and_exit), editor); + nmt_newt_grid_add (NMT_NEWT_GRID (buttons), ok, 1, 0); + nmt_newt_widget_set_padding (ok, 1, 0, 0, 0); + g_object_bind_property (routes, "valid", + ok, "sensitive", + G_BINDING_SYNC_CREATE); + + nmt_newt_form_set_content (NMT_NEWT_FORM (editor), vbox); +} + +static void +nmt_route_editor_finalize (GObject *object) +{ + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object); + + g_clear_object (&priv->orig_setting); + g_clear_object (&priv->edit_setting); + + G_OBJECT_CLASS (nmt_route_editor_parent_class)->finalize (object); +} + +static void +nmt_route_editor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SETTING: + priv->orig_setting = g_value_dup_object (value); + priv->edit_setting = nm_setting_duplicate (priv->orig_setting); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_editor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SETTING: + g_value_set_object (value, priv->edit_setting); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_editor_class_init (NmtRouteEditorClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtRouteEditorPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_route_editor_constructed; + object_class->set_property = nmt_route_editor_set_property; + object_class->get_property = nmt_route_editor_get_property; + object_class->finalize = nmt_route_editor_finalize; + + /** + * NmtRouteEditor:setting: + * + * The #NMSettingIP4Config or #NMSettingIP6Config whose routes are + * being edited. + */ + g_object_class_install_property + (object_class, PROP_SETTING, + g_param_spec_object ("setting", "", "", + NM_TYPE_SETTING, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-route-editor.h b/clients/tui/nmt-route-editor.h new file mode 100644 index 00000000..9882c713 --- /dev/null +++ b/clients/tui/nmt-route-editor.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_ROUTE_EDITOR_H +#define NMT_ROUTE_EDITOR_H + +#include <NetworkManager.h> + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_ROUTE_EDITOR (nmt_route_editor_get_type ()) +#define NMT_ROUTE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditor)) +#define NMT_ROUTE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass)) +#define NMT_IS_ROUTE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_EDITOR)) +#define NMT_IS_ROUTE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_EDITOR)) +#define NMT_ROUTE_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass)) + +typedef struct { + NmtNewtForm parent; + +} NmtRouteEditor; + +typedef struct { + NmtNewtFormClass parent; + +} NmtRouteEditorClass; + +GType nmt_route_editor_get_type (void); + +NmtNewtForm *nmt_route_editor_new (NMSetting *setting); + +G_END_DECLS + +#endif /* NMT_ROUTE_EDITOR_H */ diff --git a/clients/tui/nmt-route-entry.c b/clients/tui/nmt-route-entry.c new file mode 100644 index 00000000..3a875520 --- /dev/null +++ b/clients/tui/nmt-route-entry.c @@ -0,0 +1,291 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-route-entry + * @short_description: A set of widgets describing a single route. + * + * #NmtRouteEntry provides a set of three entry widgets, for entering + * a network/prefix, a next hop, and a metric. + * + * This is used as a building block by #NmtRouteTable. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include <glib/gi18n-lib.h> +#include <NetworkManager.h> + +#include "nmt-route-entry.h" +#include "nmt-ip-entry.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE (NmtRouteEntry, nmt_route_entry, NMT_TYPE_NEWT_GRID) + +#define NMT_ROUTE_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryPrivate)) + +typedef struct { + NmtNewtWidget *dest, *next_hop, *metric; + + int family; + int ip_entry_width, metric_entry_width; + NMIPRoute *route; +} NmtRouteEntryPrivate; + +enum { + PROP_0, + PROP_FAMILY, + PROP_IP_ENTRY_WIDTH, + PROP_METRIC_ENTRY_WIDTH, + PROP_ROUTE, + + LAST_PROP +}; + +/** + * nmt_route_entry_new: + * @family: the address family, eg %AF_INET + * @ip_entry_width: the width in characters for the IP address entries + * @metric_entry_width: the width in characters for the metric entry + * + * Creates a new #NmtRouteEntry + * + * Returns: a new #NmtRouteEntry + */ +NmtNewtWidget * +nmt_route_entry_new (int family, + int ip_entry_width, + int metric_entry_width) +{ + return g_object_new (NMT_TYPE_ROUTE_ENTRY, + "family", family, + "ip-entry-width", ip_entry_width, + "metric-entry-width", metric_entry_width, + NULL); +} + +static void +nmt_route_entry_init (NmtRouteEntry *entry) +{ +} + +static gboolean +entry_validity_transform_to_warning_label (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + if (g_value_get_boolean (source_value)) + g_value_set_string (target_value, " "); + else + g_value_set_string (target_value, "!"); + return TRUE; +} + +static NmtNewtWidget * +create_warning_label (NmtNewtWidget *entry) +{ + NmtNewtWidget *label; + + label = g_object_new (NMT_TYPE_NEWT_LABEL, + "highlight", TRUE, + NULL); + g_object_bind_property_full (entry, "valid", + label, "text", + G_BINDING_SYNC_CREATE, + entry_validity_transform_to_warning_label, + NULL, NULL, NULL); + return label; +} + +static void +nmt_route_entry_constructed (GObject *object) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object); + NmtNewtGrid *grid = NMT_NEWT_GRID (object); + NmtNewtWidget *warning_label; + + priv->dest = nmt_ip_entry_new (priv->ip_entry_width, priv->family, TRUE, FALSE); + priv->next_hop = nmt_ip_entry_new (priv->ip_entry_width, priv->family, FALSE, TRUE); + priv->metric = nmt_newt_entry_numeric_new (priv->metric_entry_width, 0, 65535); + + nmt_newt_grid_add (grid, priv->dest, 0, 0); + warning_label = create_warning_label (priv->dest); + nmt_newt_grid_add (grid, warning_label, 1, 0); + + nmt_newt_grid_add (grid, priv->next_hop, 2, 0); + nmt_newt_widget_set_padding (priv->next_hop, 1, 0, 0, 0); + warning_label = create_warning_label (priv->next_hop); + nmt_newt_grid_add (grid, warning_label, 3, 0); + + nmt_newt_grid_add (grid, priv->metric, 4, 0); + nmt_newt_widget_set_padding (priv->metric, 1, 0, 0, 0); + + nm_editor_bind_ip_route_to_strings (priv->family, + object, "route", + priv->dest, "text", + priv->next_hop, "text", + priv->metric, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + G_OBJECT_CLASS (nmt_route_entry_parent_class)->constructed (object); +} + +static newtComponent +nmt_route_entry_get_focus_component (NmtNewtWidget *widget) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (widget); + + return nmt_newt_widget_get_focus_component (priv->dest); +} + +static void +nmt_route_entry_finalize (GObject *object) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object); + + g_clear_pointer (&priv->route, nm_ip_route_unref); + + G_OBJECT_CLASS (nmt_route_entry_parent_class)->finalize (object); +} + +static void +nmt_route_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + priv->family = g_value_get_int (value); + break; + case PROP_IP_ENTRY_WIDTH: + priv->ip_entry_width = g_value_get_int (value); + break; + case PROP_METRIC_ENTRY_WIDTH: + priv->metric_entry_width = g_value_get_int (value); + break; + case PROP_ROUTE: + if (priv->route) + nm_ip_route_unref (priv->route); + priv->route = g_value_dup_boxed (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int (value, priv->family); + break; + case PROP_IP_ENTRY_WIDTH: + g_value_set_int (value, priv->ip_entry_width); + break; + case PROP_METRIC_ENTRY_WIDTH: + g_value_set_int (value, priv->metric_entry_width); + break; + case PROP_ROUTE: + g_value_set_boxed (value, priv->route); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_entry_class_init (NmtRouteEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtRouteEntryPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_route_entry_constructed; + object_class->set_property = nmt_route_entry_set_property; + object_class->get_property = nmt_route_entry_get_property; + object_class->finalize = nmt_route_entry_finalize; + + widget_class->get_focus_component = nmt_route_entry_get_focus_component; + + /** + * NmtRouteEntry:family: + * + * The address family of the route, eg, %AF_INET + */ + g_object_class_install_property + (object_class, PROP_FAMILY, + g_param_spec_int ("family", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteEntry:ip-entry-width: + * + * The width in characters of the IP address entries + */ + g_object_class_install_property + (object_class, PROP_IP_ENTRY_WIDTH, + g_param_spec_int ("ip-entry-width", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteEntry:metric-entry-width: + * + * The width in characters of the metric entry + */ + g_object_class_install_property + (object_class, PROP_METRIC_ENTRY_WIDTH, + g_param_spec_int ("metric-entry-width", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteEntry:route: + * + * The contents of the entries, as an #NMIPRoute. + */ + g_object_class_install_property + (object_class, PROP_ROUTE, + g_param_spec_boxed ("route", "", "", + nm_ip_route_get_type (), + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-route-entry.h b/clients/tui/nmt-route-entry.h new file mode 100644 index 00000000..d37b14cd --- /dev/null +++ b/clients/tui/nmt-route-entry.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_ROUTE_ENTRY_H +#define NMT_ROUTE_ENTRY_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_ROUTE_ENTRY (nmt_route_entry_get_type ()) +#define NMT_ROUTE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntry)) +#define NMT_ROUTE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass)) +#define NMT_IS_ROUTE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_ENTRY)) +#define NMT_IS_ROUTE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_ENTRY)) +#define NMT_ROUTE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtRouteEntry; + +typedef struct { + NmtNewtGridClass parent; + +} NmtRouteEntryClass; + +GType nmt_route_entry_get_type (void); + +NmtNewtWidget *nmt_route_entry_new (int family, + int ip_entry_width, + int metric_entry_width); + +G_END_DECLS + +#endif /* NMT_ROUTE_ENTRY_H */ diff --git a/clients/tui/nmt-route-table.c b/clients/tui/nmt-route-table.c new file mode 100644 index 00000000..523d8ee3 --- /dev/null +++ b/clients/tui/nmt-route-table.c @@ -0,0 +1,334 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-route-table + * @short_description: An editable list of IP4 or IP6 routes + * + * #NmtRouteTable implements a list of #NmtRouteEntry, plus headers, + * and buttons to add and remove entries. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include <glib/gi18n-lib.h> +#include <NetworkManager.h> + +#include "nmt-route-table.h" +#include "nmt-route-entry.h" +#include "nmt-widget-list.h" + +G_DEFINE_TYPE (NmtRouteTable, nmt_route_table, NMT_TYPE_NEWT_GRID) + +#define NMT_ROUTE_TABLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ROUTE_TABLE, NmtRouteTablePrivate)) + +typedef struct { + int family; + + int ip_entry_width; + int metric_entry_width; + + GPtrArray *routes; + NmtNewtWidget *list; +} NmtRouteTablePrivate; + +enum { + PROP_0, + PROP_FAMILY, + PROP_ROUTES, + + LAST_PROP +}; + +/** + * nmt_route_table_new: + * @family: the address family, eg %AF_INET + * + * Creates a new #NmtRouteTable + * + * Returns: a new #NmtRouteTable + */ +NmtNewtWidget * +nmt_route_table_new (int family) +{ + return g_object_new (NMT_TYPE_ROUTE_TABLE, + "family", family, + NULL); +} + +static gboolean +route_list_transform_to_route (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NmtRouteTable *table = NMT_ROUTE_TABLE (g_binding_get_source (binding)); + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + int n = GPOINTER_TO_INT (user_data); + NMIPRoute *route; + + if (n >= priv->routes->len) + return FALSE; + + route = priv->routes->pdata[n]; + g_value_set_boxed (target_value, route); + return TRUE; +} + +static gboolean +route_list_transform_from_route (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NmtRouteTable *table = NMT_ROUTE_TABLE (g_binding_get_source (binding)); + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + int n = GPOINTER_TO_INT (user_data); + GPtrArray *routes; + NMIPRoute *route; + + if (n >= priv->routes->len) + return FALSE; + route = priv->routes->pdata[n]; + + routes = priv->routes; + priv->routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_route_unref); + + if (route) + nm_ip_route_unref (route); + routes->pdata[n] = g_value_dup_boxed (source_value); + + g_value_take_boxed (target_value, routes); + return TRUE; +} + +static NmtNewtWidget * +create_route_entry (NmtWidgetList *list, + int num, + gpointer table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + NmtNewtWidget *entry; + + entry = nmt_route_entry_new (priv->family, + priv->ip_entry_width, + priv->metric_entry_width); + + g_object_bind_property_full (table, "routes", + entry, "route", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + route_list_transform_to_route, + route_list_transform_from_route, + GINT_TO_POINTER (num), NULL); + return entry; +} + +static void +add_route (NmtWidgetList *list, + gpointer table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + NMIPRoute *route; + + if (priv->family == AF_INET) + route = nm_ip_route_new (AF_INET, "0.0.0.0", 32, NULL, 0, NULL); + else + route = nm_ip_route_new (AF_INET6, "::", 128, NULL, 0, NULL); + g_ptr_array_add (priv->routes, route); + nmt_widget_list_set_length (list, priv->routes->len); + g_object_notify (table, "routes"); +} + +static void +remove_route (NmtWidgetList *list, + int num, + gpointer table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + NMIPRoute *route; + + if (num >= priv->routes->len) + return; + + route = priv->routes->pdata[num]; + g_ptr_array_remove_index (priv->routes, num); + nmt_widget_list_set_length (list, priv->routes->len); + + g_object_notify (table, "routes"); +} + +static void +nmt_route_table_init (NmtRouteTable *table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + NmtNewtWidget *header, *empty; + NmtNewtWidget *dest_prefix_label, *next_hop_label, *metric_label; + int dest_prefix_width, next_hop_width, metric_width; + char *text; + + priv->routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_route_unref); + + header = nmt_newt_grid_new (); + + text = g_strdup_printf ("%s/%s", _("Destination"), _("Prefix")); + dest_prefix_width = nmt_newt_text_width (text); + dest_prefix_label = g_object_new (NMT_TYPE_NEWT_LABEL, + "text", text, + "style", NMT_NEWT_LABEL_PLAIN, + NULL); + g_free (text); + nmt_newt_grid_add (NMT_NEWT_GRID (header), dest_prefix_label, 0, 0); + + text = _("Next Hop"); + next_hop_label = g_object_new (NMT_TYPE_NEWT_LABEL, + "text", text, + "style", NMT_NEWT_LABEL_PLAIN, + NULL); + next_hop_width = nmt_newt_text_width (text); + nmt_newt_grid_add (NMT_NEWT_GRID (header), next_hop_label, 1, 0); + + text = _("Metric"); + metric_label = g_object_new (NMT_TYPE_NEWT_LABEL, + "text", text, + "style", NMT_NEWT_LABEL_PLAIN, + NULL); + metric_width = nmt_newt_text_width (text); + nmt_newt_grid_add (NMT_NEWT_GRID (header), metric_label, 2, 0); + + priv->ip_entry_width = MAX (20, MAX (dest_prefix_width, next_hop_width)); + priv->metric_entry_width = MAX (7, metric_width); + + nmt_newt_widget_set_padding (dest_prefix_label, + 0, 0, priv->ip_entry_width - dest_prefix_width, 0); + nmt_newt_widget_set_padding (next_hop_label, + 2, 0, priv->ip_entry_width - next_hop_width, 0); + nmt_newt_widget_set_padding (metric_label, + 2, 0, priv->metric_entry_width - metric_width, 0); + + nmt_newt_grid_add (NMT_NEWT_GRID (table), header, 0, 0); + + empty = nmt_newt_label_new (_("No custom routes are defined.")); + priv->list = nmt_widget_list_new (create_route_entry, table, NULL, empty); + g_signal_connect (priv->list, "add-clicked", G_CALLBACK (add_route), table); + g_signal_connect (priv->list, "remove-clicked", G_CALLBACK (remove_route), table); + nmt_newt_grid_add (NMT_NEWT_GRID (table), priv->list, 0, 1); +} + +static void +nmt_route_table_finalize (GObject *object) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (object); + + g_ptr_array_unref (priv->routes); + + G_OBJECT_CLASS (nmt_route_table_parent_class)->finalize (object); +} + +static void +nmt_route_table_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (object); + GPtrArray *array; + int i; + + switch (prop_id) { + case PROP_FAMILY: + priv->family = g_value_get_int (value); + break; + case PROP_ROUTES: + array = g_value_get_boxed (value); + g_ptr_array_set_size (priv->routes, 0); + for (i = 0; i < array->len; i++) { + nm_ip_route_ref (array->pdata[i]); + g_ptr_array_add (priv->routes, array->pdata[i]); + } + nmt_widget_list_set_length (NMT_WIDGET_LIST (priv->list), priv->routes->len); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_table_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int (value, priv->family); + break; + case PROP_ROUTES: + g_value_set_boxed (value, priv->routes); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_table_class_init (NmtRouteTableClass *table_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (table_class); + + g_type_class_add_private (table_class, sizeof (NmtRouteTablePrivate)); + + /* virtual methods */ + object_class->set_property = nmt_route_table_set_property; + object_class->get_property = nmt_route_table_get_property; + object_class->finalize = nmt_route_table_finalize; + + /** + * NmtRouteTable:family: + * + * The network address family of the routes, eg %AF_INET + */ + g_object_class_install_property + (object_class, PROP_FAMILY, + g_param_spec_int ("family", "", "", + -1, G_MAXINT, -1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteTable:routes: + * + * The array of routes, suitable for binding to #NMSettingIP4Config:routes + * or #NMSettingIP6Config:routes. + * + * Element-type: NMIPRoute + */ + g_object_class_install_property + (object_class, PROP_ROUTES, + g_param_spec_boxed ("routes", "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-route-table.h b/clients/tui/nmt-route-table.h new file mode 100644 index 00000000..217bf40d --- /dev/null +++ b/clients/tui/nmt-route-table.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_ROUTE_TABLE_H +#define NMT_ROUTE_TABLE_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_ROUTE_TABLE (nmt_route_table_get_type ()) +#define NMT_ROUTE_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTable)) +#define NMT_ROUTE_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass)) +#define NMT_IS_ROUTE_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_TABLE)) +#define NMT_IS_ROUTE_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_TABLE)) +#define NMT_ROUTE_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtRouteTable; + +typedef struct { + NmtNewtGridClass parent; + +} NmtRouteTableClass; + +GType nmt_route_table_get_type (void); + +NmtNewtWidget *nmt_route_table_new (int family); + +G_END_DECLS + +#endif /* NMT_ROUTE_TABLE_H */ diff --git a/clients/tui/nmt-slave-list.c b/clients/tui/nmt-slave-list.c new file mode 100644 index 00000000..9c8596f7 --- /dev/null +++ b/clients/tui/nmt-slave-list.c @@ -0,0 +1,266 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-slave-list: + * @short_description: An editable list of a connection's slaves + * + * #NmtSlaveList implements an #NmtEditConnectionList for the + * slaves of a connection. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> +#include <NetworkManager.h> + +#include "nmt-slave-list.h" + +G_DEFINE_TYPE (NmtSlaveList, nmt_slave_list, NMT_TYPE_EDIT_CONNECTION_LIST) + +#define NMT_SLAVE_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_SLAVE_LIST, NmtSlaveListPrivate)) + +typedef struct { + NMConnection *master; + const char *master_type, *master_uuid; + + NmtAddConnectionTypeFilter type_filter; + gpointer type_filter_data; +} NmtSlaveListPrivate; + +enum { + PROP_0, + PROP_MASTER, + PROP_TYPE_FILTER, + PROP_TYPE_FILTER_DATA, + + LAST_PROP +}; + +static gboolean nmt_slave_list_connection_filter (NmtEditConnectionList *list, + NMConnection *connection, + gpointer user_data); + +/** + * nmt_slave_list_new: + * @master: the master #NMConnection whose slaves are being listed + * @type_filter: (allow-none): a function to limit the availble slave types + * @type_filter_data: (allow-none): data for @type_filter. + * + * Creates a new #NmtSlaveList. + * + * If @type_filter is non-%NULL, it will be used to limit the connection + * types that are available when the user clicks on the "Add" button to add + * a new slave. If the @type_filter filters the list down to only a single + * connection type, then the user will not be presented with a connection-type + * dialog, and will instead be immediately taken to an editor window for the + * new slave after clicking "Add". + * + * Returns: a new #NmtSlaveList. + */ +NmtNewtWidget * +nmt_slave_list_new (NMConnection *master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data) +{ + return g_object_new (NMT_TYPE_SLAVE_LIST, + "master", master, + "type-filter", type_filter, + "type-filter-data", type_filter_data, + "grouped", FALSE, + "connection-filter", nmt_slave_list_connection_filter, + NULL); +} + +static void +nmt_slave_list_init (NmtSlaveList *list) +{ +} + +static void +nmt_slave_list_finalize (GObject *object) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object); + + g_object_unref (priv->master); + + G_OBJECT_CLASS (nmt_slave_list_parent_class)->finalize (object); +} + +static gboolean +nmt_slave_list_connection_filter (NmtEditConnectionList *list, + NMConnection *connection, + gpointer user_data) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (list); + NMSettingConnection *s_con; + const char *master, *master_ifname, *slave_type; + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con != NULL, FALSE); + + slave_type = nm_setting_connection_get_slave_type (s_con); + if (g_strcmp0 (slave_type, priv->master_type) != 0) + return FALSE; + + master = nm_setting_connection_get_master (s_con); + if (!master) + return FALSE; + + master_ifname = nm_connection_get_interface_name (priv->master); + if (g_strcmp0 (master, master_ifname) != 0 && g_strcmp0 (master, priv->master_uuid) != 0) + return FALSE; + + return TRUE; +} + +static void +nmt_slave_list_add_connection (NmtEditConnectionList *list) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (list); + + nmt_add_connection_full (_("Select the type of slave connection you wish to add."), NULL, + priv->master, priv->type_filter, priv->type_filter_data); +} + +static void +nmt_slave_list_edit_connection (NmtEditConnectionList *list, + NMConnection *connection) +{ + nmt_edit_connection (connection); +} + +static void +nmt_slave_list_remove_connection (NmtEditConnectionList *list, + NMRemoteConnection *connection) +{ + nmt_remove_connection (connection); +} + +static void +nmt_slave_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MASTER: + priv->master = g_value_dup_object (value); + if (priv->master) { + NMSettingConnection *s_con = nm_connection_get_setting_connection (priv->master); + + priv->master_type = nm_setting_connection_get_connection_type (s_con); + priv->master_uuid = nm_setting_connection_get_uuid (s_con); + } + break; + case PROP_TYPE_FILTER: + priv->type_filter = g_value_get_pointer (value); + break; + case PROP_TYPE_FILTER_DATA: + priv->type_filter_data = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_slave_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MASTER: + g_value_set_object (value, priv->master); + break; + case PROP_TYPE_FILTER: + g_value_set_pointer (value, priv->type_filter); + break; + case PROP_TYPE_FILTER_DATA: + g_value_set_pointer (value, priv->type_filter_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_slave_list_class_init (NmtSlaveListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (list_class); + NmtEditConnectionListClass *connection_list_class = NMT_EDIT_CONNECTION_LIST_CLASS (list_class); + + g_type_class_add_private (list_class, sizeof (NmtSlaveListPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_slave_list_set_property; + object_class->get_property = nmt_slave_list_get_property; + object_class->finalize = nmt_slave_list_finalize; + + connection_list_class->add_connection = nmt_slave_list_add_connection; + connection_list_class->edit_connection = nmt_slave_list_edit_connection; + connection_list_class->remove_connection = nmt_slave_list_remove_connection; + + /** + * NmtSlaveList:master: + * + * The master #NMConnection whose slaves are being displayed. + */ + g_object_class_install_property + (object_class, PROP_MASTER, + g_param_spec_object ("master", "", "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtSlaveList:type-filter: + * + * If non-%NULL, this will be used to limit the connection types + * that are available when the user clicks on the "Add" button to + * add a new slave. If the filter filters the list down to only a + * single connection type, then the user will not be presented + * with a connection-type dialog, and will instead be immediately + * taken to an editor window for the new slave after clicking + * "Add". + */ + g_object_class_install_property + (object_class, PROP_TYPE_FILTER, + g_param_spec_pointer ("type-filter", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtSlaveList:type-filter-data: + * + * User data passed to #NmtSlaveList:type-filter + */ + g_object_class_install_property + (object_class, PROP_TYPE_FILTER_DATA, + g_param_spec_pointer ("type-filter-data", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-slave-list.h b/clients/tui/nmt-slave-list.h new file mode 100644 index 00000000..d10cd3a7 --- /dev/null +++ b/clients/tui/nmt-slave-list.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_SLAVE_LIST_H +#define NMT_SLAVE_LIST_H + +#include "nmt-edit-connection-list.h" +#include "nmtui-edit.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_SLAVE_LIST (nmt_slave_list_get_type ()) +#define NMT_SLAVE_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveList)) +#define NMT_SLAVE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass)) +#define NMT_IS_SLAVE_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_SLAVE_LIST)) +#define NMT_IS_SLAVE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_SLAVE_LIST)) +#define NMT_SLAVE_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass)) + +typedef struct { + NmtEditConnectionList parent; + +} NmtSlaveList; + +typedef struct { + NmtEditConnectionListClass parent; + +} NmtSlaveListClass; + +GType nmt_slave_list_get_type (void); + +NmtNewtWidget *nmt_slave_list_new (NMConnection *master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data); + +G_END_DECLS + +#endif /* NMT_SLAVE_LIST_H */ diff --git a/clients/tui/nmt-utils.c b/clients/tui/nmt-utils.c new file mode 100644 index 00000000..f2bf70db --- /dev/null +++ b/clients/tui/nmt-utils.c @@ -0,0 +1,151 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-utils + * @short_description: Miscellaneous nmtui-specific utilities + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-utils.h" + +/** + * NmtSyncOp: + * + * A helper object used when synchronously waiting for an asynchronous + * operation to complete. + * + * The caller first does: + * + * |[ + * NmtSyncOp op; + * + * nmt_sync_op_init (&op); + * ]| + * + * It then passes the op as the user_data to the async operation's + * callback function, and then calls nmt_sync_op_wait_boolean() or + * nmt_sync_op_wait_pointer() to wait for the result. + * + * When the async callback is invoked, it should call + * nmt_sync_op_complete_boolean() or nmt_sync_op_complete_pointer() to + * return a result or an error to the caller. + * + * There is no free/clear function; any memory that needs to be freed + * will have been returned to the caller from + * nmt_sync_op_wait_boolean() or nmt_sync_op_wait_pointer(), so there + * is nothing left that needs to be freed. + */ + +typedef struct { + gpointer result; + GError *error; + gpointer complete; +} NmtSyncOpReal; + +/** + * nmt_sync_op_init: + * @op: pointer to a stack-allocated #NmtSyncOp + * + * Initializes @op before use. + */ +void +nmt_sync_op_init (NmtSyncOp *op) +{ + memset (op, 0, sizeof (*op)); +} + +/** + * nmt_sync_op_wait_boolean: + * @op: the #NmtSyncOp + * @error: return location for a #GError + * + * This runs the main loop until @op's operation returns, and then + * returns the result or error. + * + * Returns: the result of the operation. + */ +gboolean +nmt_sync_op_wait_boolean (NmtSyncOp *op, + GError **error) +{ + return GPOINTER_TO_UINT (nmt_sync_op_wait_pointer (op, error)); +} + +/** + * nmt_sync_op_complete_boolean: + * @op: the #NmtSyncOp + * @result: the result of the operation + * @error: (allow-none): the error, or %NULL + * + * Completes @op and returns @result and/or @error to the caller. + */ +void +nmt_sync_op_complete_boolean (NmtSyncOp *op, + gboolean result, + GError *error) +{ + nmt_sync_op_complete_pointer (op, GUINT_TO_POINTER (result), error); +} + +/** + * nmt_sync_op_wait_pointer: + * @op: the #NmtSyncOp + * @error: return location for a #GError + * + * This runs the main loop until @op's operation returns, and then + * returns the result or error. + * + * Returns: the result of the operation. + */ +gpointer +nmt_sync_op_wait_pointer (NmtSyncOp *op, + GError **error) +{ + NmtSyncOpReal *real = (NmtSyncOpReal *)op; + + while (!real->complete) + g_main_context_iteration (NULL, TRUE); + + if (real->error) + g_propagate_error (error, real->error); + return real->result; +} + +/** + * nmt_sync_op_complete_pointer: + * @op: the #NmtSyncOp + * @result: the result of the operation + * @error: (allow-none): the error, or %NULL + * + * Completes @op and returns @result and/or @error to the caller. + */ +void +nmt_sync_op_complete_pointer (NmtSyncOp *op, + gpointer result, + GError *error) +{ + NmtSyncOpReal *real = (NmtSyncOpReal *)op; + + real->result = result; + real->error = error ? g_error_copy (error) : NULL; + real->complete = GUINT_TO_POINTER (TRUE); +} diff --git a/clients/tui/nmt-utils.h b/clients/tui/nmt-utils.h new file mode 100644 index 00000000..7e24fcab --- /dev/null +++ b/clients/tui/nmt-utils.h @@ -0,0 +1,46 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_UTILS_H +#define NMT_UTILS_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef struct { + gpointer private[3]; +} NmtSyncOp; + +void nmt_sync_op_init (NmtSyncOp *op); + +gboolean nmt_sync_op_wait_boolean (NmtSyncOp *op, + GError **error); +void nmt_sync_op_complete_boolean (NmtSyncOp *op, + gboolean result, + GError *error); + +gpointer nmt_sync_op_wait_pointer (NmtSyncOp *op, + GError **error); +void nmt_sync_op_complete_pointer (NmtSyncOp *op, + gpointer result, + GError *error); + +G_END_DECLS + +#endif /* NMT_UTILS_H */ diff --git a/clients/tui/nmt-widget-list.c b/clients/tui/nmt-widget-list.c new file mode 100644 index 00000000..9159ed98 --- /dev/null +++ b/clients/tui/nmt-widget-list.c @@ -0,0 +1,496 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-widget-list + * @short_description: A list of widgets, with Add and Remove buttons + * + * #NmtWidgetList presents a homogeneous list of widgets, with "Remove" + * buttons next to each one, and an "Add" button at the button to add + * new ones. + * + * It is the base class for #NmtAddressList, and is used internally by + * #NmtRouteTable. + * + * FIXME: The way this works is sort of weird. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> + +#include <glib/gi18n-lib.h> + +#include "nmt-widget-list.h" +#include "nmt-newt.h" + +G_DEFINE_TYPE (NmtWidgetList, nmt_widget_list, NMT_TYPE_NEWT_GRID) + +#define NMT_WIDGET_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_WIDGET_LIST, NmtWidgetListPrivate)) + +typedef struct { + int length; + + NmtWidgetListCallback create_callback; + gpointer user_data; + GDestroyNotify destroy_notify; + + NmtNewtWidget *empty_widget; + + GPtrArray *widgets; + GPtrArray *remove_buttons; + + NmtNewtWidget *add_button; + GBinding *add_sensitivity; +} NmtWidgetListPrivate; + +enum { + PROP_0, + PROP_CREATE_CALLBACK, + PROP_USER_DATA, + PROP_DESTROY_NOTIFY, + PROP_EMPTY_WIDGET, + PROP_LENGTH, + + LAST_PROP +}; + +enum { + ADD_CLICKED, + REMOVE_CLICKED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void add_clicked (NmtNewtButton *button, gpointer user_data); +static void remove_clicked (NmtNewtButton *button, gpointer user_data); + +/** + * NmtWidgetListCallback: + * @list: the #NmtWidgetList + * @n: the number of the widget being added + * @user_data: the callback's user data + * + * Called by #NmtWidgetList to ask for a new widget to be created. + * + * Note that the widget is not created to go with any particular + * value, but rather is created to be at a certain spot in the list. + * When an element is deleted from the list, it is actually always + * the last widget in the list that is removed, but it is assumed + * that the widget list is bound to some array-valued property, and + * so when an element is deleted from that array, the widgets will + * all update themselves automatically when the array changes. + * + * Returns: a new widget for the list + */ + +/** + * nmt_widget_list_new: + * @create_callback: callback to create new widgets + * @user_data: user data for @create_callback + * @destroy_notify: #GDestroyNotify for @user_data + * @empty_widget: (allow-none): a widget to display when there are + * no "real" widgets in the list. + * + * Creates a new #NmtWidgetList. + * + * Returns: a new #NmtWidgetList. + */ +NmtNewtWidget * +nmt_widget_list_new (NmtWidgetListCallback create_callback, + gpointer user_data, + GDestroyNotify destroy_notify, + NmtNewtWidget *empty_widget) +{ + return g_object_new (NMT_TYPE_WIDGET_LIST, + "create-callback", create_callback, + "user-data", user_data, + "destroy-notify", destroy_notify, + "empty-widget", empty_widget, + NULL); +} + +static void +nmt_widget_list_init (NmtWidgetList *list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + + priv->widgets = g_ptr_array_new (); + priv->remove_buttons = g_ptr_array_new (); + + priv->add_button = nmt_newt_button_new (_("Add...")); + g_signal_connect (priv->add_button, "clicked", + G_CALLBACK (add_clicked), list); + nmt_newt_grid_add (NMT_NEWT_GRID (list), priv->add_button, 0, 0); +} + +static void +nmt_widget_list_constructed (GObject *object) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object); + + if (priv->length == 0 && priv->empty_widget) { + nmt_newt_widget_set_visible (priv->empty_widget, TRUE); + nmt_newt_grid_move (NMT_NEWT_GRID (object), priv->add_button, 0, 1); + } + + G_OBJECT_CLASS (nmt_widget_list_parent_class)->constructed (object); +} + +static void +nmt_widget_list_finalize (GObject *object) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object); + + g_ptr_array_unref (priv->widgets); + g_ptr_array_unref (priv->remove_buttons); + + if (priv->user_data && priv->destroy_notify) + priv->destroy_notify (priv->user_data); + + g_clear_object (&priv->empty_widget); + + G_OBJECT_CLASS (nmt_widget_list_parent_class)->finalize (object); +} + +static void +ensure_widgets (NmtWidgetList *list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + NmtNewtWidget *widget, *button, *focus; + gboolean was_empty; + NmtNewtForm *form; + int i; + + was_empty = priv->widgets->len == 0; + + if (priv->length < priv->widgets->len) { + /* remove excess widgets */ + for (i = priv->length; i < priv->widgets->len; i++) { + nmt_newt_container_remove (NMT_NEWT_CONTAINER (list), priv->widgets->pdata[i]); + nmt_newt_container_remove (NMT_NEWT_CONTAINER (list), priv->remove_buttons->pdata[i]); + } + g_ptr_array_set_size (priv->widgets, priv->length); + g_ptr_array_set_size (priv->remove_buttons, priv->length); + + } else if (priv->length > priv->widgets->len) { + /* add new widgets */ + for (i = priv->widgets->len; i < priv->length; i++) { + widget = NMT_WIDGET_LIST_GET_CLASS (list)->create_widget (list, i); + + nmt_newt_grid_add (NMT_NEWT_GRID (list), widget, 0, i); + g_ptr_array_add (priv->widgets, widget); + + button = nmt_newt_button_new (_("Remove")); + g_signal_connect (button, "clicked", + G_CALLBACK (remove_clicked), list); + + nmt_newt_grid_add (NMT_NEWT_GRID (list), button, 1, i); + nmt_newt_widget_set_padding (button, 1, 0, 0, 0); + g_ptr_array_add (priv->remove_buttons, button); + } + + } else + return; + + if (priv->widgets->len == 0 && priv->empty_widget) { + nmt_newt_widget_set_visible (priv->empty_widget, TRUE); + nmt_newt_grid_move (NMT_NEWT_GRID (list), priv->add_button, 0, 1); + } else { + if (was_empty && priv->empty_widget) + nmt_newt_widget_set_visible (priv->empty_widget, FALSE); + nmt_newt_grid_move (NMT_NEWT_GRID (list), priv->add_button, 0, priv->length); + } + + form = nmt_newt_widget_get_form (NMT_NEWT_WIDGET (list)); + if (form) { + if (priv->widgets->len) { + if (was_empty) + focus = priv->widgets->pdata[0]; + else + focus = priv->widgets->pdata[priv->widgets->len - 1]; + } else + focus = priv->add_button; + nmt_newt_form_set_focus (form, focus); + } + + g_clear_object (&priv->add_sensitivity); + if (priv->widgets->len) { + widget = priv->widgets->pdata[priv->widgets->len - 1]; + priv->add_sensitivity = g_object_bind_property (widget, "valid", + priv->add_button, "sensitive", + G_BINDING_SYNC_CREATE); + g_object_add_weak_pointer (G_OBJECT (priv->add_sensitivity), + (gpointer *)&priv->add_sensitivity); + } else + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->add_button), TRUE); +} + +static void +add_clicked (NmtNewtButton *button, gpointer list) +{ + g_signal_emit (G_OBJECT (list), signals[ADD_CLICKED], 0, NULL); +} + +static void +remove_clicked (NmtNewtButton *button, gpointer list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + int i; + + for (i = 0; i < priv->remove_buttons->len; i++) { + if (priv->remove_buttons->pdata[i] == (gpointer)button) + break; + } + g_return_if_fail (i < priv->remove_buttons->len); + + g_signal_emit (G_OBJECT (list), signals[REMOVE_CLICKED], 0, i, NULL); +} + +static NmtNewtWidget * +nmt_widget_list_real_create_widget (NmtWidgetList *list, + int n) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + + g_return_val_if_fail (priv->create_callback != NULL, NULL); + + return priv->create_callback (list, n, priv->user_data); +} + +/** + * nmt_widget_list_get_length: + * @list: the #NmtNewtWidgetList + * + * Gets the number of widgets in the list. + * + * Returns: the number of widgets in the list. + */ +int +nmt_widget_list_get_length (NmtWidgetList *list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + + return priv->length; +} + +/** + * nmt_widget_list_set_length: + * @list: the #NmtNewtWidgetList + * @length: the new length + * + * Changes the number of widgets in the list. Widgets will be added or + * deleted as necessary. + */ +void +nmt_widget_list_set_length (NmtWidgetList *list, + int length) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + + if (priv->length != length) { + priv->length = length; + g_object_notify (G_OBJECT (list), "length"); + } + + ensure_widgets (list); +} + +static void +nmt_widget_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CREATE_CALLBACK: + priv->create_callback = g_value_get_pointer (value); + break; + case PROP_USER_DATA: + priv->user_data = g_value_get_pointer (value); + break; + case PROP_DESTROY_NOTIFY: + priv->destroy_notify = g_value_get_pointer (value); + break; + case PROP_LENGTH: + priv->length = g_value_get_int (value); + ensure_widgets (NMT_WIDGET_LIST (object)); + break; + case PROP_EMPTY_WIDGET: + priv->empty_widget = g_value_get_object (value); + if (priv->empty_widget) { + g_object_ref_sink (priv->empty_widget); + nmt_newt_grid_add (NMT_NEWT_GRID (object), priv->empty_widget, 0, 0); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_widget_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CREATE_CALLBACK: + g_value_set_pointer (value, priv->create_callback); + break; + case PROP_USER_DATA: + g_value_set_pointer (value, priv->user_data); + break; + case PROP_DESTROY_NOTIFY: + g_value_set_pointer (value, priv->destroy_notify); + break; + case PROP_LENGTH: + g_value_set_int (value, priv->length); + break; + case PROP_EMPTY_WIDGET: + g_value_set_object (value, priv->empty_widget); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_widget_list_class_init (NmtWidgetListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (list_class); + + g_type_class_add_private (list_class, sizeof (NmtWidgetListPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_widget_list_constructed; + object_class->set_property = nmt_widget_list_set_property; + object_class->get_property = nmt_widget_list_get_property; + object_class->finalize = nmt_widget_list_finalize; + + list_class->create_widget = nmt_widget_list_real_create_widget; + + /* signals */ + + /** + * NmtNewtWidget::add-clicked: + * @list: the #NmtNewtWidgetList + * + * Emitted when the user clicks the "Add" button. The caller can + * decide whether or not to add a new widget, and call + * nmt_widget_list_set_length() with the new length if so. + * + * FIXME: the "Add" button should be insensitive if it's + * not going to work. + */ + signals[ADD_CLICKED] = + g_signal_new ("add-clicked", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NmtWidgetListClass, add_clicked), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + /** + * NmtNewtWidget::remove-clicked: + * @list: the #NmtNewtWidgetList + * @n: the widget being removed + * + * Emitted when the user clicks one of the "Remove" buttons. The + * caller can decide whether or not to remove the widget, and + * call nmt_widget_list_set_length() with the new length if so. + * + * FIXME: the "Remove" button should be insensitive if it's not + * going to work. + */ + signals[REMOVE_CLICKED] = + g_signal_new ("remove-clicked", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NmtWidgetListClass, remove_clicked), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_INT); + + /* properties */ + + /** + * NmtWidgetList:create-callback: + * + * Callback called to create a new widget. + */ + g_object_class_install_property + (object_class, PROP_CREATE_CALLBACK, + g_param_spec_pointer ("create-callback", "", "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:user-data: + * + * User data for #NmtWidgetList:create-callback + */ + g_object_class_install_property + (object_class, PROP_USER_DATA, + g_param_spec_pointer ("user-data", "", "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:destroy-notify: + * + * #GDestroyNotify for #NmtWidgetList:user-data + */ + g_object_class_install_property + (object_class, PROP_DESTROY_NOTIFY, + g_param_spec_pointer ("destroy-notify", "", "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:length: + * + * The length of the widget list; changing this value will add or + * remove widgets from the list. + */ + g_object_class_install_property + (object_class, PROP_LENGTH, + g_param_spec_int ("length", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:empty-widget: + * + * If non-%NULL, this widget will be displayed when there are + * no "real" widgets in the list. + */ + g_object_class_install_property + (object_class, PROP_EMPTY_WIDGET, + g_param_spec_object ("empty-widget", "", "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/clients/tui/nmt-widget-list.h b/clients/tui/nmt-widget-list.h new file mode 100644 index 00000000..7dbeb215 --- /dev/null +++ b/clients/tui/nmt-widget-list.h @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_WIDGET_LIST_H +#define NMT_WIDGET_LIST_H + +#include "nmt-newt-grid.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_WIDGET_LIST (nmt_widget_list_get_type ()) +#define NMT_WIDGET_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetList)) +#define NMT_WIDGET_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass)) +#define NMT_IS_WIDGET_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_WIDGET_LIST)) +#define NMT_IS_WIDGET_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_WIDGET_LIST)) +#define NMT_WIDGET_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtWidgetList; + +typedef struct { + NmtNewtGridClass parent; + + /* signals */ + void (*add_clicked) (NmtWidgetList *list); + void (*remove_clicked) (NmtWidgetList *list, + int n); + + /* methods */ + NmtNewtWidget * (*create_widget) (NmtWidgetList *list, + int n); + +} NmtWidgetListClass; + +GType nmt_widget_list_get_type (void); + +typedef NmtNewtWidget * (*NmtWidgetListCallback) (NmtWidgetList *list, + int n, + gpointer user_data); + +NmtNewtWidget *nmt_widget_list_new (NmtWidgetListCallback create_callback, + gpointer user_data, + GDestroyNotify destroy_notify, + NmtNewtWidget *empty_widget); + +int nmt_widget_list_get_length (NmtWidgetList *list); +void nmt_widget_list_set_length (NmtWidgetList *list, + int length); + +G_END_DECLS + +#endif /* NMT_WIDGET_LIST_H */ diff --git a/clients/tui/nmtui-connect.c b/clients/tui/nmtui-connect.c new file mode 100644 index 00000000..438915d2 --- /dev/null +++ b/clients/tui/nmtui-connect.c @@ -0,0 +1,365 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui-connect + * @short_description: nm-applet-like functionality + * + * nmtui-connect implements activating and deactivating #NMConnections, + * including presenting a password dialog if necessary. + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib/gi18n-lib.h> + +#include "nmt-newt.h" + +#include "nmtui.h" +#include "nmtui-connect.h" +#include "nmt-connect-connection-list.h" +#include "nmt-password-dialog.h" +#include "nm-secret-agent-simple.h" +#include "nmt-utils.h" + +static void +secrets_requested (NMSecretAgentSimple *agent, + const char *request_id, + const char *title, + const char *msg, + GPtrArray *secrets, + gpointer user_data) +{ + NmtNewtForm *form; + + form = nmt_password_dialog_new (request_id, title, msg, secrets); + nmt_newt_form_run_sync (form); + + if (nmt_password_dialog_succeeded (NMT_PASSWORD_DIALOG (form))) + nm_secret_agent_simple_response (agent, request_id, secrets); + else + nm_secret_agent_simple_response (agent, request_id, NULL); + + g_object_unref (form); +} + +static void +connect_cancelled (NmtNewtForm *form, + gpointer user_data) +{ + NmtSyncOp *op = user_data; + GError *error = NULL; + + error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled"); + nmt_sync_op_complete_boolean (op, FALSE, error); + g_clear_error (&error); +} + +static void +activate_ac_state_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtSyncOp *op = user_data; + NMActiveConnectionState state; + GError *error = NULL; + + state = nm_active_connection_get_state (NM_ACTIVE_CONNECTION (object)); + if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) + return; + + if (state != NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + error = g_error_new_literal (NM_CLIENT_ERROR, NM_CLIENT_ERROR_FAILED, + _("Activation failed")); + } + + nmt_sync_op_complete_boolean (op, error == NULL, error); + g_clear_error (&error); +} + +static void +activate_callback (GObject *client, + GAsyncResult *result, + gpointer user_data) +{ + NmtSyncOp *op = user_data; + NMActiveConnection *ac; + GError *error = NULL; + + ac = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error); + if (error) + nmt_sync_op_complete_pointer (op, NULL, error); + else + nmt_sync_op_complete_pointer (op, ac, NULL); +} + +static void +add_and_activate_callback (GObject *client, + GAsyncResult *result, + gpointer user_data) +{ + NmtSyncOp *op = user_data; + NMActiveConnection *ac; + GError *error = NULL; + + ac = nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error); + if (error) + nmt_sync_op_complete_pointer (op, NULL, error); + else + nmt_sync_op_complete_pointer (op, ac, NULL); +} + +static void +activate_connection (NMConnection *connection, + NMDevice *device, + NMObject *specific_object) +{ + NmtNewtForm *form; + NMSecretAgentOld *agent; + NmtNewtWidget *label; + NmtSyncOp op; + const char *specific_object_path; + NMActiveConnection *ac; + GError *error = NULL; + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "escape-exits", TRUE, + NULL); + label = nmt_newt_label_new (_("Connecting...")); + nmt_newt_form_set_content (form, label); + + agent = nm_secret_agent_simple_new ("nmtui"); + if (agent) { + if (connection) { + nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (agent), + nm_object_get_path (NM_OBJECT (connection))); + } + g_signal_connect (agent, "request-secrets", G_CALLBACK (secrets_requested), NULL); + } + + specific_object_path = specific_object ? nm_object_get_path (specific_object) : NULL; + + /* There's no way to cancel an nm_client_activate_connection() / + * nm_client_add_and_activate_connection() call, so we always let them + * complete, even if the user hits Esc; they shouldn't normally take long + * to complete anyway. + */ + + nmt_sync_op_init (&op); + if (connection) { + nm_client_activate_connection_async (nm_client, + connection, device, specific_object_path, + NULL, activate_callback, &op); + } else { + nm_client_add_and_activate_connection_async (nm_client, + NULL, device, specific_object_path, + NULL, add_and_activate_callback, &op); + } + + nmt_newt_form_show (form); + + ac = nmt_sync_op_wait_pointer (&op, &error); + if (!ac) { + nmt_newt_message_dialog (_("Could not activate connection: %s"), error->message); + g_clear_error (&error); + goto done; + } else if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + /* Already active */ + goto done; + } else if (!nmt_newt_widget_get_realized (NMT_NEWT_WIDGET (form))) { + /* User already hit Esc */ + goto done; + } + + if (!connection) { + connection = NM_CONNECTION (nm_active_connection_get_connection (ac)); + if (connection) { + nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (agent), + nm_object_get_path (NM_OBJECT (connection))); + } + } + + /* Now wait for the connection to actually reach the ACTIVATED state, + * allowing the user to cancel if it takes too long. + */ + + nmt_sync_op_init (&op); + + g_signal_connect (form, "quit", G_CALLBACK (connect_cancelled), &op); + g_signal_connect (ac, "notify::" NM_ACTIVE_CONNECTION_STATE, + G_CALLBACK (activate_ac_state_changed), &op); + + if (!nmt_sync_op_wait_boolean (&op, &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + nmt_newt_message_dialog (_("Could not activate connection: %s"), error->message); + g_clear_error (&error); + } + + g_signal_handlers_disconnect_by_func (form, G_CALLBACK (connect_cancelled), &op); + g_signal_handlers_disconnect_by_func (ac, G_CALLBACK (activate_ac_state_changed), &op); + + done: + if (nmt_newt_widget_get_realized (NMT_NEWT_WIDGET (form))) + nmt_newt_form_quit (form); + g_object_unref (form); + + nm_secret_agent_old_unregister (agent, NULL, NULL); + g_object_unref (agent); +} + +static void +listbox_activated (NmtNewtListbox *listbox, + gpointer user_data) +{ + NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST (listbox); + NMConnection *connection; + NMDevice *device; + NMObject *specific_object; + NMActiveConnection *ac; + + if (!nmt_connect_connection_list_get_selection (list, + &connection, + &device, + &specific_object, + &ac)) + return; + + if (ac) + nm_client_deactivate_connection (nm_client, ac, NULL, NULL); + else + activate_connection (connection, device, specific_object); +} + +static void +activate_clicked (NmtNewtButton *button, + gpointer listbox) +{ + listbox_activated (listbox, NULL); +} + +static void +listbox_active_changed (GObject *object, + GParamSpec *pspec, + gpointer button) +{ + NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST (object); + static const char *activate, *deactivate; + static int deactivate_padding, activate_padding; + NMActiveConnection *ac; + gboolean has_selection; + + if (G_UNLIKELY (activate == NULL)) { + int activate_width, deactivate_width; + + activate = _("Activate"); + activate_width = nmt_newt_text_width (activate); + deactivate = _("Deactivate"); + deactivate_width = nmt_newt_text_width (deactivate); + + activate_padding = MAX (0, deactivate_width - activate_width); + deactivate_padding = MAX (0, activate_width - deactivate_width); + } + + has_selection = nmt_connect_connection_list_get_selection (list, NULL, NULL, NULL, &ac); + + nmt_newt_component_set_sensitive (button, has_selection); + if (has_selection && ac) { + nmt_newt_button_set_label (button, deactivate); + nmt_newt_widget_set_padding (button, 0, 0, deactivate_padding, 0); + } else { + nmt_newt_button_set_label (button, activate); + nmt_newt_widget_set_padding (button, 0, 0, activate_padding, 0); + } +} + +static NmtNewtForm * +nmt_connect_connection_list (void) +{ + int screen_width, screen_height; + NmtNewtForm *form; + NmtNewtWidget *list, *activate, *quit, *bbox, *grid; + + newtGetScreenSize (&screen_width, &screen_height); + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "y", 2, + "height", screen_height - 4, + "escape-exits", TRUE, + NULL); + + grid = nmt_newt_grid_new (); + + list = nmt_connect_connection_list_new (); + nmt_newt_grid_add (NMT_NEWT_GRID (grid), list, 0, 0); + nmt_newt_grid_set_flags (NMT_NEWT_GRID (grid), list, + NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | + NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_EXPAND_Y); + g_signal_connect (list, "activated", G_CALLBACK (listbox_activated), NULL); + + bbox = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_VERTICAL); + nmt_newt_grid_add (NMT_NEWT_GRID (grid), bbox, 1, 0); + nmt_newt_widget_set_padding (bbox, 1, 1, 0, 1); + + activate = nmt_newt_button_box_add_start (NMT_NEWT_BUTTON_BOX (bbox), _("Activate")); + g_signal_connect (list, "notify::active", G_CALLBACK (listbox_active_changed), activate); + listbox_active_changed (G_OBJECT (list), NULL, activate); + g_signal_connect (activate, "clicked", G_CALLBACK (activate_clicked), list); + + quit = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("Quit")); + nmt_newt_widget_set_exit_on_activate (quit, TRUE); + + nmt_newt_form_set_content (form, grid); + return form; +} + +static NmtNewtForm * +nmt_connect_connection (const char *identifier) +{ + NmtNewtWidget *list; + NMConnection *connection; + NMDevice *device; + NMObject *specific_object; + NMActiveConnection *ac; + + list = nmt_connect_connection_list_new (); + if (!nmt_connect_connection_list_get_connection (NMT_CONNECT_CONNECTION_LIST (list), + identifier, + &connection, + &device, + &specific_object, + &ac)) + nmt_newt_message_dialog (_("No such connection '%s'"), identifier); + else if (ac) + nmt_newt_message_dialog (_("Connection is already active")); + else + activate_connection (connection, device, specific_object); + g_object_unref (list); + + return NULL; +} + +NmtNewtForm * +nmtui_connect (int argc, char **argv) +{ + if (argc == 2) + return nmt_connect_connection (argv[1]); + else + return nmt_connect_connection_list (); +} diff --git a/clients/tui/nmtui-connect.h b/clients/tui/nmtui-connect.h new file mode 100644 index 00000000..8310ac38 --- /dev/null +++ b/clients/tui/nmtui-connect.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_CONNECT_H +#define NMTUI_CONNECT_H + +G_BEGIN_DECLS + +NmtNewtForm *nmtui_connect (int argc, char **argv); + +G_END_DECLS + +#endif /* NMTUI_CONNECT_H */ diff --git a/clients/tui/nmtui-edit.c b/clients/tui/nmtui-edit.c new file mode 100644 index 00000000..0c3206da --- /dev/null +++ b/clients/tui/nmtui-edit.c @@ -0,0 +1,577 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui-edit + * @short_description: nm-connection-editor-like functionality + * + * nmtui-edit implements editing #NMConnections. + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib/gi18n-lib.h> +#include <NetworkManager.h> + +#include "nmtui.h" +#include "nmtui-edit.h" +#include "nmt-edit-connection-list.h" +#include "nmt-editor.h" +#include "nmt-utils.h" + +#include "nm-editor-utils.h" + +static void +list_add_connection (NmtEditConnectionList *list, + gpointer form) +{ + nmt_add_connection (); + nmt_newt_form_set_focus (form, NMT_NEWT_WIDGET (list)); +} + +static void +list_edit_connection (NmtEditConnectionList *list, + NMConnection *connection, + gpointer form) +{ + nmt_edit_connection (connection); + nmt_newt_form_set_focus (form, NMT_NEWT_WIDGET (list)); +} + +static void +list_remove_connection (NmtEditConnectionList *list, + NMRemoteConnection *connection, + gpointer form) +{ + nmt_remove_connection (connection); + nmt_newt_form_set_focus (form, NMT_NEWT_WIDGET (list)); +} + +static gboolean +edit_connection_list_filter (NmtEditConnectionList *list, + NMConnection *connection, + gpointer user_data) +{ + NMSettingConnection *s_con; + const char *master, *slave_type; + const char *uuid, *ifname; + const GPtrArray *conns; + int i; + gboolean found_master = FALSE; + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con != NULL, FALSE); + + master = nm_setting_connection_get_master (s_con); + if (!master) + return TRUE; + slave_type = nm_setting_connection_get_slave_type (s_con); + if ( g_strcmp0 (slave_type, NM_SETTING_BOND_SETTING_NAME) != 0 + && g_strcmp0 (slave_type, NM_SETTING_TEAM_SETTING_NAME) != 0 + && g_strcmp0 (slave_type, NM_SETTING_BRIDGE_SETTING_NAME) != 0) + return TRUE; + + conns = nm_client_get_connections (nm_client); + for (i = 0; i < conns->len; i++) { + NMConnection *candidate = conns->pdata[i]; + + uuid = nm_connection_get_uuid (candidate); + ifname = nm_connection_get_interface_name (candidate); + if (!g_strcmp0 (master, uuid) || !g_strcmp0 (master, ifname)) { + found_master = TRUE; + break; + } + } + + return !found_master; +} + +static NmtNewtForm * +nmt_edit_main_connection_list (void) +{ + int screen_width, screen_height; + NmtNewtForm *form; + NmtNewtWidget *quit, *list; + + newtGetScreenSize (&screen_width, &screen_height); + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "y", 2, + "height", screen_height - 4, + "escape-exits", TRUE, + NULL); + + quit = nmt_newt_button_new (_("Quit")); + nmt_newt_widget_set_exit_on_activate (quit, TRUE); + + list = g_object_new (NMT_TYPE_EDIT_CONNECTION_LIST, + "extra-widget", quit, + "connection-filter", edit_connection_list_filter, + NULL); + + g_signal_connect (list, "add-connection", + G_CALLBACK (list_add_connection), form); + g_signal_connect (list, "edit-connection", + G_CALLBACK (list_edit_connection), form); + g_signal_connect (list, "remove-connection", + G_CALLBACK (list_remove_connection), form); + + nmt_newt_form_set_content (form, list); + return form; +} + +#define NMT_TYPE_ADD_CONNECTION (nmt_add_connection_get_type ()) +#define NMT_ADD_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ADD_CONNECTION, NmtAddConnection)) +#define NMT_IS_ADD_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ADD_CONNECTION)) + +typedef NmtNewtForm NmtAddConnection; +typedef NmtNewtFormClass NmtAddConnectionClass; + +GType nmt_add_connection_get_type (void); + +G_DEFINE_TYPE (NmtAddConnection, nmt_add_connection, NMT_TYPE_NEWT_FORM) + +#define NMT_ADD_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ADD_CONNECTION, NmtAddConnectionPrivate)) + +typedef struct { + NmtNewtTextbox *textbox; + NmtNewtListbox *listbox; + + char *primary_text; + char *secondary_text; + NMConnection *master; + NmtAddConnectionTypeFilter type_filter; + gpointer type_filter_data; + + gboolean single_type; +} NmtAddConnectionPrivate; + +enum { + PROP_0, + + PROP_PRIMARY_TEXT, + PROP_SECONDARY_TEXT, + PROP_MASTER, + PROP_TYPE_FILTER, + PROP_TYPE_FILTER_DATA, + + LAST_PROP +}; + +static void +create_connection (NmtNewtWidget *widget, gpointer list) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (list); + GType type = (GType) GPOINTER_TO_SIZE (nmt_newt_listbox_get_active_key (priv->listbox)); + NMConnection *connection; + + connection = nm_editor_utils_create_connection (type, priv->master, nm_client); + nmt_edit_connection (connection); + g_object_unref (connection); + + nmt_newt_form_quit (list); +} + +static void +nmt_add_connection_init (NmtAddConnection *form) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (form); + NmtNewtWidget *textbox, *listbox, *button; + NmtNewtGrid *grid, *buttons; + + grid = NMT_NEWT_GRID (nmt_newt_grid_new ()); + + textbox = nmt_newt_textbox_new (0, 60); + priv->textbox = NMT_NEWT_TEXTBOX (textbox); + nmt_newt_grid_add (grid, textbox, 0, 0); + + listbox = nmt_newt_listbox_new (5, NMT_NEWT_LISTBOX_SCROLL); + priv->listbox = NMT_NEWT_LISTBOX (listbox); + g_signal_connect (priv->listbox, "activated", G_CALLBACK (create_connection), form); + nmt_newt_grid_add (grid, listbox, 0, 1); + nmt_newt_widget_set_padding (listbox, 0, 1, 0, 0); + nmt_newt_grid_set_flags (grid, listbox, NMT_NEWT_GRID_EXPAND_X); + + // FIXME: VPN description textbox + + buttons = NMT_NEWT_GRID (nmt_newt_grid_new ()); + nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (buttons), 0, 2); + nmt_newt_widget_set_padding (NMT_NEWT_WIDGET (buttons), 0, 1, 0, 0); + + button = g_object_ref_sink (nmt_newt_button_new (_("Cancel"))); + nmt_newt_widget_set_exit_on_activate (button, TRUE); + nmt_newt_grid_add (NMT_NEWT_GRID (buttons), button, 0, 0); + nmt_newt_widget_set_padding (button, 0, 0, 1, 0); + nmt_newt_grid_set_flags (NMT_NEWT_GRID (buttons), button, + NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_ANCHOR_RIGHT | + NMT_NEWT_GRID_FILL_Y); + + button = g_object_ref_sink (nmt_newt_button_new (_("Create"))); + g_signal_connect (button, "clicked", G_CALLBACK (create_connection), form); + nmt_newt_grid_add (NMT_NEWT_GRID (buttons), button, 1, 0); + + nmt_newt_form_set_content (NMT_NEWT_FORM (form), NMT_NEWT_WIDGET (grid)); +} + +static void +nmt_add_connection_constructed (GObject *object) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object); + NMEditorConnectionTypeData **types; + char *text; + int i, num_types; + + if (priv->secondary_text) { + text = g_strdup_printf ("%s\n\n%s", + priv->primary_text, + priv->secondary_text); + } else + text = g_strdup (priv->primary_text); + nmt_newt_textbox_set_text (priv->textbox, text); + g_free (text); + + types = nm_editor_utils_get_connection_type_list (); + for (i = num_types = 0; types[i]; i++) { + if (priv->type_filter && !priv->type_filter (types[i]->setting_type, priv->type_filter_data)) + continue; + nmt_newt_listbox_append (priv->listbox, types[i]->name, + GSIZE_TO_POINTER (types[i]->setting_type)); + num_types++; + } + + if (num_types == 1) + priv->single_type = TRUE; + + G_OBJECT_CLASS (nmt_add_connection_parent_class)->constructed (object); +} + +static void +nmt_add_connection_show (NmtNewtForm *form) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (form); + + if (priv->single_type) { + nmt_newt_listbox_set_active (priv->listbox, 0); + create_connection (NMT_NEWT_WIDGET (priv->listbox), g_object_ref (form)); + } else + NMT_NEWT_FORM_CLASS (nmt_add_connection_parent_class)->show (form); +} + +static void +nmt_add_connection_finalize (GObject *object) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object); + + g_free (priv->primary_text); + g_free (priv->secondary_text); + g_clear_object (&priv->master); + + G_OBJECT_CLASS (nmt_add_connection_parent_class)->finalize (object); +} + +static void +nmt_add_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PRIMARY_TEXT: + priv->primary_text = g_value_dup_string (value); + break; + case PROP_SECONDARY_TEXT: + priv->secondary_text = g_value_dup_string (value); + break; + case PROP_MASTER: + priv->master = g_value_dup_object (value); + break; + case PROP_TYPE_FILTER: + priv->type_filter = g_value_get_pointer (value); + break; + case PROP_TYPE_FILTER_DATA: + priv->type_filter_data = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_add_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PRIMARY_TEXT: + g_value_set_string (value, priv->primary_text); + break; + case PROP_SECONDARY_TEXT: + g_value_set_string (value, priv->secondary_text); + break; + case PROP_MASTER: + g_value_set_object (value, priv->master); + break; + case PROP_TYPE_FILTER: + g_value_set_pointer (value, priv->type_filter); + break; + case PROP_TYPE_FILTER_DATA: + g_value_set_pointer (value, priv->type_filter_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_add_connection_class_init (NmtAddConnectionClass *add_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (add_class); + NmtNewtFormClass *form_class = NMT_NEWT_FORM_CLASS (add_class); + + g_type_class_add_private (add_class, sizeof (NmtAddConnectionPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_add_connection_constructed; + object_class->set_property = nmt_add_connection_set_property; + object_class->get_property = nmt_add_connection_get_property; + object_class->finalize = nmt_add_connection_finalize; + + form_class->show = nmt_add_connection_show; + + g_object_class_install_property + (object_class, PROP_PRIMARY_TEXT, + g_param_spec_string ("primary-text", "", "", + _("Select the type of connection you wish to create."), + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_SECONDARY_TEXT, + g_param_spec_string ("secondary-text", "", "", +#if 0 + _("If you are creating a VPN, and the VPN connection you " + "wish to create does not appear in the list, you may " + "not have the correct VPN plugin installed."), +#else + NULL, +#endif + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_MASTER, + g_param_spec_object ("master", "", "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_TYPE_FILTER, + g_param_spec_pointer ("type-filter", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_TYPE_FILTER_DATA, + g_param_spec_pointer ("type-filter-data", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +void +nmt_add_connection (void) +{ + NmtNewtForm *form; + + form = g_object_new (NMT_TYPE_ADD_CONNECTION, + "title", _("New Connection"), + NULL); + nmt_newt_form_show (form); + g_object_unref (form); +} + +void +nmt_add_connection_full (const char *primary_text, + const char *secondary_text, + NMConnection *master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data) +{ + NmtNewtForm *form; + + form = g_object_new (NMT_TYPE_ADD_CONNECTION, + "title", _("New Connection"), + "primary-text", primary_text, + "secondary-text", secondary_text, + "master", master, + "type-filter", type_filter, + "type-filter-data", type_filter_data, + NULL); + nmt_newt_form_show (form); + g_object_unref (form); +} + +void +nmt_edit_connection (NMConnection *connection) +{ + NmtNewtForm *editor; + + editor = nmt_editor_new (connection); + if (!editor) + return; + + nmt_newt_form_show (editor); + g_object_unref (editor); +} + +typedef struct { + NmtSyncOp op; + gboolean got_callback, got_signal; + NMRemoteConnection *connection; +} ConnectionDeleteData; + +static void +connection_deleted_callback (GObject *connection, + GAsyncResult *result, + gpointer user_data) +{ + ConnectionDeleteData *data = user_data; + GError *error = NULL; + + if (!nm_remote_connection_delete_finish (data->connection, result, &error)) { + nmt_newt_message_dialog (_("Unable to delete connection: %s"), + error->message); + } else + data->got_callback = TRUE; + + if (error || (data->got_callback && data->got_signal)) + nmt_sync_op_complete_boolean (&data->op, error == NULL, error); + g_clear_error (&error); +} + +static void +connection_removed_signal (NMClient *client, + NMRemoteConnection *connection, + gpointer user_data) +{ + ConnectionDeleteData *data = user_data; + + if (connection == data->connection) { + data->got_signal = TRUE; + if (data->got_callback && data->got_signal) + nmt_sync_op_complete_boolean (&data->op, TRUE, NULL); + } +} + +static void +remove_one_connection (NMRemoteConnection *connection) +{ + ConnectionDeleteData data; + GError *error = NULL; + + data.got_callback = data.got_signal = FALSE; + nmt_sync_op_init (&data.op); + + data.connection = connection; + g_signal_connect (nm_client, NM_CLIENT_CONNECTION_REMOVED, + G_CALLBACK (connection_removed_signal), &data); + nm_remote_connection_delete_async (connection, NULL, connection_deleted_callback, &data); + + if (!nmt_sync_op_wait_boolean (&data.op, &error)) { + nmt_newt_message_dialog (_("Could not delete connection '%s': %s"), + nm_connection_get_id (NM_CONNECTION (connection)), + error->message); + g_error_free (error); + } + + g_signal_handlers_disconnect_by_func (nm_client, G_CALLBACK (connection_removed_signal), &data); +} + +void +nmt_remove_connection (NMRemoteConnection *connection) +{ + const GPtrArray *all_conns; + GSList *slaves, *iter; + int i; + NMRemoteConnection *slave; + NMSettingConnection *s_con; + const char *uuid, *iface, *master; + int choice; + + choice = nmt_newt_choice_dialog (_("Cancel"), + _("Delete"), + _("Are you sure you want to delete the connection '%s'?"), + nm_connection_get_id (NM_CONNECTION (connection))); + if (choice == 1) + return; + + g_object_ref (connection); + remove_one_connection (connection); + + uuid = nm_connection_get_uuid (NM_CONNECTION (connection)); + iface = nm_connection_get_interface_name (NM_CONNECTION (connection)); + + all_conns = nm_client_get_connections (nm_client); + slaves = NULL; + for (i = 0; i < all_conns->len; i++) { + slave = all_conns->pdata[i]; + s_con = nm_connection_get_setting_connection (NM_CONNECTION (slave)); + master = nm_setting_connection_get_master (s_con); + if (master) { + if (!g_strcmp0 (master, uuid) || !g_strcmp0 (master, iface)) + slaves = g_slist_prepend (slaves, g_object_ref (slave)); + } + } + + for (iter = slaves; iter; iter = iter->next) + remove_one_connection (iter->data); + g_slist_free_full (slaves, g_object_unref); + + g_object_unref (connection); +} + +NmtNewtForm * +nmtui_edit (int argc, char **argv) +{ + NMConnection *conn = NULL; + + if (argc == 2) { + if (nm_utils_is_uuid (argv[1])) + conn = NM_CONNECTION (nm_client_get_connection_by_uuid (nm_client, argv[1])); + if (!conn) + conn = NM_CONNECTION (nm_client_get_connection_by_id (nm_client, argv[1])); + + if (!conn) { + nmt_newt_message_dialog ("%s: no such connection '%s'\n", argv[0], argv[1]); + return NULL; + } + + return nmt_editor_new (conn); + } else + return nmt_edit_main_connection_list (); +} diff --git a/clients/tui/nmtui-edit.h b/clients/tui/nmtui-edit.h new file mode 100644 index 00000000..dae91cfa --- /dev/null +++ b/clients/tui/nmtui-edit.h @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_EDIT_H +#define NMTUI_EDIT_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +typedef gboolean (*NmtAddConnectionTypeFilter) (GType connection_type, + gpointer user_data); + +NmtNewtForm *nmtui_edit (int argc, char **argv); + +void nmt_add_connection (void); +void nmt_add_connection_full (const char *primary_text, + const char *secondary_text, + NMConnection *master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data); + +void nmt_edit_connection (NMConnection *connection); + +void nmt_remove_connection (NMRemoteConnection *connection); + +G_END_DECLS + +#endif /* NMTUI_EDIT_H */ diff --git a/clients/tui/nmtui-hostname.c b/clients/tui/nmtui-hostname.c new file mode 100644 index 00000000..23de95b8 --- /dev/null +++ b/clients/tui/nmtui-hostname.c @@ -0,0 +1,128 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui-hostname + * @short_description: hostname-setting functionality + * + * nmtui-hostname implements the "set hostname" functionality + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-newt.h" + +#include "nmtui.h" +#include "nmtui-hostname.h" +#include "nmt-utils.h" + +static char * +nmtui_hostname_run_dialog (void) +{ + NmtNewtForm *form; + NmtNewtWidget *widget, *ok, *cancel; + NmtNewtGrid *grid; + NmtNewtEntry *entry; + NmtNewtButtonBox *bbox; + char *hostname, *ret = NULL; + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "title", _("Set Hostname"), + "escape-exits", TRUE, + NULL); + + widget = nmt_newt_grid_new (); + nmt_newt_form_set_content (form, widget); + grid = NMT_NEWT_GRID (widget); + + widget = nmt_newt_label_new (_("Hostname")); + nmt_newt_grid_add (grid, widget, 0, 0); + + widget = nmt_newt_entry_new (40, 0); + nmt_newt_widget_set_exit_on_activate (widget, TRUE); + nmt_newt_grid_add (grid, widget, 1, 0); + nmt_newt_widget_set_padding (widget, 1, 0, 0, 0); + entry = NMT_NEWT_ENTRY (widget); + + widget = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add (grid, widget, 1, 1); + nmt_newt_widget_set_padding (widget, 0, 1, 0, 0); + bbox = NMT_NEWT_BUTTON_BOX (widget); + + cancel = nmt_newt_button_box_add_end (bbox, _("Cancel")); + nmt_newt_widget_set_exit_on_activate (cancel, TRUE); + ok = nmt_newt_button_box_add_end (bbox, _("OK")); + nmt_newt_widget_set_exit_on_activate (ok, TRUE); + + g_object_get (G_OBJECT (nm_client), + NM_CLIENT_HOSTNAME, &hostname, + NULL); + nmt_newt_entry_set_text (entry, hostname); + g_free (hostname); + + widget = nmt_newt_form_run_sync (form); + if (widget == (NmtNewtWidget *)entry || widget == ok) + ret = g_strdup (nmt_newt_entry_get_text (entry)); + + g_object_unref (form); + return ret; +} + +static void +hostname_set (GObject *object, + GAsyncResult *result, + gpointer op) +{ + GError *error = NULL; + + nm_client_save_hostname_finish (NM_CLIENT (object), result, &error); + nmt_sync_op_complete_boolean (op, error == NULL, error); + g_clear_error (&error); +} + +NmtNewtForm * +nmtui_hostname (int argc, char **argv) +{ + const char *hostname; + char *tmp = NULL; + NmtSyncOp op; + GError *error = NULL; + + if (argc == 2) + hostname = argv[1]; + else + hostname = tmp = nmtui_hostname_run_dialog (); + + if (hostname) { + nmt_sync_op_init (&op); + nm_client_save_hostname_async (nm_client, hostname, NULL, hostname_set, &op); + if (nmt_sync_op_wait_boolean (&op, &error)) { + /* Translators: this indicates the result. ie, "I have set the hostname to ..." */ + nmt_newt_message_dialog (_("Set hostname to '%s'"), hostname); + } else { + nmt_newt_message_dialog (_("Unable to set hostname: %s"), error->message); + g_error_free (error); + } + + g_free (tmp); + } + + return NULL; +} diff --git a/clients/tui/nmtui-hostname.h b/clients/tui/nmtui-hostname.h new file mode 100644 index 00000000..a14bc699 --- /dev/null +++ b/clients/tui/nmtui-hostname.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_HOSTNAME_H +#define NMTUI_HOSTNAME_H + +G_BEGIN_DECLS + +NmtNewtForm *nmtui_hostname (int argc, char **argv); + +G_END_DECLS + +#endif /* NMTUI_HOSTNAME_H */ diff --git a/clients/tui/nmtui.c b/clients/tui/nmtui.c new file mode 100644 index 00000000..0fac98eb --- /dev/null +++ b/clients/tui/nmtui.c @@ -0,0 +1,293 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui + * @short_description: nmtui toplevel + * + * The top level of nmtui. Exists mostly just to call nmtui_connect(), + * nmtui_edit(), and nmtui_hostname(). + */ + +#include "config.h" + +#include <locale.h> +#include <stdlib.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include <NetworkManager.h> + +#include "nmt-newt.h" +#include "nm-editor-bindings.h" + +#include "nmtui.h" +#include "nmtui-edit.h" +#include "nmtui-connect.h" +#include "nmtui-hostname.h" + +NMClient *nm_client; +static GMainLoop *loop; + +typedef NmtNewtForm * (*NmtuiSubprogram) (int argc, char **argv); + +static const struct { + const char *name, *shortcut, *arg; + const char *display_name; + NmtuiSubprogram func; +} subprograms[] = { + { "edit", "nmtui-edit", N_("connection"), + N_("Edit a connection"), + nmtui_edit }, + { "connect", "nmtui-connect", N_("connection"), + N_("Activate a connection"), + nmtui_connect }, + { "hostname", "nmtui-hostname", N_("new hostname"), + N_("Set system hostname"), + nmtui_hostname } +}; +static const int num_subprograms = G_N_ELEMENTS (subprograms); + +static void +quit_func (int argc, char **argv) +{ + nmtui_quit (); +} + +static NmtNewtForm * +nmtui_main (int argc, char **argv) +{ + NmtNewtForm *form; + NmtNewtWidget *widget, *ok; + NmtNewtGrid *grid; + NmtNewtListbox *listbox; + NmtNewtButtonBox *bbox; + NmtuiSubprogram subprogram = NULL; + int i; + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "title", _("NetworkManager TUI"), + "escape-exits", TRUE, + NULL); + + widget = nmt_newt_grid_new (); + nmt_newt_form_set_content (form, widget); + grid = NMT_NEWT_GRID (widget); + + widget = nmt_newt_label_new (_("Please select an option")); + nmt_newt_grid_add (grid, widget, 0, 0); + + widget = g_object_new (NMT_TYPE_NEWT_LISTBOX, + "height", num_subprograms + 2, + "skip-null-keys", TRUE, + NULL); + nmt_newt_grid_add (grid, widget, 0, 1); + nmt_newt_widget_set_padding (widget, 0, 1, 0, 1); + nmt_newt_widget_set_exit_on_activate (widget, TRUE); + listbox = NMT_NEWT_LISTBOX (widget); + + for (i = 0; i < num_subprograms; i++) { + nmt_newt_listbox_append (listbox, _(subprograms[i].display_name), + subprograms[i].func); + } + nmt_newt_listbox_append (listbox, "", NULL); + nmt_newt_listbox_append (listbox, _("Quit"), quit_func); + + widget = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add (grid, widget, 0, 2); + bbox = NMT_NEWT_BUTTON_BOX (widget); + + ok = nmt_newt_button_box_add_end (bbox, _("OK")); + nmt_newt_widget_set_exit_on_activate (ok, TRUE); + + widget = nmt_newt_form_run_sync (form); + if (widget) + subprogram = nmt_newt_listbox_get_active_key (listbox); + g_object_unref (form); + + if (subprogram) + return subprogram (argc, argv); + else + return NULL; +} + +/** + * nmtui_quit: + * + * Causes nmtui to exit. + */ +void +nmtui_quit (void) +{ + g_main_loop_quit (loop); +} + +static void +usage (void) +{ + const char *argv0 = g_get_prgname (); + const char *usage_str = _("Usage"); + int i; + + for (i = 0; i < num_subprograms; i++) { + if (!strcmp (argv0, subprograms[i].shortcut)) { + g_printerr ("%s: %s [%s]\n", usage_str, argv0, _(subprograms[i].arg)); + exit (1); + } + } + + g_printerr ("%s: nmtui\n", usage_str); + for (i = 0; i < num_subprograms; i++) { + g_printerr ("%*s nmtui %s [%s]\n", + nmt_newt_text_width (usage_str), " ", + subprograms[i].name, + _(subprograms[i].arg)); + } + exit (1); +} + +typedef struct { + NmtuiSubprogram subprogram; + int argc; + char **argv; +} NmtuiStartupData; + +static void +toplevel_form_quit (NmtNewtForm *form, + gpointer user_data) +{ + nmtui_quit (); +} + +static gboolean +idle_run_subprogram (gpointer user_data) +{ + NmtuiStartupData *data = user_data; + NmtNewtForm *form; + + form = data->subprogram (data->argc, data->argv); + if (form) { + g_signal_connect (form, "quit", G_CALLBACK (toplevel_form_quit), NULL); + nmt_newt_form_show (form); + g_object_unref (form); + } else + nmtui_quit (); + + return FALSE; +} + +gboolean sleep_on_startup = FALSE; +gboolean noinit = FALSE; + +GOptionEntry entries[] = { + { "sleep", 's', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &sleep_on_startup, + "Sleep on startup", NULL }, + { "noinit", 'n', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &noinit, + "Don't initialize newt", NULL }, + { NULL } +}; + +int +main (int argc, char **argv) +{ + GOptionContext *opts; + GError *error = NULL; + NmtuiStartupData startup_data; + const char *prgname; + int i; + + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + opts = g_option_context_new (NULL); + g_option_context_add_main_entries (opts, entries, NULL); + + if (!g_option_context_parse (opts, &argc, &argv, &error)) { + g_printerr ("%s: %s: %s\n", + argv[0], + _("Could not parse arguments"), + error->message); + exit (1); + } + g_option_context_free (opts); + + nm_editor_bindings_init (); + + nm_client = nm_client_new (NULL, &error); + if (!nm_client) { + g_printerr (_("Could not contact NetworkManager: %s.\n"), error->message); + g_error_free (error); + exit (1); + } + if (!nm_client_get_nm_running (nm_client)) { + g_printerr ("%s\n", _("NetworkManager is not running.")); + exit (1); + } + + if (sleep_on_startup) + sleep (5); + + startup_data.subprogram = NULL; + prgname = g_get_prgname (); + if (g_str_has_prefix (prgname, "lt-")) + prgname += 3; + if (!strcmp (prgname, "nmtui")) { + if (argc > 1) { + for (i = 0; i < num_subprograms; i++) { + if (!strcmp (argv[1], subprograms[i].name)) { + argc--; + argv[0] = (char *) subprograms[i].shortcut; + memmove (&argv[1], &argv[2], argc * sizeof (char *)); + startup_data.subprogram = subprograms[i].func; + break; + } + } + } else + startup_data.subprogram = nmtui_main; + } else { + for (i = 0; i < num_subprograms; i++) { + if (!strcmp (prgname, subprograms[i].shortcut)) { + startup_data.subprogram = subprograms[i].func; + break; + } + } + } + if (!startup_data.subprogram) + usage (); + + if (!noinit) + nmt_newt_init (); + + startup_data.argc = argc; + startup_data.argv = argv; + g_idle_add (idle_run_subprogram, &startup_data); + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + if (!noinit) + nmt_newt_finished (); + + g_object_unref (nm_client); + + return 0; +} diff --git a/clients/tui/nmtui.h b/clients/tui/nmtui.h new file mode 100644 index 00000000..aada9719 --- /dev/null +++ b/clients/tui/nmtui.h @@ -0,0 +1,32 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_H +#define NMTUI_H + +#include <NetworkManager.h> + +G_BEGIN_DECLS + +extern NMClient *nm_client; + +void nmtui_quit (void); + +G_END_DECLS + +#endif /* NMTUI_H */ |