1    	/* nmcli - command-line tool to control NetworkManager
2    	 *
3    	 * This program is free software; you can redistribute it and/or modify
4    	 * it under the terms of the GNU General Public License as published by
5    	 * the Free Software Foundation; either version 2 of the License, or
6    	 * (at your option) any later version.
7    	 *
8    	 * This program is distributed in the hope that it will be useful,
9    	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   	 * GNU General Public License for more details.
12   	 *
13   	 * You should have received a copy of the GNU General Public License along
14   	 * with this program; if not, write to the Free Software Foundation, Inc.,
15   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16   	 *
17   	 * (C) Copyright 2010 - 2013 Red Hat, Inc.
18   	 */
19   	
20   	#include "config.h"
21   	
22   	#include <glib.h>
23   	#include <glib/gi18n.h>
24   	#include <dbus/dbus.h>
25   	#include <dbus/dbus-glib.h>
26   	#include <stdio.h>
27   	#include <string.h>
28   	#include <stdlib.h>
29   	#include <unistd.h>
30   	#include <errno.h>
31   	#include <signal.h>
32   	#include <netinet/ether.h>
33   	
34   	#include <nm-client.h>
35   	#include <nm-device-ethernet.h>
36   	#include <nm-device-adsl.h>
37   	#include <nm-device-wifi.h>
38   	#if WITH_WIMAX
39   	#include <nm-device-wimax.h>
40   	#endif
41   	#include <nm-device-modem.h>
42   	#include <nm-device-bt.h>
43   	#include <nm-device-olpc-mesh.h>
44   	#include <nm-device-infiniband.h>
45   	#include <nm-device-bond.h>
46   	#include <nm-device-team.h>
47   	#include <nm-device-bridge.h>
48   	#include <nm-device-vlan.h>
49   	#include <nm-remote-settings.h>
50   	#include <nm-vpn-connection.h>
51   	#include <nm-utils.h>
52   	
53   	#include "utils.h"
54   	#include "common.h"
55   	#include "settings.h"
56   	#include "connections.h"
57   	
58   	/* Activation timeout waiting for bond/team/bridge slaves (in seconds) */
59   	#define SLAVES_UP_TIMEOUT 10
60   	
61   	/* define some prompts for connection editor */
62   	#define EDITOR_PROMPT_SETTING  _("Setting name? ")
63   	#define EDITOR_PROMPT_PROPERTY _("Property name? ")
64   	#define EDITOR_PROMPT_CON_TYPE _("Enter connection type: ")
65   	
66   	/* Available fields for 'connection show configured' */
67   	static NmcOutputField nmc_fields_con_show[] = {
68   		{"NAME",            N_("NAME"),           25},  /* 0 */
69   		{"UUID",            N_("UUID"),           38},  /* 1 */
70   		{"TYPE",            N_("TYPE"),           17},  /* 2 */
71   		{"TIMESTAMP",       N_("TIMESTAMP"),      12},  /* 3 */
72   		{"TIMESTAMP-REAL",  N_("TIMESTAMP-REAL"), 34},  /* 4 */
73   		{"AUTOCONNECT",     N_("AUTOCONNECT"),    13},  /* 5 */
74   		{"READONLY",        N_("READONLY"),       10},  /* 6 */
75   		{"DBUS-PATH",       N_("DBUS-PATH"),      42},  /* 7 */
76   		{NULL,              NULL,                  0}
77   	};
78   	#define NMC_FIELDS_CON_SHOW_ALL     "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,READONLY,DBUS-PATH"
79   	#define NMC_FIELDS_CON_SHOW_COMMON  "NAME,UUID,TYPE,TIMESTAMP-REAL"
80   	
81   	/* Helper macro to define fields */
82   	#define SETTING_FIELD(setting, width) { setting, N_(setting), width, NULL, FALSE, FALSE, 0 }
83   	
84   	/* Available settings for 'connection show configured <con>' */
85   	static NmcOutputField nmc_fields_settings_names[] = {
86   		SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME, 0),            /* 0 */
87   		SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME, 0),                 /* 1 */
88   		SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME, 0),                /* 2 */
89   		SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME, 0),              /* 3 */
90   		SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, 0),     /* 4 */
91   		SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME, 0),            /* 5 */
92   		SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME, 0),            /* 6 */
93   		SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME, 0),                /* 7 */
94   		SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME, 0),                   /* 8 */
95   		SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME, 0),                 /* 9 */
96   		SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME, 0),                   /* 10 */
97   		SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME, 0),                  /* 11 */
98   		SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME, 0),             /* 12 */
99   		SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME, 0),             /* 13 */
100  		SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME, 0),                   /* 14 */
101  		SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME, 0),                 /* 15 */
102  		SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME, 0),            /* 16 */
103  		SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME, 0),                  /* 17 */
104  		SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME, 0),                  /* 18 */
105  		SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME, 0),                  /* 19 */
106  		SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME, 0),                /* 20 */
107  		SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME, 0),           /* 21 */
108  		SETTING_FIELD (NM_SETTING_TEAM_SETTING_NAME, 0),                  /* 22 */
109  		SETTING_FIELD (NM_SETTING_TEAM_PORT_SETTING_NAME, 0),             /* 23 */
110  		{NULL, NULL, 0, NULL, FALSE, FALSE, 0}
111  	};
112  	#define NMC_FIELDS_SETTINGS_NAMES_ALL_X  NM_SETTING_CONNECTION_SETTING_NAME","\
113  	                                         NM_SETTING_WIRED_SETTING_NAME","\
114  	                                         NM_SETTING_802_1X_SETTING_NAME","\
115  	                                         NM_SETTING_WIRELESS_SETTING_NAME","\
116  	                                         NM_SETTING_WIRELESS_SECURITY_SETTING_NAME","\
117  	                                         NM_SETTING_IP4_CONFIG_SETTING_NAME","\
118  	                                         NM_SETTING_IP6_CONFIG_SETTING_NAME","\
119  	                                         NM_SETTING_SERIAL_SETTING_NAME","\
120  	                                         NM_SETTING_PPP_SETTING_NAME","\
121  	                                         NM_SETTING_PPPOE_SETTING_NAME","\
122  	                                         NM_SETTING_ADSL_SETTING_NAME","\
123  	                                         NM_SETTING_GSM_SETTING_NAME","\
124  	                                         NM_SETTING_CDMA_SETTING_NAME","\
125  	                                         NM_SETTING_BLUETOOTH_SETTING_NAME","\
126  	                                         NM_SETTING_OLPC_MESH_SETTING_NAME","\
127  	                                         NM_SETTING_VPN_SETTING_NAME","\
128  	                                         NM_SETTING_INFINIBAND_SETTING_NAME","\
129  	                                         NM_SETTING_BOND_SETTING_NAME","\
130  	                                         NM_SETTING_VLAN_SETTING_NAME","\
131  	                                         NM_SETTING_BRIDGE_SETTING_NAME","\
132  	                                         NM_SETTING_BRIDGE_PORT_SETTING_NAME","\
133  	                                         NM_SETTING_TEAM_SETTING_NAME","\
134  	                                         NM_SETTING_TEAM_PORT_SETTING_NAME
135  	#if WITH_WIMAX
136  	#define NMC_FIELDS_SETTINGS_NAMES_ALL    NMC_FIELDS_SETTINGS_NAMES_ALL_X","\
137  	                                         NM_SETTING_WIMAX_SETTING_NAME
138  	#else
139  	#define NMC_FIELDS_SETTINGS_NAMES_ALL    NMC_FIELDS_SETTINGS_NAMES_ALL_X
140  	#endif
141  	
142  	
143  	/* Available fields for 'connection show active' */
144  	static NmcOutputField nmc_fields_con_show_active[] = {
145  		{"GROUP",         N_("GROUP"),         9},  /* 0 */  /* used only for 'GENERAL' group listing */
146  		{"NAME",          N_("NAME"),         25},  /* 1 */
147  		{"UUID",          N_("UUID"),         38},  /* 2 */
148  		{"DEVICES",       N_("DEVICES"),      10},  /* 3 */
149  		{"STATE",         N_("STATE"),        12},  /* 4 */
150  		{"DEFAULT",       N_("DEFAULT"),       8},  /* 5 */
151  		{"DEFAULT6",      N_("DEFAULT6"),      9},  /* 6 */
152  		{"SPEC-OBJECT",   N_("SPEC-OBJECT"),  10},  /* 7 */
153  		{"VPN",           N_("VPN"),           5},  /* 8 */
154  		{"DBUS-PATH",     N_("DBUS-PATH"),    51},  /* 9 */
155  		{"CON-PATH",      N_("CON-PATH"),     44},  /* 10 */
156  		{"ZONE",          N_("ZONE"),         15},  /* 11 */
157  		{"MASTER-PATH",   N_("MASTER-PATH"),  44},  /* 12 */
158  		{NULL,            NULL,                0}
159  	};
160  	#define NMC_FIELDS_CON_ACTIVE_ALL     "NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH"
161  	#define NMC_FIELDS_CON_ACTIVE_COMMON  "NAME,UUID,DEVICES,DEFAULT,VPN,MASTER-PATH"
162  	
163  	/* Available fields for 'connection show active <con>' */
164  	static NmcOutputField nmc_fields_con_active_details_groups[] = {
165  		{"GENERAL",  N_("GENERAL"), 9},  /* 0 */
166  		{"IP",       N_("IP"),      5},  /* 1 */
167  		{"VPN",      N_("VPN"),     5},  /* 2 */
168  		{NULL, NULL, 0}
169  	};
170  	#define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL  "GENERAL,IP,VPN"
171  	
172  	/* GENERAL group is the same as nmc_fields_con_show_active */
173  	#define NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL  "GROUP,"NMC_FIELDS_CON_ACTIVE_ALL
174  	
175  	/* IP group is handled by common.c */
176  	
177  	/* Available fields for VPN group */
178  	static NmcOutputField nmc_fields_con_active_details_vpn[] = {
179  		{"GROUP",     N_("GROUP"),       9},  /* 0 */
180  		{"TYPE",      N_("TYPE"),       15},  /* 1 */
181  		{"USERNAME",  N_("USERNAME"),   15},  /* 2 */
182  		{"GATEWAY",   N_("GATEWAY"),    25},  /* 3 */
183  		{"BANNER",    N_("BANNER"),    120},  /* 4 */
184  		{"VPN-STATE", N_("VPN-STATE"),  40},  /* 5 */
185  		{"CFG",       N_("CFG"),       120},  /* 6 */
186  		{NULL, NULL, 0}
187  	};
188  	#define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL  "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG"
189  	
190  	
191  	typedef struct {
192  		NmCli *nmc;
193  		int argc;
194  		char **argv;
195  	} ArgsInfo;
196  	
197  	/* glib main loop variable - defined in nmcli.c */
198  	extern GMainLoop *loop;
199  	
200  	static ArgsInfo args_info;
201  	static guint progress_id = 0;  /* ID of event source for displaying progress */
202  	
203  	/* for readline TAB completion */
204  	static const char *nmc_completion_con_type = NULL;
205  	static NMSetting *nmc_completion_setting = NULL;
206  	
207  	static void
208  	usage (void)
209  	{
210  		fprintf (stderr,
211  		         _("Usage: nmcli connection { COMMAND | help }\n"
212  		         "  COMMAND := { show | up | down | delete }\n\n"
213  		         "  show configured [[ id | uuid | path ] <ID>]\n\n"
214  		         "  show active     [[ id | uuid | path | apath ] <ID>]\n\n"
215  	#if WITH_WIMAX
216  		         "  up [ id | uuid | path ] <ID> [ifname <ifname>] [ap <BSSID>] [nsp <name>]\n\n"
217  	#else
218  		         "  up [ id | uuid | path ] <ID> [ifname <ifname>] [ap <BSSID>]\n\n"
219  	#endif
220  		         "  down [ id | uuid | path | apath ] <ID>\n\n"
221  		         "  add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n"
222  		         "  modify [ id | uuid | path ] <ID> <setting>.<property> <value>\n\n"
223  		         "  edit [ id | uuid | path ] <ID>  |  [type <new_con_type>] [con-name <new_con_name>]\n\n"
224  		         "  delete [ id | uuid | path ] <ID>\n\n"
225  		         "  reload\n\n\n"
226  		         ));
227  	}
228  	
229  	static void
230  	usage_connection_add (void)
231  	{
232  		fprintf (stderr,
233  		         _("Usage: nmcli connection add { OPTIONS | help }\n"
234  		         "  OPTIONS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n"
235  		         "  COMMON_OPTIONS:\n"
236  		         "                  type <type>\n"
237  		         "                  ifname <interface name> | \"*\"\n"
238  		         "                  [con-name <connection name>]\n"
239  		         "                  [autoconnect yes|no]\n\n"
240  		         "  TYPE_SPECIFIC_OPTIONS:\n"
241  		         "    ethernet:     [mac <MAC address>]\n"
242  		         "                  [cloned-mac <cloned MAC address>]\n"
243  		         "                  [mtu <MTU>]\n\n"
244  		         "    wifi:         ssid <SSID>\n"
245  		         "                  [mac <MAC address>]\n"
246  		         "                  [cloned-mac <cloned MAC address>]\n"
247  		         "                  [mtu <MTU>]\n\n"
248  		         "    wimax:        [mac <MAC address>]\n"
249  		         "                  [nsp <NSP>]\n\n"
250  		         "    gsm:          apn <APN>\n"
251  		         "                  [user <username>]\n"
252  		         "                  [password <password>]\n\n"
253  		         "    cdma:         [user <username>]\n"
254  		         "                  [password <password>]\n\n"
255  		         "    infiniband:   [mac <MAC address>]\n"
256  		         "                  [mtu <MTU>]\n"
257  		         "                  [transport-mode datagram | connected]\n"
258  		         "                  [parent <ifname>]\n"
259  		         "                  [p-key <IPoIB P_Key>]\n\n"
260  		         "    bluetooth:    [addr <bluetooth address>]\n"
261  		         "                  [bt-type panu|dun-gsm|dun-cdma]\n\n"
262  		         "    vlan:         dev <parent device (connection  UUID, ifname, or MAC)>\n"
263  		         "                  [id <VLAN id>]\n"
264  		         "                  [flags <VLAN flags>]\n"
265  		         "                  [ingress <ingress priority mapping>]\n"
266  		         "                  [egress <egress priority mapping>]\n"
267  		         "                  [mtu <MTU>]\n\n"
268  		         "    bond:         [mode balance-rr (0) | active-backup (1) | balance-xor (2) | broadcast (3) |\n"
269  		         "                        802.3ad    (4) | balance-tlb   (5) | balance-alb (6)]\n"
270  		         "                  [primary <ifname>]\n"
271  		         "                  [miimon <num>]\n"
272  		         "                  [downdelay <num>]\n"
273  		         "                  [updelay <num>]\n"
274  		         "                  [arp-interval <num>]\n"
275  		         "                  [arp-ip-target <num>]\n\n"
276  		         "    bond-slave:   master <master (ifname or connection UUID)>\n\n"
277  		         "    team:         [config <json config>]\n\n"
278  		         "    team-slave:   master <master (ifname or connection UUID)>\n"
279  		         "                  [config <json config>]\n\n"
280  		         "    bridge:       [stp yes|no>]\n"
281  		         "                  [priority <num>]\n"
282  		         "                  [forward-delay <2-30>]\n"
283  		         "                  [hello-time <1-10>]\n"
284  		         "                  [max-age <6-40>]\n"
285  		         "                  [ageing-time <0-1000000>]\n\n"
286  		         "    bridge-slave: master <master (ifname or connection UUID)\n"
287  		         "                  [priority <0-63>]\n"
288  		         "                  [path-cost <1-65535>]\n"
289  		         "                  [hairpin yes|no]\n\n"
290  		         "    vpn:          vpn-type vpnc|openvpn|pptp|openconnect|openswan\n"
291  		         "                  [user <username>]\n\n"
292  		         "    olpc-mesh:    ssid <SSID>\n"
293  		         "                  [channel <1-13>]\n"
294  		         "                  [dhcp-anycast <MAC address>]\n\n"
295  		         "  IP_OPTIONS:\n"
296  		         "                  [ip4 <IPv4 address>] [gw4 <IPv4 gateway>]\n"
297  		         "                  [ip6 <IPv6 address>] [gw6 <IPv6 gateway>]\n"
298  		         ));
299  	}
300  	
301  	/* The real commands that do something - i.e. not 'help', etc. */
302  	static const char *real_con_commands[] = {
303  		"show",
304  		"up",
305  		"down",
306  		"add",
307  		"modify",
308  		"edit",
309  		"delete",
310  		"reload",
311  		NULL
312  	};
313  	
314  	/* quit main loop */
315  	static void
316  	quit (void)
317  	{
318  		if (progress_id) {
319  			g_source_remove (progress_id);
320  			nmc_terminal_erase_line ();
321  		}
322  	
323  		g_main_loop_quit (loop);  /* quit main loop */
324  	}
325  	
326  	static gboolean
327  	nmc_connection_detail (NMConnection *connection, NmCli *nmc)
328  	{
329  		GError *error = NULL;
330  		GArray *print_settings_array;
331  		int i;
332  		char *fields_str;
333  		char *fields_all =    NMC_FIELDS_SETTINGS_NAMES_ALL;
334  		char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL;
335  		gboolean was_output = FALSE;
336  	
337  		if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
338  			fields_str = fields_common;
339  		else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
340  			fields_str = fields_all;
341  		else
342  			fields_str = nmc->required_fields;
343  	
344  		print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, &error);
345  		if (error) {
346  			if (error->code == 0)
347  				g_string_printf (nmc->return_text, _("Error: 'list configured': %s"), error->message);
348  			else
349  				g_string_printf (nmc->return_text, _("Error: 'list configured': %s; allowed fields: %s"),
350  				                 error->message, NMC_FIELDS_SETTINGS_NAMES_ALL);
351  			g_error_free (error);
352  			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
353  			return FALSE;
354  		}
355  	
356  		/* Main header */
357  		nmc->print_fields.header_name = _("Connection details");
358  		nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL,
359  		                                                 nmc_fields_settings_names, NULL);
360  	
361  		nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
362  		print_required_fields (nmc, nmc_fields_settings_names);
363  	
364  		/* Loop through the required settings and print them. */
365  		for (i = 0; i < print_settings_array->len; i++) {
366  			NMSetting *setting;
367  			int section_idx = g_array_index (print_settings_array, int, i);
368  	
369  			if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
370  				printf ("\n"); /* Empty line */
371  	
372  			was_output = FALSE;
373  	
374  			/* Remove any previous data */
375  			nmc_empty_output_fields (nmc);
376  	
377  			setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name);
378  			if (setting) {
379  				setting_details (setting, nmc);
380  				was_output = TRUE;
381  				continue;
382  			}
383  		}
384  	
385  		if (print_settings_array)
386  			g_array_free (print_settings_array, TRUE);
387  	
388  		return TRUE;
389  	}
390  	
391  	static void
392  	fill_output_connection (gpointer data, gpointer user_data)
393  	{
394  		NMConnection *connection = (NMConnection *) data;
395  		NmCli *nmc = (NmCli *) user_data;
396  		NMSettingConnection *s_con;
397  		guint64 timestamp;
398  		time_t timestamp_real;
399  		char *timestamp_str;
400  		char *timestamp_real_str;
401  		NmcOutputField *arr;
402  	
403  		s_con = nm_connection_get_setting_connection (connection);
404  		if (s_con) {
405  			/* Obtain field values */
406  			timestamp = nm_setting_connection_get_timestamp (s_con);
407  			timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
408  			if (timestamp) {
409  				timestamp_real = timestamp;
410  				timestamp_real_str = g_malloc0 (64);
411  				strftime (timestamp_real_str, 64, "%c", localtime (&timestamp_real));
412  			}
413  	
414  			arr = nmc_dup_fields_array (nmc_fields_con_show,
415  			                            sizeof (nmc_fields_con_show),
416  			                            0);
417  			set_val_strc (arr, 0, nm_setting_connection_get_id (s_con));
418  			set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con));
419  			set_val_strc (arr, 2, nm_setting_connection_get_connection_type (s_con));
420  			set_val_str  (arr, 3, timestamp_str);
421  			set_val_str  (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never")));
422  			set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no"));
423  			set_val_strc (arr, 6, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no"));
424  			set_val_strc (arr, 7, nm_connection_get_path (connection));
425  	
426  			g_ptr_array_add (nmc->output_data, arr);
427  		}
428  	}
429  	
430  	static NMConnection *
431  	find_connection (GSList *list, const char *filter_type, const char *filter_val)
432  	{
433  		NMConnection *connection;
434  		GSList *iterator;
435  		const char *id;
436  		const char *uuid;
437  		const char *path, *path_num;
438  	
439  		iterator = list;
440  		while (iterator) {
441  			connection = NM_CONNECTION (iterator->data);
442  	
443  			id = nm_connection_get_id (connection);
444  			uuid = nm_connection_get_uuid (connection);
445  			path = nm_connection_get_path (connection);
446  			path_num = path ? strrchr (path, '/') + 1 : NULL;
447  	
448  			/* When filter_type is NULL, compare connection ID (filter_val)
449  			 * against all types. Otherwise, only compare against the specific
450  			 * type. If 'path' filter type is specified, comparison against
451  			 * numeric index (in addition to the whole path) is allowed.
452  			 */
453  			if (   (   (!filter_type || strcmp (filter_type, "id")  == 0)
454  			        && strcmp (filter_val, id) == 0)
455  			    || (   (!filter_type || strcmp (filter_type, "uuid") == 0)
456  			        && strcmp (filter_val, uuid) == 0)
457  			    || (   (!filter_type || strcmp (filter_type, "path") == 0)
458  			        && (strcmp (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0))))
459  				return connection;
460  	
461  			iterator = g_slist_next (iterator);
462  		}
463  	
464  		return NULL;
465  	}
466  	
467  	static NMCResultCode
468  	do_connections_show (NmCli *nmc, int argc, char **argv)
469  	{
470  		GError *error1 = NULL;
471  		GError *error2 = NULL;
472  		char *fields_str;
473  		char *fields_all =    NMC_FIELDS_CON_SHOW_ALL;
474  		char *fields_common = NMC_FIELDS_CON_SHOW_COMMON;
475  		gboolean printed = FALSE;
476  	
477  		nmc->should_wait = FALSE;
478  	
479  		if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
480  			fields_str = fields_common;
481  		else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
482  			fields_str = fields_all;
483  		else
484  			fields_str = nmc->required_fields;
485  	
486  		if (argc == 0) {
487  			NmcOutputField *tmpl, *arr;
488  			size_t tmpl_len;
489  	
490  			tmpl = nmc_fields_con_show;
491  			tmpl_len = sizeof (nmc_fields_con_show);
492  			nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, &error1);
493  			if (error1)
494  				goto error;
495  			if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error2))
496  				goto error;
497  	
498  			/* Add headers */
499  			nmc->print_fields.header_name = _("List of configured connections");
500  			arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES);
501  			g_ptr_array_add (nmc->output_data, arr);
502  	
503  			/* Add values */
504  			g_slist_foreach (nmc->system_connections, fill_output_connection, nmc);
505  			print_data (nmc);  /* Print all data */
506  		} else {
507  			while (argc > 0) {
508  				NMConnection *con;
509  				const char *selector = NULL;
510  	
511  				if (   strcmp (*argv, "id") == 0
512  				    || strcmp (*argv, "uuid") == 0
513  				    || strcmp (*argv, "path") == 0) {
514  					selector = *argv;
515  					if (next_arg (&argc, &argv) != 0) {
516  						g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
517  						nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
518  						return nmc->return_value;
519  					}
520  				}
521  				if (!nmc->mode_specified)
522  					nmc->multiline_output = TRUE;  /* multiline mode is default for 'show configured <con>' */
523  	
524  				con = find_connection (nmc->system_connections, selector, *argv);
525  				if (con) {
526  					if (printed)
527  						printf ("\n");
528  					printed = nmc_connection_detail (con, nmc);
529  				} else {
530  					g_string_printf (nmc->return_text, _("Error: %s - no such connection."), *argv);
531  					nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
532  					return nmc->return_value;
533  				}
534  	
535  				argc--;
536  				argv++;
537  			}
538  		}
539  	
540  	error:
541  		if (error1) {
542  			if (error1->code == 0)
543  				g_string_printf (nmc->return_text, _("Error: 'show configured': %s"), error1->message);
544  			else
545  				g_string_printf (nmc->return_text, _("Error: 'show configured': %s; allowed fields: %s"),
546  				                 error1->message, NMC_FIELDS_CON_SHOW_ALL);
547  			g_error_free (error1);
548  			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
549  		}
550  		if (error2) {
551  			g_string_printf (nmc->return_text, _("Error: %s."), error2->message);
552  			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
553  			g_error_free (error2);
554  		}
555  	
556  		return nmc->return_value;
557  	}
558  	
559  	static const char *
560  	active_connection_state_to_string (NMActiveConnectionState state)
561  	{
562  		switch (state) {
563  		case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
564  			return _("activating");
565  		case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
566  			return _("activated");
567  		case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
568  			return _("deactivating");
569  		case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
570  			return _("deactivated");
571  		case NM_ACTIVE_CONNECTION_STATE_UNKNOWN:
572  		default:
573  			return _("unknown");
574  		}
575  	}
576  	
577  	static const char *
578  	vpn_connection_state_to_string (NMVPNConnectionState state)
579  	{
580  		switch (state) {
581  		case NM_VPN_CONNECTION_STATE_PREPARE:
582  			return _("VPN connecting (prepare)");
583  		case NM_VPN_CONNECTION_STATE_NEED_AUTH:
584  			return _("VPN connecting (need authentication)");
585  		case NM_VPN_CONNECTION_STATE_CONNECT:
586  			return _("VPN connecting");
587  		case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
588  			return _("VPN connecting (getting IP configuration)");
589  		case NM_VPN_CONNECTION_STATE_ACTIVATED:
590  			return _("VPN connected");
591  		case NM_VPN_CONNECTION_STATE_FAILED:
592  			return _("VPN connection failed");
593  		case NM_VPN_CONNECTION_STATE_DISCONNECTED:
594  			return _("VPN disconnected");
595  		default:
596  			return _("unknown");
597  		}
598  	}
599  	
600  	static NMConnection *
601  	get_connection_for_active (const GSList *con_list, NMActiveConnection *active)
602  	{
603  		const GSList *iter;
604  		const char *path;
605  	
606  		path = nm_active_connection_get_connection (active);
607  		g_return_val_if_fail (path != NULL, NULL);
608  	
609  		for (iter = con_list; iter; iter = g_slist_next (iter)) {
610  			NMConnection *candidate = NM_CONNECTION (iter->data);
611  	
612  			if (strcmp (nm_connection_get_path (candidate), path) == 0)
613  				return candidate;
614  		}
615  		return NULL;
616  	}
617  	
618  	static void
619  	fill_output_active_connection (NMActiveConnection *active,
620  	                               NmCli *nmc,
621  	                               gboolean with_group,
622  	                               guint32 o_flags)
623  	{
624  		GSList *iter;
625  		const char *active_path;
626  		NMSettingConnection *s_con;
627  		const GPtrArray *devices;
628  		GString *dev_str;
629  		NMActiveConnectionState state;
630  		int i;
631  		GSList *con_list = nmc->system_connections;
632  		NmcOutputField *tmpl, *arr;
633  		size_t tmpl_len;
634  		int idx_start = with_group ? 0 : 1;
635  	
636  		active_path = nm_active_connection_get_connection (active);
637  		state = nm_active_connection_get_state (active);
638  	
639  		/* Get devices of the active connection */
640  		dev_str = g_string_new (NULL);
641  		devices = nm_active_connection_get_devices (active);
642  		for (i = 0; devices && (i < devices->len); i++) {
643  			NMDevice *device = g_ptr_array_index (devices, i);
644  			const char *dev_iface = nm_device_get_iface (device);
645  	
646  			if (dev_iface) {
647  				g_string_append (dev_str, dev_iface);
648  				g_string_append_c (dev_str, ',');
649  			}
650  		}
651  		if (dev_str->len > 0)
652  			g_string_truncate (dev_str, dev_str->len - 1);  /* Cut off last ',' */
653  	
654  		tmpl = nmc_fields_con_show_active;
655  		tmpl_len = sizeof (nmc_fields_con_show_active);
656  		if (!with_group) {
657  			tmpl++;
658  			tmpl_len -= sizeof (NmcOutputField);
659  		}
660  	
661  		/* Fill field values */
662  		arr = nmc_dup_fields_array (tmpl, tmpl_len, o_flags);
663  		if (with_group)
664  			set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0].name);
665  		set_val_strc (arr, 1-idx_start, _("N/A"));
666  		set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active));
667  		set_val_str  (arr, 3-idx_start, dev_str->str);
668  		set_val_strc (arr, 4-idx_start, active_connection_state_to_string (state));
669  		set_val_strc (arr, 5-idx_start, nm_active_connection_get_default (active) ? _("yes") : _("no"));
670  		set_val_strc (arr, 6-idx_start, nm_active_connection_get_default6 (active) ? _("yes") : _("no"));
671  		set_val_strc (arr, 7-idx_start, nm_active_connection_get_specific_object (active));
672  		set_val_strc (arr, 8-idx_start, NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no"));
673  		set_val_strc (arr, 9-idx_start, nm_object_get_path (NM_OBJECT (active)));
674  		set_val_strc (arr, 10-idx_start, nm_active_connection_get_connection (active));
675  		set_val_strc (arr, 11-idx_start, _("N/A"));
676  		set_val_strc (arr, 12-idx_start, nm_active_connection_get_master (active));
677  	
678  		for (iter = con_list; iter; iter = g_slist_next (iter)) {
679  			NMConnection *connection = (NMConnection *) iter->data;
680  			const char *con_path = nm_connection_get_path (connection);
681  	
682  			if (!strcmp (active_path, con_path)) {
683  				/* This connection is active */
684  				s_con = nm_connection_get_setting_connection (connection);
685  				g_assert (s_con != NULL);
686  	
687  				/* Fill field values that depend on NMConnection */
688  				set_val_strc (arr, 1-idx_start,  nm_setting_connection_get_id (s_con));
689  				set_val_strc (arr, 11-idx_start, nm_setting_connection_get_zone (s_con));
690  	
691  				break;
692  			}
693  		}
694  		g_ptr_array_add (nmc->output_data, arr);
695  	
696  		g_string_free (dev_str, FALSE);
697  	}
698  	
699  	static NMActiveConnection *
700  	find_active_connection (const GPtrArray *active_cons, const GSList *cons,
701  	                        const char *filter_type, const char *filter_val)
702  	{
703  		int i;
704  		const char *path, *a_path, *path_num, *a_path_num;
705  		const char *id;
706  		const char *uuid;
707  		NMConnection *con;
708  	
709  		for (i = 0; active_cons && (i < active_cons->len); i++) {
710  			NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
711  	
712  			path = nm_active_connection_get_connection (candidate);
713  			a_path = nm_object_get_path (NM_OBJECT (candidate));
714  			uuid = nm_active_connection_get_uuid (candidate);
715  			path_num = path ? strrchr (path, '/') + 1 : NULL;
716  			a_path_num = a_path ? strrchr (a_path, '/') + 1 : NULL;
717  	
718  			con = get_connection_for_active (cons, candidate);
719  			id = nm_connection_get_id (con);
720  	
721  			/* When filter_type is NULL, compare connection ID (filter_val)
722  			 * against all types. Otherwise, only compare against the specific
723  			 * type. If 'path' or 'apath' filter types are specified, comparison
724  			 * against numeric index (in addition to the whole path) is allowed.
725  			 */
726  			if (   (   (!filter_type || strcmp (filter_type, "id")  == 0)
727  			        && strcmp (filter_val, id) == 0)
728  			    || (   (!filter_type || strcmp (filter_type, "uuid") == 0)
729  			        && strcmp (filter_val, uuid) == 0)
730  			    || (   (!filter_type || strcmp (filter_type, "path") == 0)
731  			        && (strcmp (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)))
732  			    || (   (!filter_type || strcmp (filter_type, "apath") == 0)
733  			        && (strcmp (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0))))
734  				return candidate;
735  		}
736  		return NULL;
737  	}
738  	
739  	typedef struct {
740  		char **array;
741  		guint32 idx;
742  	} FillVPNDataInfo;
743  	
744  	static void
745  	fill_vpn_data_item (const char *key, const char *value, gpointer user_data)
746  	{
747  	        FillVPNDataInfo *info = (FillVPNDataInfo *) user_data;
748  	
749  		info->array[info->idx++] = g_strdup_printf ("%s = %s", key, value);
750  	}
751  	
752  	// FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c),
753  	// and in gnome-control-center as well. It could probably be shared somehow.
754  	static char *
755  	get_vpn_connection_type (NMConnection *connection)
756  	{
757  		const char *type, *p;
758  	
759  		/* The service type is in form of "org.freedesktop.NetworkManager.vpnc".
760  		 * Extract end part after last dot, e.g. "vpnc"
761  		 */
762  		type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
763  		p = strrchr (type, '.');
764  		return g_strdup (p ? p + 1 : type);
765  	}
766  	
767  	/* VPN parameters can be found at:
768  	 * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h
769  	 * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h
770  	 * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h
771  	 * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h
772  	 * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h
773  	 * See also 'properties' directory in these plugins.
774  	 */
775  	static const gchar *
776  	find_vpn_gateway_key (const char *vpn_type)
777  	{
778  		if (g_strcmp0 (vpn_type, "openvpn") == 0)     return "remote";
779  		if (g_strcmp0 (vpn_type, "vpnc") == 0)        return "IPSec gateway";
780  		if (g_strcmp0 (vpn_type, "pptp") == 0)        return "gateway";
781  		if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway";
782  		if (g_strcmp0 (vpn_type, "openswan") == 0)    return "right";
783  		return "";
784  	}
785  	
786  	static const gchar *
787  	find_vpn_username_key (const char *vpn_type)
788  	{
789  		if (g_strcmp0 (vpn_type, "openvpn") == 0)     return "username";
790  		if (g_strcmp0 (vpn_type, "vpnc") == 0)        return "Xauth username";
791  		if (g_strcmp0 (vpn_type, "pptp") == 0)        return "user";
792  		if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username";
793  		if (g_strcmp0 (vpn_type, "openswan") == 0)    return "leftxauthusername";
794  		return "";
795  	}
796  	
797  	enum VpnDataItem {
798  		VPN_DATA_ITEM_GATEWAY,
799  		VPN_DATA_ITEM_USERNAME
800  	};
801  	
802  	static const gchar *
803  	get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item)
804  	{
805  		const char *key;
806  		char *type = get_vpn_connection_type (connection);
807  	
808  		switch (vpn_data_item) {
809  		case VPN_DATA_ITEM_GATEWAY:
810  			key = find_vpn_gateway_key (type);
811  			break;
812  		case VPN_DATA_ITEM_USERNAME:
813  			key = find_vpn_username_key (type);
814  			break;
815  		default:
816  			key = "";
817  			break;
818  		}
819  		g_free (type);
820  	
821  		return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key);
822  	}
823  	/* FIXME end */
824  	
825  	static gboolean
826  	nmc_active_connection_detail (NMActiveConnection *acon, NmCli *nmc)
827  	{
828  		GError *error = NULL;
829  		GArray *print_groups;
830  		int i;
831  		char *fields_str;
832  		char *fields_all =    NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
833  		char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
834  		NmcOutputField *tmpl, *arr;
835  		size_t tmpl_len;
836  		gboolean was_output = FALSE;
837  	
838  		if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
839  			fields_str = fields_common;
840  		else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
841  			fields_str = fields_all;
842  		else
843  			fields_str = nmc->required_fields;
844  	
845  		print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, &error);
846  		if (error) {
847  			if (error->code == 0)
848  				g_string_printf (nmc->return_text, _("Error: 'list active': %s"), error->message);
849  			else
850  				g_string_printf (nmc->return_text, _("Error: 'list active': %s; allowed fields: %s"),
851  				                 error->message, NMC_FIELDS_CON_ACTIVE_DETAILS_ALL);
852  			g_error_free (error);
853  			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
854  			return FALSE;
855  		}
856  	
857  		/* Main header */
858  		nmc->print_fields.header_name = _("Active connection details");
859  		nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL,
860  		                                                 nmc_fields_con_active_details_groups, NULL);
861  	
862  		nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
863  		print_required_fields (nmc, nmc_fields_con_active_details_groups);
864  	
865  		/* Loop through the groups and print them. */
866  		for (i = 0; i < print_groups->len; i++) {
867  			int group_idx = g_array_index (print_groups, int, i);
868  	
869  			if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
870  				printf ("\n"); /* Empty line */
871  	
872  			was_output = FALSE;
873  	
874  			/* Remove any previous data */
875  			nmc_empty_output_fields (nmc);
876  	
877  			/* GENERAL */
878  			if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) {
879  				/* Add field names */
880  				tmpl = nmc_fields_con_show_active;
881  				tmpl_len = sizeof (nmc_fields_con_show_active);
882  				nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL, tmpl, NULL);
883  				arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
884  				g_ptr_array_add (nmc->output_data, arr);
885  	
886  				/* Fill in values */
887  				fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX);
888  	
889  				print_data (nmc);  /* Print all data */
890  	
891  				was_output = TRUE;
892  			}
893  	
894  			/* IP */
895  			if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[1].name) == 0) {
896  				const GPtrArray *devices;
897  				int j;
898  	
899  				devices = nm_active_connection_get_devices (acon);
900  				for (j = 0; devices && (j < devices->len); j++) {
901  					gboolean b1 = FALSE, b2 = FALSE, b3 = FALSE, b4 = FALSE;
902  					NMDevice *device = g_ptr_array_index (devices, j);
903  					NMIP4Config *cfg4 = nm_device_get_ip4_config (device);
904  					NMIP6Config *cfg6 = nm_device_get_ip6_config (device);
905  					NMDHCP4Config *dhcp4 = nm_device_get_dhcp4_config (device);
906  					NMDHCP6Config *dhcp6 = nm_device_get_dhcp6_config (device);
907  	
908  					b1 = print_ip4_config (cfg4, nmc, "IP4");
909  					b2 = print_dhcp4_config (dhcp4, nmc, "DHCP4");
910  					b3 = print_ip6_config (cfg6, nmc, "IP6");
911  					b4 = print_dhcp6_config (dhcp6, nmc, "DHCP6");
912  					was_output = was_output || b1 || b2 || b3 || b4;
913  				}
914  			}
915  	
916  			/* VPN */
917  			if (NM_IS_VPN_CONNECTION (acon) &&
918  			    strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[2].name) == 0) {
919  				NMConnection *con;
920  				NMSettingConnection *s_con;
921  				NMSettingVPN *s_vpn;
922  				NMVPNConnectionState vpn_state;
923  				char *type_str, *banner_str, *vpn_state_str;
924  				const char *username = NULL;
925  				char **vpn_data_array = NULL;
926  				guint32 items_num;
927  	
928  				con = get_connection_for_active (nmc->system_connections, acon);
929  	
930  				s_con = nm_connection_get_setting_connection (con);
931  				g_assert (s_con != NULL);
932  	
933  				tmpl = nmc_fields_con_active_details_vpn;
934  				tmpl_len = sizeof (nmc_fields_con_active_details_vpn);
935  				nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL, tmpl, NULL);
936  				arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
937  				g_ptr_array_add (nmc->output_data, arr);
938  	
939  				s_vpn = nm_connection_get_setting_vpn (con);
940  				if (s_vpn) {
941  					items_num = nm_setting_vpn_get_num_data_items (s_vpn);
942  					if (items_num > 0) {
943  						FillVPNDataInfo info;
944  	
945  						vpn_data_array = g_new (char *, items_num + 1);
946  						info.array = vpn_data_array;
947  						info.idx = 0;
948  						nm_setting_vpn_foreach_data_item (s_vpn, &fill_vpn_data_item, &info);
949  						vpn_data_array[items_num] = NULL;
950  					}
951  					username = nm_setting_vpn_get_user_name (s_vpn);
952  				}
953  	
954  				type_str = get_vpn_connection_type (con);
955  				banner_str = g_strescape (nm_vpn_connection_get_banner (NM_VPN_CONNECTION (acon)), "");
956  				vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon));
957  				vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state));
958  	
959  				/* Add values */
960  				arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX);
961  				set_val_strc (arr, 0, nmc_fields_con_active_details_groups[2].name);
962  				set_val_str  (arr, 1, type_str);
963  				set_val_strc (arr, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME));
964  				set_val_strc (arr, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY));
965  				set_val_str  (arr, 4, banner_str);
966  				set_val_str  (arr, 5, vpn_state_str);
967  				set_val_arr  (arr, 6, vpn_data_array);
968  				g_ptr_array_add (nmc->output_data, arr);
969  	
970  				print_data (nmc);  /* Print all data */
971  				was_output = TRUE;
972  			}
973  		}
974  	
975  		if (print_groups)
976  			g_array_free (print_groups, TRUE);
977  	
978  		return TRUE;
979  	}
980  	
981  	static NMCResultCode
982  	do_connections_show_active (NmCli *nmc, int argc, char **argv)
983  	{
984  		const GPtrArray *active_cons;
985  		int i;
986  		GError *err1 = NULL;
987  		gboolean printed = FALSE;
988  		NmcOutputField *tmpl, *arr;
989  		size_t tmpl_len;
990  	
991  		nmc->should_wait = FALSE;
992  	
993  		/* Get active connections */
994  		nmc->get_client (nmc);
995  	
996  		if (!nm_client_get_manager_running (nmc->client)) {
997  			g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
998  			nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
999  			goto error;
1000 		}
1001 	
1002 		active_cons = nm_client_get_active_connections (nmc->client);
1003 	
1004 		if (argc == 0) {
1005 			char *fields_str;
1006 			char *fields_all =    NMC_FIELDS_CON_ACTIVE_ALL;
1007 			char *fields_common = NMC_FIELDS_CON_ACTIVE_COMMON;
1008 	
1009 			if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
1010 				fields_str = fields_common;
1011 			else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
1012 				fields_str = fields_all;
1013 			else
1014 				fields_str = nmc->required_fields;
1015 	
1016 			tmpl = nmc_fields_con_show_active + 1;
1017 			tmpl_len = sizeof (nmc_fields_con_show_active) - sizeof (NmcOutputField);
1018 			nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, &err1);
1019 			if (err1)
1020 				goto error;
1021 	
1022 			/* Add headers */
1023 			nmc->print_fields.header_name = _("List of active connections");
1024 			arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES);
1025 			g_ptr_array_add (nmc->output_data, arr);
1026 	
1027 			/* Add values */
1028 			for (i = 0; active_cons && i < active_cons->len; i++) {
1029 				NMActiveConnection *ac = g_ptr_array_index (active_cons, i);
1030 				fill_output_active_connection (ac, nmc, FALSE, 0);
1031 			}
1032 			print_data (nmc);  /* Print all data */
1033 		} else {
1034 			while (argc > 0) {
1035 				NMActiveConnection *acon;
1036 				const char *selector = NULL;
1037 	
1038 				if (   strcmp (*argv, "id") == 0
1039 				    || strcmp (*argv, "uuid") == 0
1040 				    || strcmp (*argv, "path") == 0
1041 				    || strcmp (*argv, "apath") == 0) {
1042 	
1043 					selector = *argv;
1044 					if (next_arg (&argc, &argv) != 0) {
1045 						g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1046 						nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1047 						return nmc->return_value;
1048 					}
1049 				}
1050 				if (!nmc->mode_specified)
1051 					nmc->multiline_output = TRUE;  /* multiline mode is default for 'show active <con>' */
1052 	
1053 				acon = find_active_connection (active_cons, nmc->system_connections, selector, *argv);
1054 				if (acon) {
1055 					if (printed)
1056 						printf ("\n");
1057 					printed = nmc_active_connection_detail (acon, nmc); /* separate connections by blank line */
1058 				} else {
1059 					g_string_printf (nmc->return_text, _("Error: '%s' is not an active connection."), *argv);
1060 					nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
1061 					return nmc->return_value;
1062 				}
1063 	
1064 				argc--;
1065 				argv++;
1066 			}
1067 		}
1068 	
1069 	error:
1070 		if (err1) {
1071 			if (err1->code == 0)
1072 				g_string_printf (nmc->return_text, _("Error: 'show active': %s"), err1->message);
1073 			else
1074 				g_string_printf (nmc->return_text, _("Error: 'show active': %s; allowed fields: %s"), err1->message, NMC_FIELDS_CON_ACTIVE_ALL);
1075 			g_error_free (err1);
1076 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1077 		}
1078 		return nmc->return_value;
1079 	}
1080 	
1081 	static NMActiveConnection *
1082 	get_default_active_connection (NmCli *nmc, NMDevice **device)
1083 	{
1084 		NMActiveConnection *default_ac = NULL;
1085 		NMDevice *non_default_device = NULL;
1086 		NMActiveConnection *non_default_ac = NULL;
1087 		const GPtrArray *connections;
1088 		int i;
1089 	
1090 		g_return_val_if_fail (nmc != NULL, NULL);
1091 		g_return_val_if_fail (device != NULL, NULL);
1092 		g_return_val_if_fail (*device == NULL, NULL);
1093 	
1094 		connections = nm_client_get_active_connections (nmc->client);
1095 		for (i = 0; connections && (i < connections->len); i++) {
1096 			NMActiveConnection *candidate = g_ptr_array_index (connections, i);
1097 			const GPtrArray *devices;
1098 	
1099 			devices = nm_active_connection_get_devices (candidate);
1100 			if (!devices || !devices->len)
1101 				continue;
1102 	
1103 			if (nm_active_connection_get_default (candidate)) {
1104 				if (!default_ac) {
1105 					*device = g_ptr_array_index (devices, 0);
1106 					default_ac = candidate;
1107 				}
1108 			} else {
1109 				if (!non_default_ac) {
1110 					non_default_device = g_ptr_array_index (devices, 0);
1111 					non_default_ac = candidate;
1112 				}
1113 			}
1114 		}
1115 	
1116 		/* Prefer the default connection if one exists, otherwise return the first
1117 		 * non-default connection.
1118 		 */
1119 		if (!default_ac && non_default_ac) {
1120 			default_ac = non_default_ac;
1121 			*device = non_default_device;
1122 		}
1123 		return default_ac;
1124 	}
1125 	
1126 	/* Find a device to activate the connection on.
1127 	 * IN:  connection:  connection to activate
1128 	 *      iface:       device interface name to use (optional)
1129 	 *      ap:          access point to use (optional; valid just for 802-11-wireless)
1130 	 *      nsp:         Network Service Provider to use (option; valid only for wimax)
1131 	 * OUT: device:      found device
1132 	 *      spec_object: specific_object path of NMAccessPoint
1133 	 * RETURNS: TRUE when a device is found, FALSE otherwise.
1134 	 */
1135 	static gboolean
1136 	find_device_for_connection (NmCli *nmc,
1137 	                            NMConnection *connection,
1138 	                            const char *iface,
1139 	                            const char *ap,
1140 	                            const char *nsp,
1141 	                            NMDevice **device,
1142 	                            const char **spec_object,
1143 	                            GError **error)
1144 	{
1145 		NMSettingConnection *s_con;
1146 		const char *con_type;
1147 		int i, j;
1148 	
1149 		g_return_val_if_fail (nmc != NULL, FALSE);
1150 		g_return_val_if_fail (device != NULL && *device == NULL, FALSE);
1151 		g_return_val_if_fail (spec_object != NULL && *spec_object == NULL, FALSE);
1152 		g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1153 	
1154 		s_con = nm_connection_get_setting_connection (connection);
1155 		g_assert (s_con);
1156 		con_type = nm_setting_connection_get_connection_type (s_con);
1157 	
1158 		if (strcmp (con_type, NM_SETTING_VPN_SETTING_NAME) == 0) {
1159 			/* VPN connections */
1160 			NMActiveConnection *active = NULL;
1161 			if (iface) {
1162 				*device = nm_client_get_device_by_iface (nmc->client, iface);
1163 				if (*device)
1164 					active = nm_device_get_active_connection (*device);
1165 	
1166 				if (!active) {
1167 					g_set_error (error, NMCLI_ERROR, 0, _("no active connection on device '%s'"), iface);
1168 					return FALSE;
1169 				}
1170 				*spec_object = nm_object_get_path (NM_OBJECT (active));
1171 				return TRUE;
1172 			} else {
1173 				active = get_default_active_connection (nmc, device);
1174 				if (!active) {
1175 					g_set_error_literal (error, NMCLI_ERROR, 0, _("no active connection or device"));
1176 					return FALSE;
1177 				}
1178 				*spec_object = nm_object_get_path (NM_OBJECT (active));
1179 				return TRUE;
1180 			}
1181 		} else {
1182 			/* Other connections */
1183 			NMDevice *found_device = NULL;
1184 			const GPtrArray *devices = nm_client_get_devices (nmc->client);
1185 	
1186 			for (i = 0; devices && (i < devices->len) && !found_device; i++) {
1187 				NMDevice *dev = g_ptr_array_index (devices, i);
1188 	
1189 				if (iface) {
1190 					const char *dev_iface = nm_device_get_iface (dev);
1191 					if (   !g_strcmp0 (dev_iface, iface)
1192 					    && nm_device_connection_compatible (dev, connection, NULL)) {
1193 						found_device = dev;
1194 					}
1195 				} else {
1196 					if (nm_device_connection_compatible (dev, connection, NULL)) {
1197 						found_device = dev;
1198 					}
1199 				}
1200 	
1201 				if (found_device && ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) {
1202 					char *bssid_up = g_ascii_strup (ap, -1);
1203 					const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev));
1204 					found_device = NULL;  /* Mark as not found; set to the device again later, only if AP matches */
1205 	
1206 					for (j = 0; aps && (j < aps->len); j++) {
1207 						NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j);
1208 						const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap);
1209 	
1210 						if (!strcmp (bssid_up, candidate_bssid)) {
1211 							found_device = dev;
1212 							*spec_object = nm_object_get_path (NM_OBJECT (candidate_ap));
1213 							break;
1214 						}
1215 					}
1216 					g_free (bssid_up);
1217 				}
1218 	
1219 	#if WITH_WIMAX
1220 				if (   found_device
1221 				    && nsp
1222 				    && !strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)
1223 				    && NM_IS_DEVICE_WIMAX (dev)) {
1224 					const GPtrArray *nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (dev));
1225 					found_device = NULL;  /* Mark as not found; set to the device again later, only if NSP matches */
1226 	
1227 					for (j = 0; nsps && (j < nsps->len); j++) {
1228 						NMWimaxNsp *candidate_nsp = g_ptr_array_index (nsps, j);
1229 						const char *candidate_name = nm_wimax_nsp_get_name (candidate_nsp);
1230 	
1231 						if (!strcmp (nsp, candidate_name)) {
1232 							found_device = dev;
1233 							*spec_object = nm_object_get_path (NM_OBJECT (candidate_nsp));
1234 							break;
1235 						}
1236 					}
1237 				}
1238 	#endif
1239 			}
1240 	
1241 			if (found_device) {
1242 				*device = found_device;
1243 				return TRUE;
1244 			} else {
1245 				if (iface)
1246 					g_set_error (error, NMCLI_ERROR, 0, _("device '%s' not compatible with connection '%s'"),
1247 					             iface, nm_setting_connection_get_id (s_con));
1248 				else
1249 					g_set_error (error, NMCLI_ERROR, 0, _("no device found for connection '%s'"),
1250 					             nm_setting_connection_get_id (s_con));
1251 				return FALSE;
1252 			}
1253 		}
1254 	}
1255 	
1256 	static const char *
1257 	vpn_connection_state_reason_to_string (NMVPNConnectionStateReason reason)
1258 	{
1259 		switch (reason) {
1260 		case NM_VPN_CONNECTION_STATE_REASON_UNKNOWN:
1261 			return _("unknown reason");
1262 		case NM_VPN_CONNECTION_STATE_REASON_NONE:
1263 			return _("none");
1264 		case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED:
1265 			return _("the user was disconnected");
1266 		case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED:
1267 			return _("the base network connection was interrupted");
1268 		case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED:
1269 			return _("the VPN service stopped unexpectedly");
1270 		case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID:
1271 			return _("the VPN service returned invalid configuration");
1272 		case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT:
1273 			return _("the connection attempt timed out");
1274 		case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT:
1275 			return _("the VPN service did not start in time");
1276 		case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED:
1277 			return _("the VPN service failed to start");
1278 		case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS:
1279 			return _("no valid VPN secrets");
1280 		case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED:
1281 			return _("invalid VPN secrets");
1282 		case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED:
1283 			return _("the connection was removed");
1284 		default:
1285 			return _("unknown");
1286 		}
1287 	}
1288 	
1289 	static void
1290 	active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data)
1291 	{
1292 		NmCli *nmc = (NmCli *) user_data;
1293 		NMActiveConnectionState state;
1294 	
1295 		state = nm_active_connection_get_state (active);
1296 	
1297 		if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1298 			if (nmc->print_output == NMC_PRINT_PRETTY)
1299 				nmc_terminal_erase_line ();
1300 			printf (_("Connection successfully activated (D-Bus active path: %s)\n"),
1301 			        nm_object_get_path (NM_OBJECT (active)));
1302 			quit ();
1303 		} else if (   state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
1304 		           || state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) {
1305 			g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
1306 			nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1307 			quit ();
1308 		}
1309 	}
1310 	
1311 	static void
1312 	vpn_connection_state_cb (NMVPNConnection *vpn,
1313 	                         NMVPNConnectionState state,
1314 	                         NMVPNConnectionStateReason reason,
1315 	                         gpointer user_data)
1316 	{
1317 		NmCli *nmc = (NmCli *) user_data;
1318 	
1319 		switch (state) {
1320 		case NM_VPN_CONNECTION_STATE_PREPARE:
1321 		case NM_VPN_CONNECTION_STATE_NEED_AUTH:
1322 		case NM_VPN_CONNECTION_STATE_CONNECT:
1323 		case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
1324 			/* no operation */
1325 			break;
1326 	
1327 		case NM_VPN_CONNECTION_STATE_ACTIVATED:
1328 			if (nmc->print_output == NMC_PRINT_PRETTY)
1329 				nmc_terminal_erase_line ();
1330 			printf (_("VPN connection successfully activated (D-Bus active path: %s)\n"),
1331 			        nm_object_get_path (NM_OBJECT (vpn)));
1332 			quit ();
1333 			break;
1334 	
1335 		case NM_VPN_CONNECTION_STATE_FAILED:
1336 		case NM_VPN_CONNECTION_STATE_DISCONNECTED:
1337 			g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."),
1338 			                 vpn_connection_state_reason_to_string (reason));
1339 			nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1340 			quit ();
1341 			break;
1342 	
1343 		default:
1344 			break;
1345 		}
1346 	}
1347 	
1348 	static gboolean
1349 	timeout_cb (gpointer user_data)
1350 	{
1351 		/* Time expired -> exit nmcli */
1352 	
1353 		NmCli *nmc = (NmCli *) user_data;
1354 	
1355 		g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout);
1356 		nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED;
1357 		quit ();
1358 		return FALSE;
1359 	}
1360 	
1361 	static gboolean
1362 	progress_cb (gpointer user_data)
1363 	{
1364 		const char *str = (const char *) user_data;
1365 	
1366 		nmc_terminal_show_progress (str);
1367 	
1368 		return TRUE;
1369 	}
1370 	
1371 	static gboolean
1372 	progress_device_cb (gpointer user_data)
1373 	{
1374 		NMDevice *device = (NMDevice *) user_data;
1375 	
1376 		nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : "");
1377 	
1378 		return TRUE;
1379 	}
1380 	
1381 	static gboolean
1382 	progress_vpn_cb (gpointer user_data)
1383 	{
1384 		NMVPNConnection *vpn = (NMVPNConnection *) user_data;
1385 		const char *str;
1386 	
1387 		str = NM_IS_VPN_CONNECTION (vpn) ?
1388 		        vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) :
1389 		        "";
1390 	
1391 		nmc_terminal_show_progress (str);
1392 	
1393 		return TRUE;
1394 	}
1395 	
1396 	typedef struct {
1397 		NmCli *nmc;
1398 		NMDevice *device;
1399 	} ActivateConnectionInfo;
1400 	
1401 	static gboolean
1402 	master_iface_slaves_check (gpointer user_data)
1403 	{
1404 		ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
1405 		NmCli *nmc = info->nmc;
1406 		NMDevice *device = info->device;
1407 		const GPtrArray *slaves = NULL;
1408 	
1409 		if (NM_IS_DEVICE_BOND (device))
1410 			slaves = nm_device_bond_get_slaves (NM_DEVICE_BOND (device));
1411 		else if (NM_IS_DEVICE_TEAM (device))
1412 			slaves = nm_device_team_get_slaves (NM_DEVICE_TEAM (device));
1413 		else if (NM_IS_DEVICE_BRIDGE (device))
1414 			slaves = nm_device_bridge_get_slaves (NM_DEVICE_BRIDGE (device));
1415 		else
1416 			g_warning ("%s: should not be reached.", __func__);
1417 	
1418 		if (!slaves) {
1419 			g_string_printf (nmc->return_text,
1420 			                 _("Error: Device '%s' is waiting for slaves before proceeding with activation."),
1421 			                 nm_device_get_iface (device));
1422 			nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED;
1423 			quit ();
1424 		}
1425 	
1426 		g_free (info);
1427 		return FALSE;
1428 	}
1429 	
1430 	static void
1431 	activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *error, gpointer user_data)
1432 	{
1433 		ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
1434 		NmCli *nmc = info->nmc;
1435 		NMDevice *device = info->device;
1436 		NMActiveConnectionState state;
1437 		const GPtrArray *ac_devs;
1438 	
1439 		if (error) {
1440 			g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"), error->message);
1441 			nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1442 			quit ();
1443 		} else {
1444 			state = nm_active_connection_get_state (active);
1445 			if (!device) {
1446 				/* device could be NULL for virtual devices. Fill it here. */
1447 				ac_devs = nm_active_connection_get_devices (active);
1448 				info->device = device = ac_devs && ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
1449 			}
1450 	
1451 			if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1452 				/* User doesn't want to wait or already activated */
1453 				if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1454 					if (nmc->print_output == NMC_PRINT_PRETTY)
1455 						nmc_terminal_erase_line ();
1456 					printf (_("Connection successfully activated (D-Bus active path: %s)\n"),
1457 					        nm_object_get_path (NM_OBJECT (active)));
1458 				}
1459 				quit ();
1460 			} else {
1461 				if (NM_IS_VPN_CONNECTION (active)) {
1462 					/* Monitor VPN state */
1463 					g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc);
1464 	
1465 					/* Start progress indication showing VPN states */
1466 					if (nmc->print_output == NMC_PRINT_PRETTY) {
1467 						if (progress_id)
1468 							g_source_remove (progress_id);
1469 						progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active));
1470 					}
1471 				} else {
1472 					g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc);
1473 	
1474 					/* Start progress indication showing device states */
1475 					if (nmc->print_output == NMC_PRINT_PRETTY) {
1476 						if (progress_id)
1477 							g_source_remove (progress_id);
1478 						progress_id = g_timeout_add (120, progress_device_cb, device);
1479 					}
1480 				}
1481 	
1482 				/* Start timer not to loop forever when signals are not emitted */
1483 				g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);
1484 	
1485 				/* Check for bond or team or bridge slaves */
1486 				if (   NM_IS_DEVICE_BOND (device)
1487 				    || NM_IS_DEVICE_TEAM (device)
1488 				    || NM_IS_DEVICE_BRIDGE (device)) {
1489 					g_timeout_add_seconds (SLAVES_UP_TIMEOUT, master_iface_slaves_check, info);
1490 					return; /* info will be freed in master_iface_slaves_check () */
1491 				}
1492 			}
1493 		}
1494 		g_free (info);
1495 	}
1496 	
1497 	static NMCResultCode
1498 	do_connection_up (NmCli *nmc, int argc, char **argv)
1499 	{
1500 		ActivateConnectionInfo *info;
1501 		NMDevice *device = NULL;
1502 		const char *spec_object = NULL;
1503 		gboolean device_found;
1504 		NMConnection *connection = NULL;
1505 		const char *ifname = NULL;
1506 		const char *ap = NULL;
1507 		const char *nsp = NULL;
1508 		GError *error = NULL;
1509 		gboolean is_virtual = FALSE;
1510 		const char *selector = NULL;
1511 		const char *name;
1512 		char *line = NULL;
1513 	
1514 		/*
1515 		 * Set default timeout for connection activation.
1516 		 * Activation can take quite a long time, use 90 seconds.
1517 		 */
1518 		if (nmc->timeout == -1)
1519 			nmc->timeout = 90;
1520 	
1521 		if (argc == 0) {
1522 			if (nmc->ask) {
1523 				line = nmc_get_user_input (_("Connection (name, UUID, or path): "));
1524 				name = line ? line : "";
1525 				// TODO: enhancement:  when just Enter is pressed (line is NULL), list
1526 				// available connections so that the user can select one
1527 			} else {
1528 				g_string_printf (nmc->return_text, _("Error: No connection specified."));
1529 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1530 				goto error;
1531 			}
1532 		} else {
1533 			if (   strcmp (*argv, "id") == 0
1534 			    || strcmp (*argv, "uuid") == 0
1535 			    || strcmp (*argv, "path") == 0) {
1536 	
1537 				selector = *argv;
1538 				if (next_arg (&argc, &argv) != 0) {
1539 					g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
1540 					nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1541 					goto error;
1542 				}
1543 				name = *argv;
1544 			}
1545 			name = *argv;
1546 		}
1547 	
1548 		connection = find_connection (nmc->system_connections, selector, name);
1549 	
1550 		if (!connection) {
1551 			g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), name);
1552 			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
1553 			goto error;
1554 		}
1555 		next_arg (&argc, &argv);
1556 	
1557 		while (argc > 0) {
1558 			if (strcmp (*argv, "ifname") == 0) {
1559 				if (next_arg (&argc, &argv) != 0) {
1560 					g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1561 					nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1562 					goto error;
1563 				}
1564 	
1565 				ifname = *argv;
1566 			}
1567 			else if (strcmp (*argv, "ap") == 0) {
1568 				if (next_arg (&argc, &argv) != 0) {
1569 					g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1570 					nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1571 					goto error;
1572 				}
1573 	
1574 				ap = *argv;
1575 			}
1576 	#if WITH_WIMAX
1577 			else if (strcmp (*argv, "nsp") == 0) {
1578 				if (next_arg (&argc, &argv) != 0) {
1579 					g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1580 					nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1581 					goto error;
1582 				}
1583 	
1584 				nsp = *argv;
1585 			}
1586 	#endif
1587 			 else {
1588 				fprintf (stderr, _("Unknown parameter: %s\n"), *argv);
1589 			}
1590 	
1591 			argc--;
1592 			argv++;
1593 		}
1594 	
1595 		/* create NMClient */
1596 		nmc->get_client (nmc);
1597 	
1598 		if (!nm_client_get_manager_running (nmc->client)) {
1599 			g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
1600 			nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
1601 			goto error;
1602 		}
1603 	
1604 		if (nm_connection_get_virtual_iface_name (connection))
1605 			is_virtual = TRUE;
1606 	
1607 		device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &error);
1608 		/* Virtual connection may not have their interfaces created yet */
1609 		if (!device_found && !is_virtual) {
1610 			if (error)
1611 				g_string_printf (nmc->return_text, _("Error: No suitable device found: %s."), error->message);
1612 			else
1613 				g_string_printf (nmc->return_text, _("Error: No suitable device found."));
1614 			nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1615 			g_clear_error (&error);
1616 			goto error;
1617 		}
1618 	
1619 		/* Use nowait_flag instead of should_wait because exiting has to be postponed till
1620 		 * active_connection_state_cb() is called. That gives NM time to check our permissions
1621 		 * and we can follow activation progress.
1622 		 */
1623 		nmc->nowait_flag = (nmc->timeout == 0);
1624 		nmc->should_wait = TRUE;
1625 	
1626 		info = g_malloc0 (sizeof (ActivateConnectionInfo));
1627 		info->nmc = nmc;
1628 		info->device = device;
1629 	
1630 		nm_client_activate_connection (nmc->client,
1631 		                               connection,
1632 		                               device,
1633 		                               spec_object,
1634 		                               activate_connection_cb,
1635 		                               info);
1636 	
1637 		/* Start progress indication */
1638 		if (nmc->print_output == NMC_PRINT_PRETTY)
1639 			progress_id = g_timeout_add (120, progress_cb, _("preparing"));
1640 	
1641 		g_free (line);
1642 		return nmc->return_value;
1643 	error:
1644 		nmc->should_wait = FALSE;
1645 		g_free (line);
1646 		return nmc->return_value;
1647 	}
1648 	
1649 	static NMCResultCode
1650 	do_connection_down (NmCli *nmc, int argc, char **argv)
1651 	{
1652 		NMActiveConnection *active;
1653 		const GPtrArray *active_cons;
1654 		char *line = NULL;
1655 		char **arg_arr = NULL;
1656 		char **arg_ptr = argv;
1657 		int arg_num = argc;
1658 	
1659 		if (argc == 0) {
1660 			if (nmc->ask) {
1661 				line = nmc_get_user_input (_("Connection (name, UUID, or path): "));
1662 				nmc_string_to_arg_array (line, "", &arg_arr, &arg_num);
1663 				arg_ptr = arg_arr;
1664 			}
1665 			if (arg_num == 0) {
1666 				g_string_printf (nmc->return_text, _("Error: No connection specified."));
1667 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1668 				goto error;
1669 			}
1670 		}
1671 	
1672 		/* create NMClient */
1673 		nmc->get_client (nmc);
1674 	
1675 		if (!nm_client_get_manager_running (nmc->client)) {
1676 			g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
1677 			nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
1678 			goto error;
1679 		}
1680 	
1681 		/* Get active connections */
1682 		active_cons = nm_client_get_active_connections (nmc->client);
1683 		while (arg_num > 0) {
1684 			const char *selector = NULL;
1685 	
1686 			if (   strcmp (*arg_ptr, "id") == 0
1687 			    || strcmp (*arg_ptr, "uuid") == 0
1688 			    || strcmp (*arg_ptr, "path") == 0
1689 			    || strcmp (*arg_ptr, "apath") == 0) {
1690 	
1691 				selector = *arg_ptr;
1692 				if (next_arg (&arg_num, &arg_ptr) != 0) {
1693 					g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
1694 					nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1695 					goto error;
1696 				}
1697 			}
1698 	
1699 			active = find_active_connection (active_cons, nmc->system_connections, selector, *arg_ptr);
1700 			if (active) {
1701 				nm_client_deactivate_connection (nmc->client, active);
1702 			} else {
1703 				g_string_printf (nmc->return_text, _("Error: '%s' is not an active connection."), *arg_ptr);
1704 				nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
1705 				goto error;
1706 			}
1707 	
1708 			next_arg (&arg_num, &arg_ptr);
1709 		}
1710 	
1711 		// FIXME: do something better then sleep()
1712 		/* Don't quit immediatelly and give NM time to check our permissions */
1713 		sleep (1);
1714 	
1715 	error:
1716 		nmc->should_wait = FALSE;
1717 		g_strfreev (arg_arr);
1718 		return nmc->return_value;
1719 	}
1720 	
1721 	/*----------------------------------------------------------------------------*/
1722 	
1723 	typedef struct NameItem {
1724 		const char *name;
1725 		const char *alias;
1726 		const struct NameItem *settings;
1727 		gboolean mandatory;
1728 	} NameItem;
1729 	
1730 	static const NameItem nmc_ethernet_settings [] = {
1731 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
1732 		{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, TRUE  },
1733 		{ NM_SETTING_802_1X_SETTING_NAME,     NULL,       NULL, FALSE },
1734 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1735 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1736 		{ NULL, NULL, NULL, FALSE }
1737 	};
1738 	
1739 	static const NameItem nmc_infiniband_settings [] = {
1740 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE  },
1741 		{ NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL, TRUE  },
1742 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1743 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1744 		{ NULL, NULL, NULL, FALSE }
1745 	};
1746 	
1747 	static const NameItem nmc_wifi_settings [] = {
1748 		{ NM_SETTING_CONNECTION_SETTING_NAME,        NULL,       NULL, TRUE  },
1749 		{ NM_SETTING_WIRELESS_SETTING_NAME,          "wifi",     NULL, TRUE  },
1750 		{ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-sec", NULL, FALSE },
1751 		{ NM_SETTING_802_1X_SETTING_NAME,            NULL,       NULL, FALSE },
1752 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME,        NULL,       NULL, FALSE },
1753 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME,        NULL,       NULL, FALSE },
1754 		{ NULL, NULL, NULL, FALSE }
1755 	};
1756 	
1757 	static const NameItem nmc_wimax_settings [] = {
1758 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
1759 		{ NM_SETTING_WIMAX_SETTING_NAME,      NULL,   NULL, TRUE  },
1760 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
1761 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
1762 		{ NULL, NULL, NULL, FALSE }
1763 	};
1764 	
1765 	static const NameItem nmc_gsm_settings [] = {
1766 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
1767 		{ NM_SETTING_GSM_SETTING_NAME,        NULL,       NULL, TRUE  },
1768 		{ NM_SETTING_SERIAL_SETTING_NAME,     NULL,       NULL, FALSE },
1769 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1770 		{ NULL, NULL, NULL, FALSE }
1771 	};
1772 	
1773 	static const NameItem nmc_cdma_settings [] = {
1774 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
1775 		{ NM_SETTING_CDMA_SETTING_NAME,       NULL,       NULL, TRUE  },
1776 		{ NM_SETTING_SERIAL_SETTING_NAME,     NULL,       NULL, FALSE },
1777 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1778 		{ NULL, NULL, NULL, FALSE }
1779 	};
1780 	
1781 	static const NameItem nmc_mobile_settings [] = {
1782 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
1783 		{ NM_SETTING_SERIAL_SETTING_NAME,     NULL,   NULL, FALSE },
1784 		{ NM_SETTING_PPP_SETTING_NAME,        NULL,   NULL, FALSE },
1785 		{ NM_SETTING_GSM_SETTING_NAME,        NULL,   NULL, TRUE  },
1786 		{ NM_SETTING_CDMA_SETTING_NAME,       NULL,   NULL, TRUE  },
1787 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
1788 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
1789 		{ NULL, NULL, NULL, FALSE }
1790 	};
1791 	
1792 	static const NameItem nmc_bluetooth_settings [] = {
1793 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
1794 		{ NM_SETTING_BLUETOOTH_SETTING_NAME,  NULL,   NULL, TRUE  },
1795 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
1796 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
1797 		{ NULL, NULL, NULL, FALSE }
1798 	};
1799 	
1800 	static const NameItem nmc_adsl_settings [] = {
1801 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
1802 		{ NM_SETTING_ADSL_SETTING_NAME,       NULL,   NULL, TRUE  },
1803 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
1804 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
1805 		{ NULL, NULL, NULL, FALSE }
1806 	};
1807 	
1808 	static const NameItem nmc_ppoe_settings [] = {
1809 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
1810 		{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
1811 		{ NM_SETTING_PPPOE_SETTING_NAME,      NULL,       NULL, TRUE  },
1812 		{ NM_SETTING_PPP_SETTING_NAME,        NULL,       NULL, FALSE },
1813 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1814 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1815 		{ NULL, NULL, NULL, FALSE }
1816 	};
1817 	
1818 	static const NameItem nmc_olpc_mesh_settings [] = {
1819 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,        NULL, TRUE  },
1820 		{ NM_SETTING_OLPC_MESH_SETTING_NAME,  "olpc-mesh", NULL, TRUE  },
1821 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,        NULL, FALSE },
1822 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,        NULL, FALSE },
1823 		{ NULL, NULL, NULL, FALSE }
1824 	};
1825 	
1826 	static const NameItem nmc_vpn_settings [] = {
1827 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
1828 		{ NM_SETTING_VPN_SETTING_NAME,        NULL,   NULL, TRUE  },
1829 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
1830 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
1831 		{ NULL, NULL, NULL, FALSE }
1832 	};
1833 	
1834 	static const NameItem nmc_vlan_settings [] = {
1835 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
1836 		{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
1837 		{ NM_SETTING_VLAN_SETTING_NAME,       NULL,       NULL, TRUE  },
1838 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1839 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1840 		{ NULL, NULL, NULL, FALSE }
1841 	};
1842 	
1843 	static const NameItem nmc_bond_settings [] = {
1844 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
1845 		{ NM_SETTING_BOND_SETTING_NAME,       NULL,       NULL, TRUE  },
1846 		{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
1847 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1848 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1849 		{ NULL, NULL, NULL, FALSE }
1850 	};
1851 	
1852 	static const NameItem nmc_team_settings [] = {
1853 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
1854 		{ NM_SETTING_TEAM_SETTING_NAME,       NULL,       NULL, TRUE  },
1855 		{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
1856 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1857 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1858 		{ NULL, NULL, NULL, FALSE }
1859 	};
1860 	
1861 	static const NameItem nmc_bridge_settings [] = {
1862 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
1863 		{ NM_SETTING_BRIDGE_SETTING_NAME,     NULL,       NULL, TRUE  },
1864 		{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
1865 		{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1866 		{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,       NULL, FALSE },
1867 		{ NULL, NULL, NULL, FALSE }
1868 	};
1869 	
1870 	static const NameItem nmc_bond_slave_settings [] = {
1871 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
1872 		{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, TRUE  },
1873 		{ NM_SETTING_802_1X_SETTING_NAME,     NULL,       NULL, FALSE },
1874 		{ NULL, NULL, NULL, FALSE }
1875 	};
1876 	
1877 	static const NameItem nmc_team_slave_settings [] = {
1878 		{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
1879 		{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, TRUE  },
1880 		{ NM_SETTING_TEAM_PORT_SETTING_NAME,  NULL,       NULL, TRUE  },
1881 		{ NM_SETTING_802_1X_SETTING_NAME,     NULL,       NULL, FALSE },
1882 		{ NULL, NULL, NULL, FALSE }
1883 	};
1884 	
1885 	static const NameItem nmc_bridge_slave_settings [] = {
1886 		{ NM_SETTING_CONNECTION_SETTING_NAME,  NULL,       NULL, TRUE  },
1887 		{ NM_SETTING_BRIDGE_PORT_SETTING_NAME, NULL,       NULL, TRUE  },
1888 		{ NM_SETTING_WIRED_SETTING_NAME,       "ethernet", NULL, TRUE  },
1889 		{ NM_SETTING_802_1X_SETTING_NAME,      NULL,       NULL, FALSE },
1890 		{ NULL, NULL, NULL, FALSE }
1891 	};
1892 	
1893 	
1894 	/* Available connection types */
1895 	static const NameItem nmc_valid_connection_types[] = {
1896 		{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet",  nmc_ethernet_settings     },
1897 		{ NM_SETTING_WIRELESS_SETTING_NAME,   "wifi",      nmc_wifi_settings         },
1898 		{ NM_SETTING_WIMAX_SETTING_NAME,      NULL,        nmc_wimax_settings        },
1899 		{ NM_SETTING_GSM_SETTING_NAME,        NULL,        nmc_gsm_settings          },
1900 		{ NM_SETTING_CDMA_SETTING_NAME,       NULL,        nmc_cdma_settings         },
1901 		{ NM_SETTING_INFINIBAND_SETTING_NAME, NULL,        nmc_infiniband_settings   },
1902 		{ NM_SETTING_ADSL_SETTING_NAME,       NULL,        nmc_adsl_settings         },
1903 		{ NM_SETTING_BLUETOOTH_SETTING_NAME,  NULL,        nmc_bluetooth_settings    },
1904 		{ NM_SETTING_VPN_SETTING_NAME,        NULL,        nmc_vpn_settings          },
1905 		{ NM_SETTING_OLPC_MESH_SETTING_NAME,  "olpc-mesh", nmc_olpc_mesh_settings    },
1906 		{ NM_SETTING_VLAN_SETTING_NAME,       NULL,        nmc_vlan_settings         },
1907 		{ NM_SETTING_BOND_SETTING_NAME,       NULL,        nmc_bond_settings         },
1908 		{ NM_SETTING_TEAM_SETTING_NAME,       NULL,        nmc_team_settings         },
1909 		{ NM_SETTING_BRIDGE_SETTING_NAME,     NULL,        nmc_bridge_settings       },
1910 		{ "bond-slave",                       NULL,        nmc_bond_slave_settings   },
1911 		{ "team-slave",                       NULL,        nmc_team_slave_settings   },
1912 		{ "bridge-slave",                     NULL,        nmc_bridge_slave_settings },
1913 		{ NULL, NULL, NULL }
1914 	};
1915 	
1916 	/*
1917 	 * Return an alias for the 'name' if exists, else return the 'name'.
1918 	 * The returned string must not be freed.
1919 	 */
1920 	static const char *
1921 	get_name_alias (const char *name, const NameItem array[])
1922 	{
1923 		const NameItem *iter = &array[0];
1924 	
1925 		if (!name)
1926 			return NULL;
1927 	
1928 	        while (iter && iter->name) {
1929 			if (!strcmp (name, iter->name)) {
1930 				if (iter->alias)
1931 					return iter->alias;
1932 				else
1933 					return iter->name;
1934 			}
1935 			iter++;
1936 		}
1937 		return name;
1938 	}
1939 	
1940 	/*
1941 	 * Construct a string with names and aliases from the array formatted as:
1942 	 * "name (alias), name, name (alias), name, name"
1943 	 *
1944 	 * Returns: string; the caller is responsible for freeing it.
1945 	 */
1946 	static char *
1947 	get_valid_options_string (const NameItem array[])
1948 	{
1949 		const NameItem *iter = &array[0];
1950 		GString *str;
1951 	
1952 		str = g_string_sized_new (150);
1953 		while (iter && iter->name) {
1954 			if (str->len)
1955 				g_string_append (str, ", ");
1956 			if (iter->alias)
1957 				g_string_append_printf (str, "%s (%s)", iter->name, iter->alias);
1958 			else
1959 				g_string_append (str, iter->name);
1960 			iter++;
1961 		}
1962 		return g_string_free (str, FALSE);
1963 	}
1964 	
1965 	/*
1966 	 * Check if 'val' is valid string in either array->name or array->alias.
1967 	 * It accepts shorter string provided they are not ambiguous.
1968 	 * 'val' == NULL doesn't hurt.
1969 	 *
1970 	 * Returns: pointer to array->name string or NULL on failure.
1971 	 * The returned string must not be freed.
1972 	 */
1973 	static const char *
1974 	check_valid_name (const char *val, const NameItem array[], GError **error)
1975 	{
1976 		const NameItem *iter;
1977 		GPtrArray *tmp_arr;
1978 		const char *str;
1979 		GError *tmp_err = NULL;
1980 	
1981 		/* Create a temporary array that can be used in nmc_string_is_valid() */
1982 		tmp_arr = g_ptr_array_sized_new (30);
1983 		iter = &array[0];
1984 		while (iter && iter->name) {
1985 			g_ptr_array_add (tmp_arr, (gpointer) iter->name);
1986 			if (iter->alias)
1987 				g_ptr_array_add (tmp_arr, (gpointer) iter->alias);
1988 			iter++;
1989 		}
1990 		g_ptr_array_add (tmp_arr, (gpointer) NULL);
1991 	
1992 		/* Check string validity */
1993 		str = nmc_string_is_valid (val, (const char **) tmp_arr->pdata, &tmp_err);
1994 		if (!str) {
1995 			if (tmp_err->code == 1)
1996 				g_propagate_error (error, tmp_err);
1997 			else {
1998 				/* We want to handle aliases, so construct own error message */
1999 				char *err_str = get_valid_options_string (array);
2000 				g_set_error (error, 1, 0, _("'%s' not among [%s]"),
2001 				             val ? val : "", err_str);
2002 				g_free (err_str);
2003 				g_clear_error (&tmp_err);
2004 			}
2005 			g_ptr_array_free (tmp_arr, TRUE);
2006 			return NULL;
2007 		}
2008 	
2009 		/* Return a pointer to the found string in passed 'array' */
2010 		iter = &array[0];
2011 		while (iter && iter->name) {
2012 			if (   (iter->name && g_strcmp0 (iter->name, str) == 0)
2013 			    || (iter->alias && g_strcmp0 (iter->alias, str) == 0)) {
2014 				g_ptr_array_free (tmp_arr, TRUE);
2015 				return iter->name;
2016 			}
2017 			iter++;
2018 		}
2019 		/* We should not really come here */
2020 		g_ptr_array_free (tmp_arr, TRUE);
2021 		g_set_error (error, 1, 0, _("Unknown error"));
2022 		return NULL;
2023 	}
2024 	
2025 	static const NameItem *
2026 	get_valid_settings_array (const char *con_type)
2027 	{
2028 		guint i, num;
2029 	
2030 		if (!con_type)
2031 			return NULL;
2032 	
2033 		num = G_N_ELEMENTS (nmc_valid_connection_types);
2034 	        for (i = 0; i < num; i++) {
2035 			if (!strcmp (con_type, nmc_valid_connection_types[i].name))
2036 				return nmc_valid_connection_types[i].settings;
2037 		}
2038 		return NULL;
2039 	}
2040 	
2041 	static gboolean
2042 	is_setting_mandatory (NMConnection *connection, NMSetting *setting)
2043 	{
2044 		NMSettingConnection *s_con;
2045 		const char *c_type;
2046 		const NameItem *item;
2047 		const char *name;
2048 	
2049 		s_con = nm_connection_get_setting_connection (connection);
2050 		g_assert (s_con);
2051 		c_type = nm_setting_connection_get_connection_type (s_con);
2052 	
2053 		name = nm_setting_get_name (setting);
2054 	
2055 		item = get_valid_settings_array (c_type);
2056 		while (item && item->name) {
2057 			if (!strcmp (name, item->name))
2058 				return item->mandatory;
2059 			item++;
2060 		}
2061 		return FALSE;
2062 	}
2063 	
2064 	/*----------------------------------------------------------------------------*/
2065 	
2066 	static gboolean
2067 	check_and_convert_mac (const char *mac,
2068 	                       GByteArray **mac_array,
2069 	                       int type,
2070 	                       const char *keyword,
2071 	                       GError **error)
2072 	{
2073 		GByteArray *local_mac_array = NULL;
2074 		g_return_val_if_fail (mac_array == NULL || *mac_array == NULL, FALSE);
2075 	
2076 		if (!mac)
2077 			return TRUE;
2078 	
2079 		local_mac_array = nm_utils_hwaddr_atoba (mac, type);
2080 		if (!local_mac_array) {
2081 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2082 			             _("Error: '%s': '%s' is not a valid %s MAC address."),
2083 			             keyword, mac, type == ARPHRD_INFINIBAND ? _("InfiniBand") : _("Ethernet"));
2084 			return FALSE;
2085 		}
2086 	
2087 		if (mac_array)
2088 			*mac_array = local_mac_array;
2089 		else
2090 			if (local_mac_array)
2091 				g_byte_array_free (local_mac_array, TRUE);
2092 	
2093 		return TRUE;
2094 	}
2095 	
2096 	static gboolean
2097 	check_and_convert_mtu (const char *mtu, guint32 *mtu_int, GError **error)
2098 	{
2099 		unsigned long local_mtu_int;
2100 	
2101 		if (!mtu)
2102 			return TRUE;
2103 	
2104 		if (!nmc_string_to_uint (mtu, TRUE, 0, G_MAXUINT32, &local_mtu_int)) {
2105 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2106 			             _("Error: 'mtu': '%s' is not a valid MTU."), mtu);
2107 			return FALSE;
2108 		}
2109 		if (mtu_int)
2110 			*mtu_int = (guint32) local_mtu_int;
2111 		return TRUE;
2112 	}
2113 	
2114 	static gboolean
2115 	check_infiniband_parent (const char *parent, GError **error)
2116 	{
2117 		if (!parent)
2118 			return TRUE;
2119 	
2120 		if (!nm_utils_iface_valid_name (parent)) {
2121 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2122 			             _("Error: 'parent': '%s' is not a valid interface name."), parent);
2123 			return FALSE;
2124 		}
2125 		return TRUE;
2126 	}
2127 	
2128 	
2129 	static gboolean
2130 	check_infiniband_p_key (const char *p_key, guint32 *p_key_int, GError **error)
2131 	{
2132 		unsigned long local_p_key_int;
2133 		gboolean p_key_valid = FALSE;
2134 		if (!p_key)
2135 			return TRUE;
2136 	
2137 		if (!strncmp (p_key, "0x", 2))
2138 			p_key_valid = nmc_string_to_uint_base (p_key + 2, 16, TRUE, 0, G_MAXUINT16, &local_p_key_int);
2139 		else
2140 			p_key_valid = nmc_string_to_uint (p_key, TRUE, 0, G_MAXUINT16, &local_p_key_int);
2141 		if (!p_key_valid) {
2142 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2143 			             _("Error: 'p-key': '%s' is not a valid InfiniBand P_KEY."), p_key);
2144 			return FALSE;
2145 		}
2146 		if (p_key_int)
2147 			*p_key_int = (guint32) local_p_key_int;
2148 		return TRUE;
2149 	}
2150 	
2151 	static gboolean
2152 	check_infiniband_mode (const char *mode, GError **error)
2153 	{
2154 		if (!mode)
2155 			return TRUE;
2156 	
2157 		if (strcmp (mode, "datagram") && strcmp (mode, "connected")) {
2158 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2159 			             _("Error: 'mode': '%s' is not a valid InfiniBand transport mode [datagram, connected]."), mode);
2160 			return FALSE;
2161 		}
2162 		return TRUE;
2163 	}
2164 	
2165 	static gboolean
2166 	check_and_convert_vlan_flags (const char *flags, guint32 *flags_int, GError **error)
2167 	{
2168 		unsigned long local_flags_int;
2169 	
2170 		if (!flags)
2171 			return TRUE;
2172 	
2173 		if (!nmc_string_to_uint (flags, TRUE, 0, 7, &local_flags_int)) {
2174 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2175 			             _("Error: 'flags': '%s' is not valid; use <0-7>."), flags);
2176 			return FALSE;
2177 		}
2178 		if (flags_int)
2179 			*flags_int = (guint32) local_flags_int;
2180 		return TRUE;
2181 	}
2182 	
2183 	static gboolean
2184 	check_and_convert_vlan_prio_maps (const char *prio_map,
2185 	                                  NMVlanPriorityMap type,
2186 	                                  char ***prio_map_arr,
2187 	                                  GError **error)
2188 	{
2189 		char **local_prio_map_arr;
2190 		GError *local_err = NULL;
2191 	
2192 		if (!prio_map)
2193 			return TRUE;
2194 	
2195 		if (!(local_prio_map_arr = nmc_vlan_parse_priority_maps (prio_map, type, &local_err))) {
2196 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2197 			             _("Error: '%s': '%s' is not valid; %s "),
2198 			             type == NM_VLAN_INGRESS_MAP ? "ingress" : "egress",
2199 			             prio_map, local_err->message);
2200 			return FALSE;
2201 		}
2202 	
2203 		if (prio_map_arr)
2204 			*prio_map_arr = local_prio_map_arr;
2205 		return TRUE;
2206 	}
2207 	
2208 	static gboolean
2209 	add_ip4_address_to_connection (NMIP4Address *ip4addr, NMConnection *connection)
2210 	{
2211 		NMSettingIP4Config *s_ip4;
2212 		gboolean ret;
2213 	
2214 		if (!ip4addr)
2215 			return TRUE;
2216 	
2217 		s_ip4 = nm_connection_get_setting_ip4_config (connection);
2218 		if (!s_ip4) {
2219 			s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
2220 			nm_connection_add_setting (connection, NM_SETTING (s_ip4));
2221 			g_object_set (s_ip4,
2222 			              NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
2223 			              NULL);
2224 		}
2225 		ret = nm_setting_ip4_config_add_address (s_ip4, ip4addr);
2226 		nm_ip4_address_unref (ip4addr);
2227 	
2228 		return ret;
2229 	}
2230 	
2231 	static gboolean
2232 	add_ip6_address_to_connection (NMIP6Address *ip6addr, NMConnection *connection)
2233 	{
2234 		NMSettingIP6Config *s_ip6;
2235 		gboolean ret;
2236 	
2237 		if (!ip6addr)
2238 			return TRUE;
2239 	
2240 		s_ip6 = nm_connection_get_setting_ip6_config (connection);
2241 		if (!s_ip6) {
2242 			s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
2243 			nm_connection_add_setting (connection, NM_SETTING (s_ip6));
2244 			g_object_set (s_ip6,
2245 			              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
2246 			              NULL);
2247 		}
2248 		ret = nm_setting_ip6_config_add_address (s_ip6, ip6addr);
2249 		nm_ip6_address_unref (ip6addr);
2250 	
2251 		return ret;
2252 	}
2253 	
2254 	static char *
2255 	unique_master_iface_ifname (GSList *list,
2256 	                            const char *type,
2257 	                            const char *ifname_property,
2258 	                            const char *try_name)
2259 	{
2260 		NMConnection *connection;
2261 		NMSetting *setting;
2262 		char *new_name;
2263 		unsigned int num = 1;
2264 		GSList *iterator = list;
2265 		char *ifname_val = NULL;
2266 	
2267 		new_name = g_strdup (try_name);
2268 		while (iterator) {
2269 			connection = NM_CONNECTION (iterator->data);
2270 			setting = nm_connection_get_setting_by_name (connection, type);
2271 			if (!setting) {
2272 				iterator = g_slist_next (iterator);
2273 				continue;
2274 			}
2275 	
2276 			g_object_get (setting, ifname_property, &ifname_val, NULL);
2277 			if (g_strcmp0 (new_name, ifname_val) == 0) {
2278 				g_free (new_name);
2279 				new_name = g_strdup_printf ("%s%d", try_name, num++);
2280 				iterator = list;
2281 			} else
2282 				iterator = g_slist_next (iterator);
2283 			g_free (ifname_val);
2284 		}
2285 		return new_name;
2286 	}
2287 	
2288 	static gboolean
2289 	bridge_prop_string_to_uint (const char *str,
2290 	                            const char *nmc_arg,
2291 	                            GType bridge_type,
2292 	                            const char *propname,
2293 	                            unsigned long *out_val,
2294 	                            GError **error)
2295 	{
2296 		GParamSpecUInt *pspec;
2297 	
2298 		pspec = (GParamSpecUInt *) g_object_class_find_property (g_type_class_peek (bridge_type),
2299 		                                                         propname);
2300 		g_assert (G_IS_PARAM_SPEC_UINT (pspec));
2301 	
2302 		if (!nmc_string_to_uint (str, TRUE, pspec->minimum, pspec->maximum, out_val)) {
2303 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2304 			             _("Error: '%s': '%s' is not valid; use <%u-%u>."),
2305 			             nmc_arg, str, pspec->minimum, pspec->maximum);
2306 			return FALSE;
2307 		}
2308 		return TRUE;
2309 	}
2310 	
2311 	static void
2312 	do_questionnaire_ethernet (gboolean ethernet, char **mtu, char **mac, char **cloned_mac)
2313 	{
2314 		char *answer;
2315 		gboolean answer_bool;
2316 		gboolean once_more;
2317 		GError *error = NULL;
2318 		const char *type = ethernet ? _("ethernet") : _("Wi-Fi");
2319 	
2320 		/* Ask for optional arguments */
2321 		printf (_("There are 3 optional arguments for '%s' connection type.\n"), type);;
2322 		answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2323 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2324 			g_free (answer);
2325 			return;
2326 		}
2327 	
2328 		if (!*mtu) {
2329 			do {
2330 				*mtu = nmc_get_user_input (_("MTU [auto]: "));
2331 				once_more = !check_and_convert_mtu (*mtu, NULL, &error);
2332 				if (once_more) {
2333 					printf ("%s\n", error->message);
2334 					g_clear_error (&error);
2335 					g_free (*mtu);
2336 				}
2337 			} while (once_more);
2338 		}
2339 		if (!*mac) {
2340 			do {
2341 				*mac = nmc_get_user_input (_("MAC [none]: "));
2342 				once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error);
2343 				if (once_more) {
2344 					printf ("%s\n", error->message);
2345 					g_clear_error (&error);
2346 					g_free (*mac);
2347 				}
2348 			} while (once_more);
2349 		}
2350 		if (!*cloned_mac) {
2351 			do {
2352 				*cloned_mac = nmc_get_user_input (_("Cloned MAC [none]: "));
2353 				once_more = !check_and_convert_mac (*cloned_mac, NULL, ARPHRD_ETHER, "cloned-mac", &error);
2354 				if (once_more) {
2355 					printf ("%s\n", error->message);
2356 					g_clear_error (&error);
2357 					g_free (*cloned_mac);
2358 				}
2359 			} while (once_more);
2360 		}
2361 	
2362 		g_free (answer);
2363 		return;
2364 	}
2365 	
2366 	static void
2367 	do_questionnaire_infiniband (char **mtu, char **mac, char **mode, char **parent, char **p_key)
2368 	{
2369 		char *answer;
2370 		gboolean answer_bool;
2371 		gboolean once_more;
2372 		GError *error = NULL;
2373 	
2374 		/* Ask for optional arguments */
2375 		printf (_("There are 5 optional arguments for 'InfiniBand' connection type.\n"));
2376 		answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2377 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2378 			g_free (answer);
2379 			return;
2380 		}
2381 	
2382 		if (!*mtu) {
2383 			do {
2384 				*mtu = nmc_get_user_input (_("MTU [auto]: "));
2385 				once_more = !check_and_convert_mtu (*mtu, NULL, &error);
2386 				if (once_more) {
2387 					printf ("%s\n", error->message);
2388 					g_clear_error (&error);
2389 					g_free (*mtu);
2390 				}
2391 			} while (once_more);
2392 		}
2393 		if (!*mac) {
2394 			do {
2395 				*mac = nmc_get_user_input (_("MAC [none]: "));
2396 				once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_INFINIBAND, "mac", &error);
2397 				if (once_more) {
2398 					printf ("%s\n", error->message);
2399 					g_clear_error (&error);
2400 					g_free (*mac);
2401 				}
2402 			} while (once_more);
2403 		}
2404 		if (!*mode) {
2405 			do {
2406 				*mode = nmc_get_user_input (_("Transport mode (datagram or connected) [datagram]: "));
2407 				if (!*mode)
2408 					*mode = g_strdup ("datagram");
2409 				once_more = !check_infiniband_mode (*mode, &error);
2410 				if (once_more) {
2411 					printf ("%s\n", error->message);
2412 					g_clear_error (&error);
2413 					g_free (*mode);
2414 				}
2415 			} while (once_more);
2416 		}
2417 		if (!*parent) {
2418 			do {
2419 				*parent = nmc_get_user_input (_("Parent interface [none]: "));
2420 				once_more = !check_infiniband_parent (*parent, &error);
2421 				if (once_more) {
2422 					printf ("%s\n", error->message);
2423 					g_clear_error (&error);
2424 					g_free (*parent);
2425 				}
2426 			} while (once_more);
2427 		}
2428 		if (!*p_key) {
2429 			do {
2430 				*p_key = nmc_get_user_input (_("P_KEY [none]: "));
2431 				once_more = !check_infiniband_p_key (*p_key, NULL, &error);
2432 				if (once_more) {
2433 					printf ("%s\n", error->message);
2434 					g_clear_error (&error);
2435 					g_free (*p_key);
2436 				}
2437 				/* If parent is specified, so has to be P_KEY */
2438 				if (!once_more && *parent && !*p_key) {
2439 					once_more = TRUE;
2440 					printf (_("Error: 'p-key' is mandatory when 'parent' is specified.\n"));
2441 				}
2442 			} while (once_more);
2443 		}
2444 	
2445 		g_free (answer);
2446 		return;
2447 	}
2448 	
2449 	static void
2450 	do_questionnaire_wifi (char **mtu, char **mac, char **cloned_mac)
2451 	{
2452 		/* At present, the optional Wi-Fi arguments are the same as for ethernet. */
2453 		return do_questionnaire_ethernet (FALSE, mtu, mac, cloned_mac);
2454 	}
2455 	
2456 	static void
2457 	do_questionnaire_wimax (char **mac)
2458 	{
2459 		char *answer;
2460 		gboolean answer_bool;
2461 		gboolean once_more;
2462 		GError *error = NULL;
2463 	
2464 		/* Ask for optional 'wimax' arguments. */
2465 		printf (_("There is 1 optional argument for 'WiMax' connection type.\n"));
2466 		answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] "));
2467 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2468 			g_free (answer);
2469 			return;
2470 		}
2471 	
2472 		if (!*mac) {
2473 			do {
2474 				*mac = nmc_get_user_input (_("MAC [none]: "));
2475 				once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error);
2476 				if (once_more) {
2477 					printf ("%s\n", error->message);
2478 					g_clear_error (&error);
2479 					g_free (*mac);
2480 				}
2481 			} while (once_more);
2482 		}
2483 	
2484 		g_free (answer);
2485 		return;
2486 	}
2487 	
2488 	static void
2489 	do_questionnaire_mobile (char **user, char **password)
2490 	{
2491 		char *answer;
2492 		gboolean answer_bool;
2493 	
2494 		/* Ask for optional 'gsm' or 'cdma' arguments. */
2495 		printf (_("There are 2 optional arguments for 'mobile broadband' connection type.\n"));
2496 		answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2497 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2498 			g_free (answer);
2499 			return;
2500 		}
2501 	
2502 		if (!*user)
2503 			*user = nmc_get_user_input (_("Username [none]: "));
2504 		if (!*password)
2505 			*password = nmc_get_user_input (_("Password [none]: "));
2506 	
2507 		g_free (answer);
2508 		return;
2509 	}
2510 	
2511 	static void
2512 	do_questionnaire_bluetooth (char **bt_type)
2513 	{
2514 		char *answer;
2515 		gboolean answer_bool;
2516 		gboolean once_more;
2517 	
2518 		/* Ask for optional 'bluetooth' arguments. */
2519 		printf (_("There is 1 optional argument for 'bluetooth' connection type.\n"));
2520 		answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] "));
2521 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2522 			g_free (answer);
2523 			return;
2524 		}
2525 	
2526 		if (!*bt_type) {
2527 			do {
2528 				*bt_type = nmc_get_user_input (_("Bluetooth type (panu, dun-gsm or dun-cdma) [panu]: "));
2529 				if (!*bt_type)
2530 					*bt_type = g_strdup ("panu");
2531 				once_more =    strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
2532 				            && strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")
2533 				            && strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")
2534 				            && strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU);
2535 				if (once_more) {
2536 					printf (_("Error: 'bt-type': '%s' is not a valid bluetooth type.\n"), *bt_type);
2537 					g_free (*bt_type);
2538 				}
2539 			} while (once_more);
2540 		}
2541 	
2542 		g_free (answer);
2543 		return;
2544 	}
2545 	
2546 	static void
2547 	do_questionnaire_vlan (char **mtu, char **flags, char **ingress, char **egress)
2548 	{
2549 		char *answer;
2550 		gboolean answer_bool;
2551 		gboolean once_more;
2552 		GError *error = NULL;
2553 	
2554 		/* Ask for optional 'vlan' arguments. */
2555 		printf (_("There are 4 optional arguments for 'VLAN' connection type.\n"));
2556 		answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2557 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2558 			g_free (answer);
2559 			return;
2560 		}
2561 	
2562 		if (!*mtu) {
2563 			do {
2564 				*mtu = nmc_get_user_input (_("MTU [auto]: "));
2565 				once_more = !check_and_convert_mtu (*mtu, NULL, &error);
2566 				if (once_more) {
2567 					printf ("%s\n", error->message);
2568 					g_clear_error (&error);
2569 					g_free (*mtu);
2570 				}
2571 			} while (once_more);
2572 		}
2573 		if (!*flags) {
2574 			do {
2575 				*flags = nmc_get_user_input (_("VLAN flags (<0-7>) [none]: "));
2576 				once_more = !check_and_convert_vlan_flags (*flags, NULL, &error);
2577 				if (once_more) {
2578 					printf ("%s\n", error->message);
2579 					g_clear_error (&error);
2580 					g_free (*flags);
2581 				}
2582 			} while (once_more);
2583 		}
2584 		if (!*ingress) {
2585 			do {
2586 				*ingress = nmc_get_user_input (_("Ingress priority maps [none]: "));
2587 				once_more = !check_and_convert_vlan_prio_maps (*ingress, NM_VLAN_INGRESS_MAP, NULL, &error);
2588 				if (once_more) {
2589 					printf ("%s\n", error->message);
2590 					g_clear_error (&error);
2591 					g_free (*ingress);
2592 				}
2593 			} while (once_more);
2594 		}
2595 		if (!*egress) {
2596 			do {
2597 				*egress = nmc_get_user_input (_("Egress priority maps [none]: "));
2598 				once_more = !check_and_convert_vlan_prio_maps (*egress, NM_VLAN_EGRESS_MAP, NULL, &error);
2599 				if (once_more) {
2600 					printf ("%s\n", error->message);
2601 					g_clear_error (&error);
2602 					g_free (*egress);
2603 				}
2604 			} while (once_more);
2605 		}
2606 	
2607 		g_free (answer);
2608 		return;
2609 	}
2610 	
2611 	static void
2612 	do_questionnaire_bond (char **mode, char **primary, char **miimon,
2613 	                       char **downdelay, char **updelay,
2614 	                       char **arpinterval, char **arpiptarget)
2615 	{
2616 		char *answer, *monitor_mode;
2617 		gboolean answer_bool;
2618 		unsigned long tmp;
2619 		gboolean once_more;
2620 		GError *error = NULL;
2621 	
2622 		/* Ask for optional 'bond' arguments. */
2623 		printf (_("There are optional arguments for 'bond' connection type.\n"));
2624 		answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2625 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2626 			g_free (answer);
2627 			return;
2628 		}
2629 	
2630 		if (!*mode) {
2631 			const char *mode_tmp;
2632 			do {
2633 				*mode = nmc_get_user_input (_("Bonding mode [balance-rr]: "));
2634 				if (!*mode)
2635 					*mode = g_strdup ("balance-rr");
2636 				mode_tmp = nmc_bond_validate_mode (*mode, &error);
2637 				g_free (*mode);
2638 				if (mode_tmp) {
2639 					*mode = g_strdup (mode_tmp);
2640 				} else {
2641 					printf ("%s\n", error->message);
2642 					g_clear_error (&error);
2643 				}
2644 			} while (!mode_tmp);
2645 		}
2646 	
2647 		if (g_strcmp0 (*mode, "active-backup") == 0 && !*primary) {
2648 			do {
2649 				*primary = nmc_get_user_input (_("Bonding primary interface [none]: "));
2650 				once_more = *primary && !nm_utils_iface_valid_name (*primary);
2651 				if (once_more) {
2652 					printf (_("Error: 'primary': '%s' is not a valid interface name.\n"),
2653 					        *primary);
2654 					g_free (*primary);
2655 				}
2656 			} while (once_more);
2657 		}
2658 	
2659 		do {
2660 			monitor_mode = nmc_get_user_input (_("Bonding monitoring mode (miimon or arp) [miimon]: "));
2661 			if (!monitor_mode)
2662 				monitor_mode = g_strdup ("miimon");
2663 			once_more = strcmp (monitor_mode, "miimon") && strcmp (monitor_mode, "arp");
2664 			if (once_more) {
2665 				printf (_("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"),
2666 				        monitor_mode, "miimon", "arp");
2667 				g_free (monitor_mode);
2668 			}
2669 		} while (once_more);
2670 	
2671 		if (strcmp (monitor_mode, "miimon") == 0) {
2672 			if (!*miimon) {
2673 				do {
2674 					*miimon = nmc_get_user_input (_("Bonding miimon [100]: "));
2675 					once_more = *miimon && !nmc_string_to_uint (*miimon, TRUE, 0, G_MAXUINT32, &tmp);
2676 					if (once_more) {
2677 						printf (_("Error: 'miimon': '%s' is not a valid number <0-%u>.\n"),
2678 						        *miimon, G_MAXUINT32);
2679 						g_free (*miimon);
2680 					}
2681 				} while (once_more);
2682 			}
2683 			if (!*downdelay) {
2684 				do {
2685 					*downdelay = nmc_get_user_input (_("Bonding downdelay [0]: "));
2686 					once_more = *downdelay && !nmc_string_to_uint (*downdelay, TRUE, 0, G_MAXUINT32, &tmp);
2687 					if (once_more) {
2688 						printf (_("Error: 'downdelay': '%s' is not a valid number <0-%u>.\n"),
2689 						        *downdelay, G_MAXUINT32);
2690 						g_free (*downdelay);
2691 					}
2692 				} while (once_more);
2693 			}
2694 			if (!*updelay) {
2695 				do {
2696 					*updelay = nmc_get_user_input (_("Bonding updelay [0]: "));
2697 					once_more = *updelay && !nmc_string_to_uint (*updelay, TRUE, 0, G_MAXUINT32, &tmp);
2698 					if (once_more) {
2699 						printf (_("Error: 'updelay': '%s' is not a valid number <0-%u>.\n"),
2700 						        *updelay, G_MAXUINT32);
2701 						g_free (*updelay);
2702 					}
2703 				} while (once_more);
2704 			}
2705 		} else {
2706 			if (!*arpinterval) {
2707 				do {
2708 					*arpinterval = nmc_get_user_input (_("Bonding arp-interval [0]: "));
2709 					once_more = *arpinterval && !nmc_string_to_uint (*arpinterval, TRUE, 0, G_MAXUINT32, &tmp);
2710 					if (once_more) {
2711 						printf (_("Error: 'arp-interval': '%s' is not a valid number <0-%u>.\n"),
2712 						        *arpinterval, G_MAXUINT32);
2713 						g_free (*arpinterval);
2714 					}
2715 				} while (once_more);
2716 			}
2717 			if (!*arpiptarget) {
2718 				//FIXME: verify the string
2719 				*arpiptarget = nmc_get_user_input (_("Bonding arp-ip-target [none]: "));
2720 			}
2721 		}
2722 	
2723 		g_free (answer);
2724 		g_free (monitor_mode);
2725 		return;
2726 	}
2727 	
2728 	static void
2729 	do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay,
2730 	                         char **hello_time, char **max_age, char **ageing_time)
2731 	{
2732 		char *answer;
2733 		gboolean answer_bool;
2734 		unsigned long tmp;
2735 		gboolean once_more;
2736 		GError *error = NULL;
2737 	
2738 		/* Ask for optional 'bridge' arguments. */
2739 		printf (_("There are 6 optional arguments for 'bridge' connection type.\n"));
2740 		answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2741 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2742 			g_free (answer);
2743 			return;
2744 		}
2745 	
2746 		if (!*stp) {
2747 			gboolean stp_bool;
2748 			do {
2749 				*stp = nmc_get_user_input (_("Enable STP (yes/no) [yes]: "));
2750 				*stp = *stp ? *stp : g_strdup ("yes");
2751 				once_more = !nmc_string_to_bool (*stp, &stp_bool, &error);
2752 				if (once_more) {
2753 					printf (_("Error: 'stp': '%s'.\n"), error->message);
2754 					g_clear_error (&error);
2755 					g_free (*stp);
2756 				}
2757 			} while (once_more);
2758 		}
2759 		if (!*priority) {
2760 			do {
2761 				*priority = nmc_get_user_input (_("STP priority [128]: "));
2762 				*priority = *priority ? *priority : g_strdup ("128");
2763 				once_more = !nmc_string_to_uint (*priority, TRUE, 0, G_MAXUINT16, &tmp);
2764 				if (once_more) {
2765 					printf (_("Error: 'priority': '%s' is not a valid number <0-%d>.\n"),
2766 					        *priority, G_MAXUINT16);
2767 					g_free (*priority);
2768 				}
2769 			} while (once_more);
2770 		}
2771 		if (!*fwd_delay) {
2772 			do {
2773 				*fwd_delay = nmc_get_user_input (_("Forward delay [15]: "));
2774 				*fwd_delay = *fwd_delay ? *fwd_delay : g_strdup ("15");
2775 				once_more = !nmc_string_to_uint (*fwd_delay, TRUE, 2, 30, &tmp);
2776 				if (once_more) {
2777 					printf (_("Error: 'forward-delay': '%s' is not a valid number <2-30>.\n"),
2778 					        *fwd_delay);
2779 					g_free (*fwd_delay);
2780 				}
2781 			} while (once_more);
2782 		}
2783 	
2784 		if (!*hello_time) {
2785 			do {
2786 				*hello_time = nmc_get_user_input (_("Hello time [2]: "));
2787 				*hello_time = *hello_time ? *hello_time : g_strdup ("2");
2788 				once_more = !nmc_string_to_uint (*hello_time, TRUE, 1, 10, &tmp);
2789 				if (once_more) {
2790 					printf (_("Error: 'hello-time': '%s' is not a valid number <1-10>.\n"),
2791 					        *hello_time);
2792 					g_free (*hello_time);
2793 				}
2794 			} while (once_more);
2795 		}
2796 		if (!*max_age) {
2797 			do {
2798 				*max_age = nmc_get_user_input (_("Max age [20]: "));
2799 				*max_age = *max_age ? *max_age : g_strdup ("20");
2800 				once_more = !nmc_string_to_uint (*max_age, TRUE, 6, 40, &tmp);
2801 				if (once_more) {
2802 					printf (_("Error: 'max-age': '%s' is not a valid number <6-40>.\n"),
2803 					        *max_age);
2804 					g_free (*max_age);
2805 				}
2806 			} while (once_more);
2807 		}
2808 		if (!*ageing_time) {
2809 			do {
2810 				*ageing_time = nmc_get_user_input (_("MAC address ageing time [300]: "));
2811 				*ageing_time = *ageing_time ? *ageing_time : g_strdup ("300");
2812 				once_more = !nmc_string_to_uint (*ageing_time, TRUE, 0, 1000000, &tmp);
2813 				if (once_more) {
2814 					printf (_("Error: 'ageing-time': '%s' is not a valid number <0-1000000>.\n"),
2815 					        *ageing_time);
2816 					g_free (*ageing_time);
2817 				}
2818 			} while (once_more);
2819 		}
2820 	
2821 		g_free (answer);
2822 		return;
2823 	}
2824 	
2825 	static void
2826 	do_questionnaire_bridge_slave (char **priority, char **path_cost, char **hairpin)
2827 	{
2828 		char *answer;
2829 		gboolean answer_bool;
2830 		unsigned long tmp;
2831 		gboolean once_more;
2832 		GError *error = NULL;
2833 	
2834 		/* Ask for optional 'bridge-slave' arguments. */
2835 		printf (_("There are 3 optional arguments for 'bridge-slave' connection type.\n"));
2836 		answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2837 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2838 			g_free (answer);
2839 			return;
2840 		}
2841 	
2842 		if (!*priority) {
2843 			do {
2844 				*priority = nmc_get_user_input (_("Bridge port priority [32]: "));
2845 				*priority = *priority ? *priority : g_strdup ("32");
2846 				once_more = !bridge_prop_string_to_uint (*priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
2847 				                                         NM_SETTING_BRIDGE_PORT_PRIORITY, &tmp, &error);
2848 				if (once_more) {
2849 					printf ("%s\n", error->message);
2850 					g_clear_error (&error);
2851 					g_free (*priority);
2852 				}
2853 			} while (once_more);
2854 		}
2855 		if (!*path_cost) {
2856 			do {
2857 				*path_cost = nmc_get_user_input (_("Bridge port STP path cost [100]: "));
2858 				*path_cost = *path_cost ? *path_cost : g_strdup ("100");
2859 				once_more = !bridge_prop_string_to_uint (*path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
2860 				                                         NM_SETTING_BRIDGE_PORT_PATH_COST, &tmp, &error);
2861 				if (once_more) {
2862 					printf ("%s\n", error->message);
2863 					g_clear_error (&error);
2864 					g_free (*path_cost);
2865 				}
2866 			} while (once_more);
2867 		}
2868 		if (!*hairpin) {
2869 			gboolean hairpin_bool;
2870 			do {
2871 				*hairpin = nmc_get_user_input (_("Hairpin (yes/no) [yes]: "));
2872 				*hairpin = *hairpin ? *hairpin : g_strdup ("yes");
2873 				once_more = !nmc_string_to_bool (*hairpin, &hairpin_bool, &error);
2874 				if (once_more) {
2875 					printf (_("Error: 'hairpin': '%s'.\n"), error->message);
2876 					g_clear_error (&error);
2877 					g_free (*hairpin);
2878 				}
2879 			} while (once_more);
2880 		}
2881 	
2882 		g_free (answer);
2883 		return;
2884 	}
2885 	
2886 	static void
2887 	do_questionnaire_vpn (char **user)
2888 	{
2889 		char *answer;
2890 		gboolean answer_bool;
2891 	
2892 		/* Ask for optional 'vpn' arguments. */
2893 		printf (_("There is 1 optional argument for 'VPN' connection type.\n"));
2894 		answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] "));
2895 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2896 			g_free (answer);
2897 			return;
2898 		}
2899 	
2900 		if (!*user)
2901 			*user = nmc_get_user_input (_("Username [none]: "));
2902 	
2903 		g_free (answer);
2904 		return;
2905 	}
2906 	
2907 	static void
2908 	do_questionnaire_olpc (char **channel, char **dhcp_anycast)
2909 	{
2910 		char *answer;
2911 		gboolean answer_bool;
2912 		unsigned long tmp;
2913 		gboolean once_more;
2914 		GError *error = NULL;
2915 	
2916 		/* Ask for optional 'olpc' arguments. */
2917 		printf (_("There are 2 optional arguments for 'OLPC Mesh' connection type.\n"));
2918 		answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2919 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2920 			g_free (answer);
2921 			return;
2922 		}
2923 	
2924 		if (!*channel) {
2925 			do {
2926 				*channel = nmc_get_user_input (_("OLPC Mesh channel [1]: "));
2927 				once_more = *channel && !nmc_string_to_uint (*channel, TRUE, 1, 13, &tmp);
2928 				if (once_more) {
2929 					printf (_("Error: 'channel': '%s' is not a valid number <1-13>.\n"),
2930 					        *channel);
2931 					g_free (*channel);
2932 				}
2933 			} while (once_more);
2934 		}
2935 		if (!*dhcp_anycast) {
2936 			do {
2937 				*dhcp_anycast = nmc_get_user_input (_("DHCP anycast MAC address [none]: "));
2938 				once_more = !check_and_convert_mac (*dhcp_anycast, NULL, ARPHRD_ETHER, "dhcp-anycast", &error);
2939 				if (once_more) {
2940 					printf ("%s\n", error->message);
2941 					g_clear_error (&error);
2942 					g_free (*dhcp_anycast);
2943 				}
2944 			} while (once_more);
2945 		}
2946 	
2947 		g_free (answer);
2948 		return;
2949 	}
2950 	
2951 	static gboolean
2952 	split_address (char* str, char **ip, char **gw, char **rest)
2953 	{
2954 		size_t n1, n2, n3, n4, n5;
2955 	
2956 		*ip = *gw = *rest = NULL;
2957 		if (!str)
2958 			return FALSE;
2959 	
2960 		n1 = strspn  (str,    " \t");
2961 		n2 = strcspn (str+n1, " \t\0") + n1;
2962 		n3 = strspn  (str+n2, " \t")   + n2;
2963 		n4 = strcspn (str+n3, " \t\0") + n3;
2964 		n5 = strspn  (str+n4, " \t")   + n4;
2965 	
2966 		str[n2] = str[n4] = '\0';
2967 		*ip = str[n1] ? str + n1 : NULL;
2968 		*gw = str[n3] ? str + n3 : NULL;
2969 		*rest = str[n5] ? str + n5 : NULL;
2970 	
2971 		return TRUE;
2972 	}
2973 	
2974 	static void
2975 	ask_for_ip_addresses (NMConnection *connection, int family)
2976 	{
2977 		gboolean ip_loop;
2978 		GError *error = NULL;
2979 		char *str, *ip, *gw, *rest;
2980 		const char *prompt;
2981 		gboolean added;
2982 		gpointer ipaddr;
2983 	
2984 		if (family == 4)
2985 			prompt =_("IPv4 address (IP[/plen] [gateway]) [none]: ");
2986 		else
2987 			prompt =_("IPv6 address (IP[/plen] [gateway]) [none]: ");
2988 	
2989 		ip_loop = TRUE;
2990 		do {
2991 			str = nmc_get_user_input (prompt);
2992 			split_address (str, &ip, &gw, &rest);
2993 			if (ip) {
2994 				if (family == 4)
2995 					ipaddr = nmc_parse_and_build_ip4_address (ip, gw, &error);
2996 				else
2997 					ipaddr = nmc_parse_and_build_ip6_address (ip, gw, &error);
2998 				if (ipaddr) {
2999 					if (family == 4)
3000 						added = add_ip4_address_to_connection ((NMIP4Address *) ipaddr, connection);
3001 					else
3002 						added = add_ip6_address_to_connection ((NMIP6Address *) ipaddr, connection);
3003 					gw = gw ? gw : (family == 4) ? "0.0.0.0" : "::";
3004 					if (added)
3005 						printf (_("  Address successfully added: %s %s\n"), ip, gw);
3006 					else
3007 						printf (_("  Warning: address already present: %s %s\n"), ip, gw);
3008 					if (rest)
3009 						printf (_("  Warning: ignoring garbage at the end: '%s'\n"), rest);
3010 				} else {
3011 					g_prefix_error (&error, _("Error: "));
3012 					printf ("%s\n", error->message);
3013 					g_clear_error (&error);
3014 				}
3015 			} else
3016 				ip_loop = FALSE;
3017 	
3018 			g_free (str);
3019 		} while (ip_loop);
3020 	}
3021 	
3022 	static void
3023 	do_questionnaire_ip (NMConnection *connection)
3024 	{
3025 		char *answer;
3026 		gboolean answer_bool;
3027 	
3028 		/* Ask for IP addresses */
3029 		answer = nmc_get_user_input (_("Do you want to add IP addresses? (yes/no) [yes] "));
3030 		if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
3031 			g_free (answer);
3032 			return;
3033 		}
3034 	
3035 		printf (_("Press <Enter> to finish adding addresses.\n"));
3036 	
3037 		ask_for_ip_addresses (connection, 4);
3038 		ask_for_ip_addresses (connection, 6);
3039 	
3040 		g_free (answer);
3041 		return;
3042 	}
3043 	
3044 	static gboolean
3045 	complete_connection_by_type (NMConnection *connection,
3046 	                             const char *con_type,
3047 	                             GSList *all_connections,
3048 	                             gboolean ask,
3049 	                             int argc,
3050 	                             char **argv,
3051 	                             GError **error)
3052 	{
3053 		NMSettingConnection *s_con;
3054 		NMSettingWired *s_wired;
3055 		NMSettingInfiniband *s_infiniband;
3056 		NMSettingWireless *s_wifi;
3057 		NMSettingWimax *s_wimax;
3058 		NMSettingGsm *s_gsm;
3059 		NMSettingCdma *s_cdma;
3060 		NMSettingBluetooth *s_bt;
3061 		NMSettingVlan *s_vlan;
3062 		NMSettingBond *s_bond;
3063 		NMSettingTeam *s_team;
3064 		NMSettingTeamPort *s_team_port;
3065 		NMSettingBridge *s_bridge;
3066 		NMSettingBridgePort *s_bridge_port;
3067 		NMSettingVPN *s_vpn;
3068 		NMSettingOlpcMesh *s_olpc_mesh;
3069 	
3070 		g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3071 	
3072 		s_con = nm_connection_get_setting_connection (connection);
3073 		g_assert (s_con);
3074 	
3075 		if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) {
3076 			/* Build up the settings required for 'ethernet' */
3077 			gboolean success = FALSE;
3078 			const char *mtu_c = NULL;
3079 			char *mtu = NULL;
3080 			guint32 mtu_int = 0;
3081 			const char *mac_c = NULL;
3082 			char *mac = NULL;
3083 			const char *cloned_mac_c = NULL;
3084 			char *cloned_mac = NULL;
3085 			GByteArray *array = NULL;
3086 			GByteArray *cloned_array = NULL;
3087 			nmc_arg_t exp_args[] = { {"mtu",        TRUE, &mtu_c,        FALSE},
3088 			                         {"mac",        TRUE, &mac_c,        FALSE},
3089 			                         {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
3090 			                         {NULL} };
3091 	
3092 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3093 				return FALSE;
3094 	
3095 			/* Also ask for all optional arguments if '--ask' is specified. */
3096 			mtu = mtu_c ? g_strdup (mtu_c) : NULL;
3097 			mac = mac_c ? g_strdup (mac_c) : NULL;
3098 			cloned_mac = cloned_mac_c ? g_strdup (cloned_mac_c) : NULL;
3099 			if (ask)
3100 				do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac);
3101 	
3102 			if (!check_and_convert_mtu (mtu, &mtu_int, error))
3103 				goto cleanup_wired;
3104 			if (!check_and_convert_mac (mac, &array, ARPHRD_ETHER, "mac", error))
3105 				goto cleanup_wired;
3106 			if (!check_and_convert_mac (cloned_mac, &cloned_array, ARPHRD_ETHER, "cloned-mac", error))
3107 				goto cleanup_wired;
3108 	
3109 			/* Add ethernet setting */
3110 			s_wired = (NMSettingWired *) nm_setting_wired_new ();
3111 			nm_connection_add_setting (connection, NM_SETTING (s_wired));
3112 	
3113 			if (mtu)
3114 				g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
3115 			if (array)
3116 				g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, array, NULL);
3117 			if (cloned_array)
3118 				g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_array, NULL);
3119 	
3120 			success = TRUE;
3121 	cleanup_wired:
3122 			g_free (mtu);
3123 			g_free (mac);
3124 			g_free (cloned_mac);
3125 			if (array)
3126 				g_byte_array_free (array, TRUE);
3127 			if (cloned_array)
3128 				g_byte_array_free (cloned_array, TRUE);
3129 			if (!success)
3130 				return FALSE;
3131 	
3132 		} else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
3133 			/* Build up the settings required for 'infiniband' */
3134 			gboolean success = FALSE;
3135 			const char *mtu_c = NULL;
3136 			char *mtu = NULL;
3137 			guint32 mtu_int = 0;
3138 			const char *mac_c = NULL;
3139 			char *mac = NULL;
3140 			GByteArray *array = NULL;
3141 			const char *mode_c = NULL;
3142 			char *mode = NULL;
3143 			const char *parent_c = NULL;
3144 			char *parent = NULL;
3145 			const char *p_key_c = NULL;
3146 			char *p_key = NULL;
3147 			guint32 p_key_int = 0;
3148 			nmc_arg_t exp_args[] = { {"mtu",            TRUE, &mtu_c,    FALSE},
3149 			                         {"mac",            TRUE, &mac_c,    FALSE},
3150 			                         {"transport-mode", TRUE, &mode_c,   FALSE},
3151 			                         {"parent",         TRUE, &parent_c, FALSE},
3152 			                         {"p-key",          TRUE, &p_key_c,  FALSE},
3153 			                         {NULL} };
3154 	
3155 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3156 				return FALSE;
3157 	
3158 			/* Also ask for all optional arguments if '--ask' is specified. */
3159 			mtu = mtu_c ? g_strdup (mtu_c) : NULL;
3160 			mac = mac_c ? g_strdup (mac_c) : NULL;
3161 			mode = mode_c ? g_strdup (mode_c) : NULL;
3162 			parent = parent_c ? g_strdup (parent_c) : NULL;
3163 			p_key = p_key_c ? g_strdup (p_key_c) : NULL;
3164 			if (ask)
3165 				do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key);
3166 	
3167 			if (!check_and_convert_mtu (mtu, &mtu_int, error))
3168 				goto cleanup_ib;
3169 			if (!check_and_convert_mac (mac, &array, ARPHRD_INFINIBAND, "mac", error))
3170 				goto cleanup_ib;
3171 			if (!check_infiniband_mode (mode, error))
3172 				goto cleanup_ib;
3173 			if (p_key) {
3174 				if (!check_infiniband_p_key (p_key, &p_key_int, error))
3175 					goto cleanup_ib;
3176 				if (!check_infiniband_parent (parent, error))
3177 					goto cleanup_ib;
3178 			} else if (parent) {
3179 				g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3180 				             _("Error: 'parent': not valid without 'p-key'."));
3181 				goto cleanup_ib;
3182 			}
3183 	
3184 			/* Add 'infiniband' setting */
3185 			s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
3186 			nm_connection_add_setting (connection, NM_SETTING (s_infiniband));
3187 	
3188 			g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL);
3189 			if (mtu)
3190 				g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL);
3191 			if (array) {
3192 				g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, array, NULL);
3193 				g_byte_array_free (array, TRUE);
3194 			}
3195 			if (p_key)
3196 				g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL);
3197 			if (parent)
3198 				g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL);
3199 	
3200 	
3201 			success = TRUE;
3202 	cleanup_ib:
3203 			g_free (mtu);
3204 			g_free (mac);
3205 			g_free (mode);
3206 			g_free (parent);
3207 			g_free (p_key);
3208 			if (!success)
3209 				return FALSE;
3210 	
3211 		} else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) {
3212 			/* Build up the settings required for 'wifi' */
3213 			gboolean success = FALSE;
3214 			char *ssid_ask = NULL;
3215 			const char *ssid = NULL;
3216 			GByteArray *ssid_arr = NULL;
3217 			const char *mtu_c = NULL;
3218 			char *mtu = NULL;
3219 			guint32 mtu_int = 0;
3220 			const char *mac_c = NULL;
3221 			char *mac = NULL;
3222 			GByteArray *mac_array = NULL;
3223 			const char *cloned_mac_c = NULL;
3224 			char *cloned_mac = NULL;
3225 			GByteArray *cloned_mac_array = NULL;
3226 			nmc_arg_t exp_args[] = { {"ssid",       TRUE, &ssid,         !ask},
3227 			                         {"mtu",        TRUE, &mtu_c,        FALSE},
3228 			                         {"mac",        TRUE, &mac_c,        FALSE},
3229 			                         {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
3230 			                         {NULL} };
3231 	
3232 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3233 				return FALSE;
3234 	
3235 			if (!ssid && ask)
3236 				ssid = ssid_ask = nmc_get_user_input (_("SSID: "));
3237 			if (!ssid) {
3238 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3239 				                     _("Error: 'ssid' is required."));
3240 				return FALSE;
3241 			}
3242 	
3243 			/* Also ask for all optional arguments if '--ask' is specified. */
3244 			mtu = mtu_c ? g_strdup (mtu_c) : NULL;
3245 			mac = mac_c ? g_strdup (mac_c) : NULL;
3246 			cloned_mac = cloned_mac_c ? g_strdup (cloned_mac_c) : NULL;
3247 			if (ask)
3248 				do_questionnaire_wifi (&mtu, &mac, &cloned_mac);
3249 	
3250 			if (!check_and_convert_mtu (mtu, &mtu_int, error))
3251 				goto cleanup_wifi;
3252 			if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error))
3253 				goto cleanup_wifi;
3254 			if (!check_and_convert_mac (cloned_mac, &cloned_mac_array, ARPHRD_ETHER, "cloned-mac", error))
3255 				goto cleanup_wifi;
3256 	
3257 			/* Add wifi setting */
3258 			s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
3259 			nm_connection_add_setting (connection, NM_SETTING (s_wifi));
3260 	
3261 			ssid_arr = g_byte_array_sized_new (strlen (ssid));
3262 			g_byte_array_append (ssid_arr, (const guint8 *) ssid, strlen (ssid));
3263 			g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_arr, NULL);
3264 	
3265 			if (mtu)
3266 				g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL);
3267 			if (mac_array)
3268 				g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac_array, NULL);
3269 			if (cloned_mac_array)
3270 				g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac_array, NULL);
3271 	
3272 			success = TRUE;
3273 	cleanup_wifi:
3274 			g_free (ssid_ask);
3275 			g_free (mtu);
3276 			g_free (mac);
3277 			g_free (cloned_mac);
3278 			if (ssid_arr)
3279 				g_byte_array_free (ssid_arr, TRUE);
3280 			if (mac_array)
3281 				g_byte_array_free (mac_array, TRUE);
3282 			if (cloned_mac_array)
3283 				g_byte_array_free (cloned_mac_array, TRUE);
3284 			if (!success)
3285 				return FALSE;
3286 	
3287 		} else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) {
3288 			/* Build up the settings required for 'wimax' */
3289 			gboolean success = FALSE;
3290 			const char *nsp_name = NULL;
3291 			char *nsp_name_ask = NULL;
3292 			const char *mac_c = NULL;
3293 			char *mac = NULL;
3294 			GByteArray *mac_array = NULL;
3295 			nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask},
3296 			                         {"mac", TRUE, &mac_c,    FALSE},
3297 			                         {NULL} };
3298 	
3299 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3300 				return FALSE;
3301 	
3302 			if (!nsp_name && ask)
3303 				nsp_name = nsp_name_ask = nmc_get_user_input (_("WiMAX NSP name: "));
3304 			if (!nsp_name) {
3305 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3306 				                     _("Error: 'nsp' is required."));
3307 				goto cleanup_wimax;
3308 			}
3309 	
3310 			/* Also ask for all optional arguments if '--ask' is specified. */
3311 			mac = mac_c ? g_strdup (mac_c) : NULL;
3312 			if (ask)
3313 				do_questionnaire_wimax (&mac);
3314 	
3315 			if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error))
3316 				goto cleanup_wimax;
3317 	
3318 			/* Add 'wimax' setting */
3319 			s_wimax = (NMSettingWimax *) nm_setting_wimax_new ();
3320 			nm_connection_add_setting (connection, NM_SETTING (s_wimax));
3321 			g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL);
3322 	
3323 			if (mac_array) {
3324 				g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac_array, NULL);
3325 				g_byte_array_free (mac_array, TRUE);
3326 			}
3327 	
3328 			success = TRUE;
3329 	cleanup_wimax:
3330 			g_free (nsp_name_ask);
3331 			g_free (mac);
3332 			if (!success)
3333 				return FALSE;
3334 	
3335 		} else if (   !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME)
3336 		           || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) {
3337 			/* Build up the settings required for 'gsm' or 'cdma' mobile broadband */
3338 			gboolean success = FALSE;
3339 			const char *apn = NULL;
3340 			char *apn_ask = NULL;
3341 			const char *user_c = NULL;
3342 			char *user = NULL;
3343 			const char *password_c = NULL;
3344 			char *password = NULL;
3345 			gboolean is_gsm;
3346 			int i = 0;
3347 			nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */
3348 			                         {NULL} };
3349 	
3350 			is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME);
3351 	
3352 			if (is_gsm)
3353 				gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask};
3354 			gsm_args[i++] = (nmc_arg_t) {"user",     TRUE, &user_c,     FALSE};
3355 			gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE};
3356 			gsm_args[i++] = (nmc_arg_t) {NULL};
3357 	
3358 			if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error))
3359 				return FALSE;
3360 	
3361 			if (!apn && ask && is_gsm)
3362 				apn = apn_ask = nmc_get_user_input (_("APN: "));
3363 			if (!apn && is_gsm) {
3364 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3365 				                     _("Error: 'apn' is required."));
3366 				goto cleanup_mobile;
3367 			}
3368 	
3369 			/* Also ask for all optional arguments if '--ask' is specified. */
3370 			user = user_c ? g_strdup (user_c) : NULL;
3371 			password = password_c ? g_strdup (password_c) : NULL;
3372 			if (ask)
3373 				do_questionnaire_mobile (&user, &password);
3374 	
3375 			if (is_gsm) {
3376 				g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL);
3377 	
3378 				/* Add 'gsm' setting */
3379 				s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
3380 				nm_connection_add_setting (connection, NM_SETTING (s_gsm));
3381 				g_object_set (s_gsm,
3382 				              NM_SETTING_GSM_NUMBER, "*99#",
3383 				              NM_SETTING_GSM_APN, apn,
3384 				              NM_SETTING_GSM_USERNAME, user,
3385 				              NM_SETTING_GSM_PASSWORD, password,
3386 				              NULL);
3387 				g_free (apn_ask);
3388 			} else {
3389 				g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL);
3390 	
3391 				/* Add 'cdma' setting */
3392 				s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
3393 				nm_connection_add_setting (connection, NM_SETTING (s_cdma));
3394 				g_object_set (s_cdma,
3395 				              NM_SETTING_CDMA_NUMBER, "#777",
3396 				              NM_SETTING_CDMA_USERNAME, user,
3397 				              NM_SETTING_CDMA_PASSWORD, password,
3398 				              NULL);
3399 			}
3400 	
3401 			success = TRUE;
3402 	cleanup_mobile:
3403 			g_free (user);
3404 			g_free (password);
3405 			if (!success)
3406 				return FALSE;
3407 	
3408 		} else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
3409 			/* Build up the settings required for 'bluetooth' */
3410 			gboolean success = FALSE;
3411 			const char *addr = NULL;
3412 			char *addr_ask = NULL;
3413 			const char *bt_type_c = NULL;
3414 			char *bt_type = NULL;
3415 			GByteArray *array = NULL;
3416 			nmc_arg_t exp_args[] = { {"addr",    TRUE, &addr,      !ask},
3417 			                         {"bt-type", TRUE, &bt_type_c, FALSE},
3418 			                         {NULL} };
3419 	
3420 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3421 				return FALSE;
3422 	
3423 			if (!addr && ask)
3424 				addr = addr_ask = nmc_get_user_input (_("Bluetooth device address: "));
3425 			if (!addr) {
3426 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3427 				                     _("Error: 'addr' is required."));
3428 				return FALSE;
3429 			}
3430 			if (!check_and_convert_mac (addr, &array, ARPHRD_ETHER, "addr", error))
3431 				goto cleanup_bt;
3432 	
3433 			/* Also ask for all optional arguments if '--ask' is specified. */
3434 			bt_type = bt_type_c ? g_strdup (bt_type_c) : NULL;
3435 			if (ask)
3436 				do_questionnaire_bluetooth (&bt_type);
3437 	
3438 			/* Default to 'panu' if bt-type is not provided. */
3439 			if (!bt_type)
3440 				bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU);
3441 	
3442 			/* Add 'bluetooth' setting */
3443 			s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new ();
3444 			nm_connection_add_setting (connection, NM_SETTING (s_bt));
3445 	
3446 			if (array) {
3447 				g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, array, NULL);
3448 				g_byte_array_free (array, TRUE);
3449 			}
3450 	
3451 			/* 'dun' type requires adding 'gsm' or 'cdma' setting */
3452 			if (   !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
3453 			    || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) {
3454 				bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
3455 				s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
3456 				nm_connection_add_setting (connection, NM_SETTING (s_gsm));
3457 				g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
3458 	//			g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL;
3459 	
3460 			} else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) {
3461 				bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
3462 				s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
3463 				nm_connection_add_setting (connection, NM_SETTING (s_cdma));
3464 				g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL);
3465 	
3466 			} else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
3467 				/* no op */
3468 			} else {
3469 				g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3470 				             _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."),
3471 				             bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN,
3472 				             NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma");
3473 				goto cleanup_bt;
3474 			}
3475 			g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL);
3476 	
3477 			success = TRUE;
3478 	cleanup_bt:
3479 			g_free (addr_ask);
3480 			g_free (bt_type);
3481 			if (!success)
3482 				return FALSE;
3483 	
3484 		} else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) {
3485 			/* Build up the settings required for 'vlan' */
3486 			gboolean success = FALSE;
3487 			const char *ifname = NULL;
3488 			const char *parent = NULL;
3489 			char *parent_ask = NULL;
3490 			const char *vlan_id = NULL;
3491 			char *vlan_id_ask = NULL;
3492 			unsigned long id = 0;
3493 			const char *flags_c = NULL;
3494 			char *flags = NULL;
3495 			guint32 flags_int = 0;
3496 			const char *ingress_c = NULL, *egress_c = NULL;
3497 			char *ingress = NULL, *egress = NULL;
3498 			char **ingress_arr = NULL, **egress_arr = NULL, **p;
3499 			const char *mtu_c = NULL;
3500 			char *mtu = NULL;
3501 			guint32 mtu_int;
3502 			GByteArray *addr_array = NULL;
3503 			nmc_arg_t exp_args[] = { {"dev",     TRUE, &parent,    !ask},
3504 			                         {"id",      TRUE, &vlan_id,   !ask},
3505 			                         {"flags",   TRUE, &flags_c,   FALSE},
3506 			                         {"ingress", TRUE, &ingress_c, FALSE},
3507 			                         {"egress",  TRUE, &egress_c,  FALSE},
3508 			                         {"mtu",     TRUE, &mtu_c,     FALSE},
3509 			                         {NULL} };
3510 	
3511 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3512 				return FALSE;
3513 	
3514 			if (!parent && ask)
3515 				parent = parent_ask = nmc_get_user_input (_("VLAN parent device or connection UUID: "));
3516 			if (!parent) {
3517 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3518 				                     _("Error: 'dev' is required."));
3519 				return FALSE;
3520 			}
3521 			if (!vlan_id && ask)
3522 				vlan_id = vlan_id_ask = nmc_get_user_input (_("VLAN ID <0-4095>: "));
3523 			if (!vlan_id) {
3524 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3525 				                     _("Error: 'id' is required."));
3526 				goto cleanup_vlan;
3527 			}
3528 			if (vlan_id) {
3529 				if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4095, &id)) {
3530 					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3531 					             _("Error: 'id': '%s' is not valid; use <0-4095>."),
3532 					             vlan_id);
3533 					goto cleanup_vlan;
3534 				}
3535 			}
3536 	
3537 			if (   !(addr_array = nm_utils_hwaddr_atoba (parent, ARPHRD_ETHER))
3538 			    && !nm_utils_is_uuid (parent)
3539 			    && !nm_utils_iface_valid_name (parent)) {
3540 				g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3541 				             _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
3542 				             parent);
3543 				goto cleanup_vlan;
3544 			}
3545 	
3546 			/* Also ask for all optional arguments if '--ask' is specified. */
3547 			mtu = mtu_c ? g_strdup (mtu_c) : NULL;
3548 			flags = flags_c ? g_strdup (flags_c) : NULL;
3549 			ingress = ingress_c ? g_strdup (ingress_c) : NULL;
3550 			egress = egress_c ? g_strdup (egress_c) : NULL;
3551 			if (ask)
3552 				do_questionnaire_vlan (&mtu, &flags, &ingress, &egress);
3553 	
3554 			/* ifname is taken from connection's ifname */
3555 			ifname = nm_setting_connection_get_interface_name (s_con);
3556 	
3557 			if (!check_and_convert_mtu (mtu, &mtu_int, error))
3558 				goto cleanup_vlan;
3559 			if (!check_and_convert_vlan_flags (flags, &flags_int, error))
3560 				goto cleanup_vlan;
3561 			if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error))
3562 				goto cleanup_vlan;
3563 			if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &ingress_arr, error))
3564 				goto cleanup_vlan;
3565 	
3566 			/* Add 'vlan' setting */
3567 			s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
3568 			nm_connection_add_setting (connection, NM_SETTING (s_vlan));
3569 	
3570 			/* Add 'wired' setting if necessary */
3571 			if (mtu || addr_array) {
3572 				s_wired = (NMSettingWired *) nm_setting_wired_new ();
3573 				nm_connection_add_setting (connection, NM_SETTING (s_wired));
3574 	
3575 				if (mtu)
3576 					g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
3577 				if (addr_array)
3578 					g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, addr_array, NULL);
3579 			}
3580 	
3581 			/* Set 'vlan' properties */
3582 			if (!addr_array)
3583 				g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL);
3584 	
3585 			if (ifname)
3586 				g_object_set (s_vlan, NM_SETTING_VLAN_INTERFACE_NAME, ifname, NULL);
3587 			g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL);
3588 	
3589 			if (flags)
3590 				g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL);
3591 			for (p = ingress_arr; p && *p; p++)
3592 				nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p);
3593 			for (p = egress_arr; p && *p; p++)
3594 				nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p);
3595 	
3596 			success = TRUE;
3597 	cleanup_vlan:
3598 			g_free (mtu);
3599 			g_free (flags);
3600 			g_free (ingress);
3601 			g_free (egress);
3602 			if (addr_array)
3603 				g_byte_array_free (addr_array, TRUE);
3604 			g_free (parent_ask);
3605 			g_free (vlan_id_ask);
3606 			g_strfreev (ingress_arr);
3607 			g_strfreev (egress_arr);
3608 			if (!success)
3609 				return FALSE;
3610 	
3611 		} else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) {
3612 			/* Build up the settings required for 'bond' */
3613 			gboolean success = FALSE;
3614 			char *bond_ifname = NULL;
3615 			const char *ifname = NULL;
3616 			const char *bond_mode_c = NULL;
3617 			char *bond_mode = NULL;
3618 			const char *bond_primary_c = NULL;
3619 			char *bond_primary = NULL;
3620 			const char *bond_miimon_c = NULL;
3621 			char *bond_miimon = NULL;
3622 			const char *bond_downdelay_c = NULL;
3623 			char *bond_downdelay = NULL;
3624 			const char *bond_updelay_c = NULL;
3625 			char *bond_updelay = NULL;
3626 			const char *bond_arpinterval_c = NULL;
3627 			char *bond_arpinterval = NULL;
3628 			const char *bond_arpiptarget_c = NULL;
3629 			char *bond_arpiptarget = NULL;
3630 			nmc_arg_t exp_args[] = { {"mode",          TRUE, &bond_mode_c,        FALSE},
3631 			                         {"primary",       TRUE, &bond_primary_c,     FALSE},
3632 			                         {"miimon",        TRUE, &bond_miimon_c,      FALSE},
3633 			                         {"downdelay",     TRUE, &bond_downdelay_c,   FALSE},
3634 			                         {"updelay",       TRUE, &bond_updelay_c,     FALSE},
3635 			                         {"arp-interval",  TRUE, &bond_arpinterval_c, FALSE},
3636 			                         {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE},
3637 			                         {NULL} };
3638 	
3639 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3640 				return FALSE;
3641 	
3642 			/* Also ask for all optional arguments if '--ask' is specified. */
3643 			bond_mode = bond_mode_c ? g_strdup (bond_mode_c) : NULL;
3644 			bond_primary = bond_primary_c ? g_strdup (bond_primary_c) : NULL;
3645 			bond_miimon = bond_miimon_c ? g_strdup (bond_miimon_c) : NULL;
3646 			bond_downdelay = bond_downdelay_c ? g_strdup (bond_downdelay_c) : NULL;
3647 			bond_updelay = bond_updelay_c ? g_strdup (bond_updelay_c) : NULL;
3648 			bond_arpinterval = bond_arpinterval_c ? g_strdup (bond_arpinterval_c) : NULL;
3649 			bond_arpiptarget = bond_arpiptarget_c ? g_strdup (bond_arpiptarget_c) : NULL;
3650 			if (ask)
3651 				do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon,
3652 				                       &bond_downdelay, &bond_updelay,
3653 				                       &bond_arpinterval, &bond_arpiptarget);
3654 	
3655 			/* Use connection's ifname as 'bond' ifname if exists, else generate one */
3656 			ifname = nm_setting_connection_get_interface_name (s_con);
3657 			if (!ifname)
3658 				bond_ifname = unique_master_iface_ifname (all_connections,
3659 				                                          NM_SETTING_BOND_SETTING_NAME,
3660 				                                          NM_SETTING_BOND_INTERFACE_NAME,
3661 				                                          "nm-bond");
3662 			else
3663 				bond_ifname = g_strdup (ifname);
3664 	
3665 			/* Add 'bond' setting */
3666 			s_bond = (NMSettingBond *) nm_setting_bond_new ();
3667 			nm_connection_add_setting (connection, NM_SETTING (s_bond));
3668 	
3669 			/* Set bond options */
3670 			g_object_set (s_bond, NM_SETTING_BOND_INTERFACE_NAME, bond_ifname, NULL);
3671 			if (bond_mode) {
3672 				GError *err = NULL;
3673 				const char *bm;
3674 				if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) {
3675 					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3676 					             _("Error: 'mode': %s."), err->message);
3677 					g_clear_error (&err);
3678 					goto cleanup_bond;
3679 				}
3680 				nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm);
3681 			}
3682 			if (bond_primary) {
3683 				if (!nm_utils_iface_valid_name (bond_primary)) {
3684 					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3685 					             _("Error: 'primary': '%s' is not a valid interface name."),
3686 					             bond_primary);
3687 					goto cleanup_bond;
3688 				}
3689 				nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary);
3690 			}
3691 			if (bond_miimon)
3692 				nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon);
3693 			if (bond_downdelay && strcmp (bond_downdelay, "0") != 0)
3694 				nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay);
3695 			if (bond_updelay && strcmp (bond_updelay, "0") != 0)
3696 				nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay);
3697 			if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0)
3698 				nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval);
3699 			if (bond_arpiptarget)
3700 				nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget);
3701 	
3702 			success = TRUE;
3703 	cleanup_bond:
3704 			g_free (bond_ifname);
3705 			g_free (bond_mode);
3706 			g_free (bond_primary);
3707 			g_free (bond_miimon);
3708 			g_free (bond_downdelay);
3709 			g_free (bond_updelay);
3710 			g_free (bond_arpinterval);
3711 			g_free (bond_arpiptarget);
3712 			if (!success)
3713 				return FALSE;
3714 	
3715 		} else if (!strcmp (con_type, "bond-slave")) {
3716 			/* Build up the settings required for 'bond-slave' */
3717 			const char *master = NULL;
3718 			char *master_ask = NULL;
3719 			const char *type = NULL;
3720 			nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask},
3721 			                         {"type",   TRUE, &type,   FALSE},
3722 			                         {NULL} };
3723 	
3724 			if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error))
3725 				return FALSE;
3726 	
3727 			if (!master && ask)
3728 				master = master_ask = nmc_get_user_input (_("Bond master: "));
3729 			if (!master) {
3730 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3731 				                     _("Error: 'master' is required."));
3732 				return FALSE;
3733 			}
3734 	
3735 			if (type)
3736 				printf (_("Warning: 'type' is currently ignored. "
3737 				          "We only support ethernet slaves for now.\n"));
3738 	
3739 			/* Change properties in 'connection' setting */
3740 			g_object_set (s_con,
3741 			              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
3742 			              NM_SETTING_CONNECTION_MASTER, master,
3743 			              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME,
3744 			              NULL);
3745 	
3746 			/* Add ethernet setting */
3747 			s_wired = (NMSettingWired *) nm_setting_wired_new ();
3748 			nm_connection_add_setting (connection, NM_SETTING (s_wired));
3749 	
3750 			g_free (master_ask);
3751 	
3752 		} else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) {
3753 			/* Build up the settings required for 'team' */
3754 			char *team_ifname = NULL;
3755 			const char *ifname = NULL;
3756 			const char *config = NULL;
3757 			nmc_arg_t exp_args[] = { {"config", TRUE, &config, FALSE},
3758 			                         {NULL} };
3759 	
3760 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3761 				return FALSE;
3762 	
3763 			/* Use connection's ifname as 'team' ifname if exists, else generate one */
3764 			ifname = nm_setting_connection_get_interface_name (s_con);
3765 			if (!ifname)
3766 				team_ifname = unique_master_iface_ifname (all_connections,
3767 				                                          NM_SETTING_TEAM_SETTING_NAME,
3768 				                                          NM_SETTING_TEAM_INTERFACE_NAME,
3769 				                                          "nm-team");
3770 			else
3771 				team_ifname = g_strdup (ifname);
3772 	
3773 			/* Add 'team' setting */
3774 			s_team = (NMSettingTeam *) nm_setting_team_new ();
3775 			nm_connection_add_setting (connection, NM_SETTING (s_team));
3776 	
3777 			/* Set team options */
3778 			g_object_set (s_team, NM_SETTING_TEAM_INTERFACE_NAME, team_ifname, NULL);
3779 			if (config)
3780 				g_object_set (s_team, NM_SETTING_TEAM_CONFIG, config, NULL);
3781 	
3782 			g_free (team_ifname);
3783 	
3784 		} else if (!strcmp (con_type, "team-slave")) {
3785 			/* Build up the settings required for 'team-slave' */
3786 			const char *master = NULL;
3787 			char *master_ask = NULL;
3788 			const char *type = NULL;
3789 			const char *config = NULL;
3790 			nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask},
3791 			                         {"type",   TRUE, &type,   FALSE},
3792 			                         {"config",  TRUE, &config,  FALSE},
3793 			                         {NULL} };
3794 	
3795 			if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error))
3796 				return FALSE;
3797 	
3798 			if (!master && ask)
3799 				master = master_ask = nmc_get_user_input (_("Team master: "));
3800 			if (!master) {
3801 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3802 				                     _("Error: 'master' is required."));
3803 				return FALSE;
3804 			}
3805 	
3806 			if (type)
3807 				printf (_("Warning: 'type' is currently ignored. "
3808 				          "We only support ethernet slaves for now.\n"));
3809 	
3810 			/* Add 'team-port' setting */
3811 			s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new ();
3812 			nm_connection_add_setting (connection, NM_SETTING (s_team_port));
3813 	
3814 			if (config)
3815 				g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, config, NULL);
3816 	
3817 			/* Change properties in 'connection' setting */
3818 			g_object_set (s_con,
3819 			              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
3820 			              NM_SETTING_CONNECTION_MASTER, master,
3821 			              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME,
3822 			              NULL);
3823 	
3824 			/* Add ethernet setting */
3825 			s_wired = (NMSettingWired *) nm_setting_wired_new ();
3826 			nm_connection_add_setting (connection, NM_SETTING (s_wired));
3827 	
3828 			g_free (master_ask);
3829 	
3830 		} else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
3831 			/* Build up the settings required for 'bridge' */
3832 			gboolean success = FALSE;
3833 			char *bridge_ifname = NULL;
3834 			const char *ifname = NULL;
3835 			const char *stp_c = NULL;
3836 			char *stp = NULL;
3837 			const char *priority_c = NULL;
3838 			char *priority = NULL;
3839 			const char *fwd_delay_c = NULL;
3840 			char *fwd_delay = NULL;
3841 			const char *hello_time_c = NULL;
3842 			char *hello_time = NULL;
3843 			const char *max_age_c = NULL;
3844 			char *max_age = NULL;
3845 			const char *ageing_time_c = NULL;
3846 			char *ageing_time = NULL;
3847 			gboolean stp_bool;
3848 			unsigned long stp_prio_int, fwd_delay_int, hello_time_int,
3849 			              max_age_int, ageing_time_int;
3850 			nmc_arg_t exp_args[] = { {"stp",           TRUE, &stp_c,         FALSE},
3851 			                         {"priority",      TRUE, &priority_c,    FALSE},
3852 			                         {"forward-delay", TRUE, &fwd_delay_c,   FALSE},
3853 			                         {"hello-time",    TRUE, &hello_time_c,  FALSE},
3854 			                         {"max-age",       TRUE, &max_age_c,     FALSE},
3855 			                         {"ageing-time",   TRUE, &ageing_time_c, FALSE},
3856 			                         {NULL} };
3857 	
3858 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3859 				return FALSE;
3860 	
3861 			/* Also ask for all optional arguments if '--ask' is specified. */
3862 			stp = stp_c ? g_strdup (stp_c) : NULL;
3863 			priority = priority_c ? g_strdup (priority_c) : NULL;
3864 			fwd_delay = fwd_delay_c ? g_strdup (fwd_delay_c) : NULL;
3865 			hello_time = hello_time_c ? g_strdup (hello_time_c) : NULL;
3866 			max_age = max_age_c ? g_strdup (max_age_c) : NULL;
3867 			ageing_time = ageing_time_c ? g_strdup (ageing_time_c) : NULL;
3868 			if (ask)
3869 				do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time,
3870 				                         &max_age, &ageing_time);
3871 	
3872 			/* Use connection's ifname as 'bridge' ifname if exists, else generate one */
3873 			ifname = nm_setting_connection_get_interface_name (s_con);
3874 			if (!ifname)
3875 				bridge_ifname = unique_master_iface_ifname (all_connections,
3876 				                                            NM_SETTING_BRIDGE_SETTING_NAME,
3877 				                                            NM_SETTING_BRIDGE_INTERFACE_NAME,
3878 				                                            "nm-bridge");
3879 			else
3880 				bridge_ifname = g_strdup (ifname);
3881 	
3882 			if (stp) {
3883 				GError *tmp_err = NULL;
3884 				if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) {
3885 					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3886 					             _("Error: 'stp': %s."), tmp_err->message);
3887 					g_clear_error (&tmp_err);
3888 					goto cleanup_bridge;
3889 				}
3890 			}
3891 	
3892 			/* Add 'bond' setting */
3893 			/* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
3894 			s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
3895 			nm_connection_add_setting (connection, NM_SETTING (s_bridge));
3896 	
3897 			if (priority)
3898 				if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE,
3899 				                                 NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error))
3900 					goto cleanup_bridge;
3901 			if (fwd_delay)
3902 				if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE,
3903 				                                 NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error))
3904 					goto cleanup_bridge;
3905 			if (hello_time)
3906 				if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE,
3907 				                                 NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error))
3908 					goto cleanup_bridge;
3909 			if (max_age)
3910 				if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE,
3911 				                                 NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error))
3912 					goto cleanup_bridge;
3913 			if (ageing_time)
3914 				if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE,
3915 				                                 NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error))
3916 					goto cleanup_bridge;
3917 	
3918 			/* Set bridge options */
3919 			g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, bridge_ifname, NULL);
3920 			if (stp)
3921 				g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL);
3922 			if (priority)
3923 				g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL);
3924 			if (fwd_delay)
3925 				g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL);
3926 			if (hello_time)
3927 				g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL);
3928 			if (max_age)
3929 				g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL);
3930 			if (ageing_time)
3931 				g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL);
3932 	
3933 			success = TRUE;
3934 	cleanup_bridge:
3935 			g_free (bridge_ifname);
3936 			g_free (stp);
3937 			g_free (priority);
3938 			g_free (fwd_delay);
3939 			g_free (hello_time);
3940 			g_free (max_age);
3941 			g_free (ageing_time);
3942 			if (!success)
3943 				return FALSE;
3944 	
3945 		} else if (!strcmp (con_type, "bridge-slave")) {
3946 			/* Build up the settings required for 'bridge-slave' */
3947 			gboolean success = FALSE;
3948 			const char *master = NULL;
3949 			char *master_ask = NULL;
3950 			const char *type = NULL;
3951 			const char *priority_c = NULL;
3952 			char *priority = NULL;
3953 			const char *path_cost_c = NULL;
3954 			char *path_cost = NULL;
3955 			const char *hairpin_c = NULL;
3956 			char *hairpin = NULL;
3957 			unsigned long prio_int, path_cost_int;
3958 			gboolean hairpin_bool;
3959 			nmc_arg_t exp_args[] = { {"master",    TRUE, &master,      !ask},
3960 			                         {"type",      TRUE, &type,        FALSE},
3961 			                         {"priority",  TRUE, &priority_c,  FALSE},
3962 			                         {"path-cost", TRUE, &path_cost_c, FALSE},
3963 			                         {"hairpin",   TRUE, &hairpin_c,   FALSE},
3964 			                         {NULL} };
3965 	
3966 			if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error))
3967 				return FALSE;
3968 	
3969 			if (!master && ask)
3970 				master = master_ask = nmc_get_user_input (_("Bridge master: "));
3971 			if (!master) {
3972 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3973 				                     _("Error: 'master' is required."));
3974 				return FALSE;
3975 			}
3976 			if (!nm_utils_is_uuid (master) && !nm_utils_iface_valid_name (master)) {
3977 				g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3978 				             _("Error: 'master': '%s' is not valid UUID nor interface."),
3979 				             master);
3980 				goto cleanup_bridge_slave;
3981 			}
3982 	
3983 			if (type)
3984 				printf (_("Warning: 'type' is currently ignored. "
3985 				          "We only support ethernet slaves for now.\n"));
3986 	
3987 			/* Add 'bridge-port' setting */
3988 			/* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
3989 			s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new ();
3990 			nm_connection_add_setting (connection, NM_SETTING (s_bridge_port));
3991 	
3992 			/* Also ask for all optional arguments if '--ask' is specified. */
3993 			priority = priority_c ? g_strdup (priority_c) : NULL;
3994 			path_cost = path_cost_c ? g_strdup (path_cost_c) : NULL;
3995 			hairpin = hairpin_c ? g_strdup (hairpin_c) : NULL;
3996 			if (ask)
3997 				do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin);
3998 	
3999 			if (priority)
4000 				if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
4001 				                                 NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error))
4002 					goto cleanup_bridge_slave;
4003 			if (path_cost)
4004 				if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
4005 				                                 NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error))
4006 					goto cleanup_bridge_slave;
4007 			if (hairpin) {
4008 				GError *tmp_err = NULL;
4009 				if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) {
4010 					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4011 					             _("Error: 'hairpin': %s."), tmp_err->message);
4012 					g_clear_error (&tmp_err);
4013 					goto cleanup_bridge_slave;
4014 				}
4015 			}
4016 	
4017 			/* Change properties in 'connection' setting */
4018 			g_object_set (s_con,
4019 			              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
4020 			              NM_SETTING_CONNECTION_MASTER, master,
4021 			              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
4022 			              NULL);
4023 	
4024 			/* Add ethernet setting */
4025 			s_wired = (NMSettingWired *) nm_setting_wired_new ();
4026 			nm_connection_add_setting (connection, NM_SETTING (s_wired));
4027 	
4028 			if (priority)
4029 				g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL);
4030 			if (path_cost)
4031 				g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL);
4032 			if (hairpin)
4033 				g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL);
4034 	
4035 			success = TRUE;
4036 	cleanup_bridge_slave:
4037 			g_free (master_ask);
4038 			g_free (priority);
4039 			g_free (path_cost);
4040 			g_free (hairpin);
4041 			if (!success)
4042 				return FALSE;
4043 	
4044 		} else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) {
4045 			/* Build up the settings required for 'vpn' */
4046 			gboolean success = FALSE;
4047 			const char *valid_vpns[] = { "openvpn", "vpnc", "pptp", "openconnect", "openswan", NULL };
4048 			const char *vpn_type = NULL;
4049 			char *vpn_type_ask = NULL;
4050 			const char *user_c = NULL;
4051 			char *user = NULL;
4052 			const char *st;
4053 			char *service_type = NULL;
4054 			GError *tmp_err = NULL;
4055 			nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask},
4056 			                         {"user",     TRUE, &user_c,   FALSE},
4057 			                         {NULL} };
4058 	
4059 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4060 				return FALSE;
4061 	
4062 			if (!vpn_type && ask)
4063 				vpn_type = vpn_type_ask = nmc_get_user_input (_("VPN type: "));
4064 			if (!vpn_type) {
4065 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4066 				                     _("Error: 'vpn-type' is required."));
4067 				goto cleanup_vpn;
4068 			}
4069 	
4070 			/* Also ask for all optional arguments if '--ask' is specified. */
4071 			user = user_c ? g_strdup (user_c) : NULL;
4072 			if (ask)
4073 				do_questionnaire_vpn (&user);
4074 	
4075 			if (!(st = nmc_string_is_valid (vpn_type, valid_vpns, &tmp_err))) {
4076 				g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4077 				             _("Error: 'vpn-type': %s."), tmp_err->message);
4078 				g_clear_error (&tmp_err);
4079 				goto cleanup_vpn;
4080 			}
4081 			service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st);
4082 	
4083 			/* Add 'vpn' setting */
4084 			s_vpn = (NMSettingVPN *) nm_setting_vpn_new ();
4085 			nm_connection_add_setting (connection, NM_SETTING (s_vpn));
4086 	
4087 			g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL);
4088 			g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL);
4089 	
4090 			success = TRUE;
4091 	cleanup_vpn:
4092 			g_free (vpn_type_ask);
4093 			g_free (service_type);
4094 			g_free (user);
4095 			if (!success)
4096 				return FALSE;
4097 	
4098 		} else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) {
4099 			/* Build up the settings required for 'olpc' */
4100 			gboolean success = FALSE;
4101 			char *ssid_ask = NULL;
4102 			const char *ssid = NULL;
4103 			GByteArray *ssid_arr;
4104 			const char *channel_c = NULL;
4105 			char *channel = NULL;
4106 			unsigned long chan;
4107 			const char *dhcp_anycast_c = NULL;
4108 			char *dhcp_anycast = NULL;
4109 			GByteArray *array = NULL;
4110 			nmc_arg_t exp_args[] = { {"ssid",         TRUE, &ssid,           !ask},
4111 			                         {"channel",      TRUE, &channel_c,      FALSE},
4112 			                         {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE},
4113 			                         {NULL} };
4114 	
4115 			if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4116 				return FALSE;
4117 	
4118 			if (!ssid && ask)
4119 				ssid = ssid_ask = nmc_get_user_input (_("SSID: "));
4120 			if (!ssid) {
4121 				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4122 				                     _("Error: 'ssid' is required."));
4123 				goto cleanup_olpc;
4124 			}
4125 	
4126 			/* Also ask for all optional arguments if '--ask' is specified. */
4127 			channel = channel_c ? g_strdup (channel_c) : NULL;
4128 			dhcp_anycast = dhcp_anycast_c ? g_strdup (dhcp_anycast_c) : NULL;
4129 			if (ask)
4130 				do_questionnaire_olpc (&channel, &dhcp_anycast);
4131 	
4132 			if (channel) {
4133 				if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) {
4134 					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4135 					             _("Error: 'channel': '%s' is not valid; use <1-13>."),
4136 					             channel);
4137 					goto cleanup_olpc;
4138 				}
4139 			}
4140 			if (!check_and_convert_mac (dhcp_anycast, &array, ARPHRD_ETHER, "dhcp-anycast", error))
4141 				goto cleanup_olpc;
4142 	
4143 			/* Add OLPC mesh setting */
4144 			s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new ();
4145 			nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh));
4146 	
4147 			ssid_arr = g_byte_array_sized_new (strlen (ssid));
4148 			g_byte_array_append (ssid_arr, (const guint8 *) ssid, strlen (ssid));
4149 			g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_arr, NULL);
4150 			if (channel)
4151 				g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL);
4152 			else
4153 				g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL);
4154 			if (array) {
4155 				g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, array, NULL);
4156 				g_byte_array_free (array, TRUE);
4157 			}
4158 			g_byte_array_free (ssid_arr, TRUE);
4159 	
4160 			success = TRUE;
4161 	cleanup_olpc:
4162 			g_free (ssid_ask);
4163 			g_free (channel);
4164 			g_free (dhcp_anycast);
4165 			if (!success)
4166 				return FALSE;
4167 	
4168 		} else {
4169 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4170 			             _("Error: '%s' is not a valid connection type."),
4171 			             con_type);
4172 			return FALSE;
4173 		}
4174 	
4175 		/* Read and add IP configuration */
4176 		if (   strcmp (con_type, "bond-slave") != 0
4177 		    && strcmp (con_type, "team-slave") != 0
4178 		    && strcmp (con_type, "bridge-slave") != 0) {
4179 	
4180 			NMIP4Address *ip4addr = NULL;
4181 			NMIP6Address *ip6addr = NULL;
4182 			const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL;
4183 			nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE},
4184 			                         {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE},
4185 			                         {NULL} };
4186 	
4187 			while (argc) {
4188 				nmc_arg_t *p;
4189 	
4190 				/* reset 'found' flag */
4191 				for (p = exp_args; p->name; p++)
4192 					p->found = FALSE;
4193 	
4194 				ip4 = gw4 = ip6 = gw6 = NULL;
4195 	
4196 				if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error))
4197 					return FALSE;
4198 	
4199 				if (ip4) {
4200 					ip4addr = nmc_parse_and_build_ip4_address (ip4, gw4, error);
4201 					if (!ip4addr) {
4202 						g_prefix_error (error, _("Error: "));
4203 						return FALSE;
4204 					}
4205 				}
4206 				add_ip4_address_to_connection (ip4addr, connection);
4207 	
4208 				if (ip6) {
4209 					ip6addr = nmc_parse_and_build_ip6_address (ip6, gw6, error);
4210 					if (!ip6addr) {
4211 						g_prefix_error (error, _("Error: "));
4212 						return FALSE;
4213 					}
4214 				}
4215 				add_ip6_address_to_connection (ip6addr, connection);
4216 			}
4217 	
4218 			/* Ask for addresses if '--ask' is specified. */
4219 			if (ask)
4220 				do_questionnaire_ip (connection);
4221 		}
4222 	
4223 		return TRUE;
4224 	}
4225 	
4226 	static char *
4227 	unique_connection_name (GSList *list, const char *try_name)
4228 	{
4229 		NMConnection *connection;
4230 		const char *name;
4231 		char *new_name;
4232 		unsigned int num = 1;
4233 		GSList *iterator = list;
4234 	
4235 		new_name = g_strdup (try_name);
4236 		while (iterator) {
4237 			connection = NM_CONNECTION (iterator->data);
4238 	
4239 			name = nm_connection_get_id (connection);
4240 			if (g_strcmp0 (new_name, name) == 0) {
4241 				g_free (new_name);
4242 				new_name = g_strdup_printf ("%s-%d", try_name, num++);
4243 				iterator = list;
4244 			}
4245 			iterator = g_slist_next (iterator);
4246 		}
4247 		return new_name;
4248 	}
4249 	
4250 	typedef struct {
4251 		NmCli *nmc;
4252 		char *con_name;
4253 	} AddConnectionInfo;
4254 	
4255 	static void
4256 	add_connection_cb (NMRemoteSettings *settings,
4257 	                   NMRemoteConnection *connection,
4258 	                   GError *error,
4259 	                   gpointer user_data)
4260 	{
4261 		AddConnectionInfo *info = (AddConnectionInfo *) user_data;
4262 		NmCli *nmc = info->nmc;
4263 	
4264 		if (error) {
4265 			g_string_printf (nmc->return_text,
4266 			                 _("Error: Failed to add '%s' connection: (%d) %s"),
4267 			                 info->con_name, error->code, error->message);
4268 			nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
4269 		} else {
4270 			printf (_("Connection '%s' (%s) successfully added.\n"),
4271 			        nm_connection_get_id (NM_CONNECTION (connection)),
4272 			        nm_connection_get_uuid (NM_CONNECTION (connection)));
4273 		}
4274 	
4275 		g_free (info->con_name);
4276 		g_free (info);
4277 		quit ();
4278 	}
4279 	
4280 	static NMCResultCode
4281 	do_connection_add (NmCli *nmc, int argc, char **argv)
4282 	{
4283 		NMConnection *connection = NULL;
4284 		NMSettingConnection *s_con;
4285 		char *uuid;
4286 		char *default_name = NULL;
4287 		const char *type = NULL;
4288 		char *type_ask = NULL;
4289 		const char *con_name = NULL;
4290 		const char *autoconnect = NULL;
4291 		gboolean auto_bool = TRUE;
4292 		const char *ifname = NULL;
4293 		char *ifname_ask = NULL;
4294 		gboolean ifname_mandatory = TRUE;
4295 		AddConnectionInfo *info = NULL;
4296 		const char *setting_name;
4297 		GError *error = NULL;
4298 		nmc_arg_t exp_args[] = { {"type",        TRUE, &type,        !nmc->ask},
4299 		                         {"con-name",    TRUE, &con_name,    FALSE},
4300 		                         {"autoconnect", TRUE, &autoconnect, FALSE},
4301 		                         {"ifname",      TRUE, &ifname,      FALSE},
4302 		                         {NULL} };
4303 	
4304 		nmc->return_value = NMC_RESULT_SUCCESS;
4305 	
4306 		if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) {
4307 			g_string_assign (nmc->return_text, error->message);
4308 			nmc->return_value = error->code;
4309 			g_clear_error (&error);
4310 			goto error;
4311 		}
4312 	
4313 		if (!type && nmc->ask) {
4314 			char *types_tmp = get_valid_options_string (nmc_valid_connection_types);
4315 			printf ("Valid types: [%s]\n", types_tmp);
4316 			type = type_ask = nmc_get_user_input (_("Connection type: "));
4317 			g_free (types_tmp);
4318 		}
4319 		if (!type) {
4320 			g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
4321 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
4322 			goto error;
4323 		}
4324 	
4325 		if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, &error))) {
4326 			g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."),
4327 			                 error->message);
4328 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
4329 			g_clear_error (&error);
4330 			goto error;
4331 		}
4332 		if (autoconnect) {
4333 			GError *tmp_err = NULL;
4334 			if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) {
4335 				g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."),
4336 				                 tmp_err->message);
4337 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
4338 				g_clear_error (&tmp_err);
4339 				goto error;
4340 			}
4341 		}
4342 	
4343 		/* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */
4344 		if (   strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0
4345 		    || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0
4346 		    || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0
4347 		    || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0)
4348 			ifname_mandatory = FALSE;
4349 	
4350 		if (!ifname && ifname_mandatory && nmc->ask)
4351 			ifname = ifname_ask = nmc_get_user_input (_("Interface name [*]: "));
4352 		if (!ifname && ifname_mandatory)
4353 			ifname = ifname_ask = g_strdup ("*");
4354 	
4355 		if (ifname) {
4356 			if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) {
4357 				g_string_printf (nmc->return_text,
4358 				                 _("Error: 'ifname': '%s' is not a valid interface nor '*'."),
4359 				                 ifname);
4360 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
4361 				goto error;
4362 			}
4363 			/* Special value of '*' means no specific interface name */
4364 			if (strcmp (ifname, "*") == 0)
4365 				ifname = NULL;
4366 		}
4367 	
4368 		/* Create a new connection object */
4369 		connection = nm_connection_new ();
4370 	
4371 		/* Build up the 'connection' setting */
4372 		s_con = (NMSettingConnection *) nm_setting_connection_new ();
4373 		uuid = nm_utils_uuid_generate ();
4374 		if (con_name)
4375 			default_name = g_strdup (con_name);
4376 		else {
4377 			char *try_name = ifname ?
4378 			                     g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname)
4379 			                   : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types));
4380 			default_name = unique_connection_name (nmc->system_connections, try_name);
4381 			g_free (try_name);
4382 		}
4383 		g_object_set (s_con,
4384 		              NM_SETTING_CONNECTION_ID, default_name,
4385 		              NM_SETTING_CONNECTION_UUID, uuid,
4386 		              NM_SETTING_CONNECTION_TYPE, setting_name,
4387 		              NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool,
4388 		              NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
4389 		              NULL);
4390 		g_free (uuid);
4391 		g_free (default_name);
4392 		nm_connection_add_setting (connection, NM_SETTING (s_con));
4393 	
4394 		if (!complete_connection_by_type (connection,
4395 		                                  setting_name,
4396 		                                  nmc->system_connections,
4397 		                                  nmc->ask,
4398 		                                  argc,
4399 		                                  argv,
4400 		                                  &error)) {
4401 			g_string_assign (nmc->return_text, error->message);
4402 			nmc->return_value = error->code;
4403 			g_clear_error (&error);
4404 			goto error;
4405 		}
4406 	
4407 		nmc->should_wait = TRUE;
4408 	
4409 		info = g_malloc0 (sizeof (AddConnectionInfo));
4410 		info->nmc = nmc;
4411 		info->con_name = g_strdup (nm_connection_get_id (connection));
4412 	
4413 		/* Tell the settings service to add the new connection */
4414 		nm_remote_settings_add_connection (nmc->system_settings,
4415 		                                   connection,
4416 		                                   add_connection_cb,
4417 		                                   info);
4418 	
4419 		if (connection)
4420 			g_object_unref (connection);
4421 	
4422 		return nmc->return_value;
4423 	
4424 	error:
4425 		if (connection)
4426 			g_object_unref (connection);
4427 		g_free (type_ask);
4428 		g_free (ifname_ask);
4429 	
4430 		nmc->should_wait = FALSE;
4431 		return nmc->return_value;
4432 	}
4433 	
4434 	
4435 	/*----------------------------------------------------------------------------*/
4436 	
4437 	typedef char *CPFunction ();
4438 	typedef char **CPPFunction ();
4439 	/* History entry struct copied from libreadline's history.h */
4440 	typedef struct _hist_entry {
4441 		char *line;
4442 		char *timestamp;
4443 		char *data;
4444 	} HIST_ENTRY;
4445 	
4446 	typedef char *  (*ReadLineFunc)     (const char *);
4447 	typedef void    (*AddHistoryFunc)   (const char *);
4448 	typedef HIST_ENTRY** (*HistoryListFunc)  (void);
4449 	typedef int     (*RlInsertTextFunc) (char *);
4450 	typedef char ** (*RlCompletionMatchesFunc) (char *, CPFunction *);
4451 	
4452 	typedef struct {
4453 		ReadLineFunc readline_func;
4454 		AddHistoryFunc add_history_func;
4455 		HistoryListFunc history_list_func;
4456 		RlInsertTextFunc rl_insert_text_func;
4457 		void **rl_startup_hook_x;
4458 		RlCompletionMatchesFunc completion_matches_func;
4459 		void **rl_attempted_completion_function_x;
4460 		void **rl_completion_entry_function_x;
4461 		char **rl_line_buffer_x;
4462 		char **rl_prompt_x;
4463 		int *rl_attempted_completion_over_x;
4464 		int *rl_completion_append_character_x;
4465 		const char **rl_completer_word_break_characters_x;
4466 		void (*rl_free_line_state_func) (void);
4467 		void (*rl_cleanup_after_signal_func) (void);
4468 	} EditLibSymbols;
4469 	
4470 	static EditLibSymbols edit_lib_symbols;
4471 	static char *pre_input_deftext;
4472 	
4473 	static int
4474 	set_deftext (void)
4475 	{
4476 		if (   pre_input_deftext
4477 		    && edit_lib_symbols.rl_insert_text_func
4478 		    && edit_lib_symbols.rl_startup_hook_x) {
4479 			edit_lib_symbols.rl_insert_text_func (pre_input_deftext);
4480 			g_free (pre_input_deftext);
4481 			pre_input_deftext = NULL;
4482 			*edit_lib_symbols.rl_startup_hook_x = NULL;
4483 		}
4484 		return 0;
4485 	}
4486 	
4487 	static char *
4488 	gen_func_basic (char *text, int state, const char **words)
4489 	{
4490 		static int list_idx, len;
4491 		const char *name;
4492 	
4493 		if (!state) {
4494 			list_idx = 0;
4495 			len = strlen (text);
4496 		}
4497 	
4498 		/* Return the next name which partially matches one from the 'words' list. */
4499 		while ((name = words[list_idx])) {
4500 			list_idx++;
4501 	
4502 			if (strncmp (name, text, len) == 0)
4503 				return g_strdup (name);
4504 		}
4505 		return NULL;
4506 	}
4507 	
4508 	static char *
4509 	gen_nmcli_cmds_menu (char *text, int state)
4510 	{
4511 		const char *words[] = { "goto", "set", "remove", "describe", "print", "verify",
4512 		                        "save", "back", "help", "quit", "nmcli",
4513 		                        NULL };
4514 		return gen_func_basic (text, state, words);
4515 	}
4516 	
4517 	static char *
4518 	gen_nmcli_cmds_submenu (char *text, int state)
4519 	{
4520 		const char *words[] = { "set", "add", "change", "remove", "describe",
4521 		                        "print", "back", "help", "quit",
4522 		                        NULL };
4523 		return gen_func_basic (text, state, words);
4524 	}
4525 	
4526 	static char *
4527 	gen_cmd_nmcli (char *text, int state)
4528 	{
4529 		const char *words[] = { "status-line", "save-confirmation", "prompt-color", NULL };
4530 		return gen_func_basic (text, state, words);
4531 	}
4532 	
4533 	static char *
4534 	gen_cmd_nmcli_prompt_color (char *text, int state)
4535 	{
4536 		const char *words[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", NULL };
4537 		return gen_func_basic (text, state, words);
4538 	}
4539 	
4540 	static char *
4541 	gen_func_bool_values (char *text, int state)
4542 	{
4543 		const char *words[] = { "yes", "no", NULL };
4544 		return gen_func_basic (text, state, words);
4545 	}
4546 	
4547 	static char *
4548 	gen_cmd_verify0 (char *text, int state)
4549 	{
4550 		const char *words[] = { "all", NULL };
4551 		return gen_func_basic (text, state, words);
4552 	}
4553 	
4554 	static char *
4555 	gen_cmd_print2 (char *text, int state)
4556 	{
4557 		const char *words[] = { "setting", "connection", "all", NULL };
4558 		return gen_func_basic (text, state, words);
4559 	}
4560 	
4561 	static char *
4562 	gen_connection_types (char *text, int state)
4563 	{
4564 		static int list_idx, len;
4565 		const char *c_type, *a_type;
4566 	
4567 		if (!state) {
4568 			list_idx = 0;
4569 			len = strlen (text);
4570 		}
4571 	
4572 		while (nmc_valid_connection_types[list_idx].name) {
4573 			a_type = nmc_valid_connection_types[list_idx].alias;
4574 			c_type = nmc_valid_connection_types[list_idx].name;
4575 			list_idx++;
4576 			if (a_type && !strncmp (text, a_type, len))
4577 				return g_strdup (a_type);
4578 			if (c_type && !strncmp (text, c_type, len))
4579 				return g_strdup (c_type);
4580 		}
4581 	
4582 		return NULL;
4583 	}
4584 	
4585 	static char *
4586 	gen_setting_names (char *text, int state)
4587 	{
4588 		static int list_idx, len;
4589 		const char *s_name, *a_name;
4590 		const NameItem *valid_settings_arr;
4591 	
4592 		if (!state) {
4593 			list_idx = 0;
4594 			len = strlen (text);
4595 		}
4596 	
4597 		valid_settings_arr = get_valid_settings_array (nmc_completion_con_type);
4598 		if (!valid_settings_arr)
4599 			return NULL;
4600 		while (valid_settings_arr[list_idx].name) {
4601 			a_name = valid_settings_arr[list_idx].alias;
4602 			s_name = valid_settings_arr[list_idx].name;
4603 			list_idx++;
4604 			if (len == 0 && a_name)
4605 				return g_strdup_printf ("%s (%s)", s_name, a_name);
4606 			if (a_name && !strncmp (text, a_name, len))
4607 				return g_strdup (a_name);
4608 			if (s_name && !strncmp (text, s_name, len))
4609 				return g_strdup (s_name);
4610 		}
4611 		return NULL;
4612 	}
4613 	
4614 	static char *
4615 	gen_property_names (char *text, int state)
4616 	{
4617 		NMSetting *setting = NULL;
4618 		char **valid_props = NULL;
4619 		char *ret = NULL;
4620 		char *line = g_strdup (*edit_lib_symbols.rl_line_buffer_x);
4621 		const char *setting_name;
4622 		char **strv = NULL;
4623 		const NameItem *valid_settings_arr;
4624 		const char *p1;
4625 	
4626 		/* Try to get the setting from 'line' - setting_name.property */
4627 		p1 = strchr (line, '.');
4628 		if (p1) {
4629 			while (p1 > line && !g_ascii_isspace (*p1))
4630 				p1--;
4631 	
4632 			strv = g_strsplit (p1+1, ".", 2);
4633 	
4634 			valid_settings_arr = get_valid_settings_array (nmc_completion_con_type);
4635 			setting_name = check_valid_name (strv[0], valid_settings_arr, NULL);
4636 			setting = nmc_setting_new_for_name (setting_name);
4637 		} else {
4638 			/* Else take the current setting, if any */
4639 			setting = nmc_completion_setting ? g_object_ref (nmc_completion_setting) : NULL;
4640 		}
4641 	
4642 		if (setting) {
4643 			valid_props = nmc_setting_get_valid_properties (setting);
4644 			ret = gen_func_basic (text, state, (const char **) valid_props);
4645 		}
4646 	
4647 		g_free (line);
4648 		g_strfreev (strv);
4649 		g_strfreev (valid_props);
4650 		if (setting)
4651 			g_object_unref (setting);
4652 		return ret;
4653 	}
4654 	
4655 	typedef char * (*my_gen_func_ptr) (char *, int);
4656 	static my_gen_func_ptr
4657 	get_gen_func_cmd_nmcli (char *str)
4658 	{
4659 		if (!str)
4660 			return NULL;
4661 		if (matches (str, "status-line") == 0)
4662 			return gen_func_bool_values;
4663 		if (matches (str, "save-confirmation") == 0)
4664 			return gen_func_bool_values;
4665 		if (matches (str, "prompt-color") == 0)
4666 			return gen_cmd_nmcli_prompt_color;
4667 		return NULL;
4668 	}
4669 	
4670 	/*
4671 	 * Helper function parsing line for completion.
4672 	 * IN:
4673 	 *   line : the whole line to be parsed
4674 	 *   end  : the position of cursor in the line
4675 	 *   cmd  : command to match
4676 	 * OUT:
4677 	 *   cw_num    : is set to the word number being completed (1, 2, 3, 4).
4678 	 *   prev_word : returns the previous word (so that we have some context).
4679 	 *
4680 	 * Returns TRUE when the first word of the 'line' matches 'cmd'.
4681 	 *
4682 	 * Examples:
4683 	 * line="rem"              cmd="remove"   -> TRUE  cw_num=1
4684 	 * line="set con"          cmd="set"      -> TRUE  cw_num=2
4685 	 * line="go ipv4.method"   cmd="goto"     -> TRUE  cw_num=2
4686 	 * line="  des eth.mtu "   cmd="describe" -> TRUE  cw_num=3
4687 	 * line=" bla ipv4.method" cmd="goto"     -> FALSE
4688 	 */
4689 	static gboolean
4690 	should_complete_cmd (const char *line, int end, const char *cmd,
4691 	                     int *cw_num, char **prev_word)
4692 	{
4693 		char *tmp;
4694 		const char *word1, *word2, *word3;
4695 		size_t n1, n2, n3, n4, n5, n6;
4696 		gboolean word1_done, word2_done, word3_done;
4697 		gboolean ret = FALSE;
4698 	
4699 		if (!line)
4700 			return FALSE;
4701 	
4702 		tmp = g_strdup (line);
4703 	
4704 		n1 = strspn  (tmp,    " \t");
4705 		n2 = strcspn (tmp+n1, " \t\0") + n1;
4706 		n3 = strspn  (tmp+n2, " \t")   + n2;
4707 		n4 = strcspn (tmp+n3, " \t\0") + n3;
4708 		n5 = strspn  (tmp+n4, " \t")   + n4;
4709 		n6 = strcspn (tmp+n5, " \t\0") + n5;
4710 	
4711 		word1_done = end > n2;
4712 		word2_done = end > n4;
4713 		word3_done = end > n6;
4714 		tmp[n2] = tmp[n4] = tmp[n6] = '\0';
4715 	
4716 		word1 = tmp[n1] ? tmp + n1 : NULL;
4717 		word2 = tmp[n3] ? tmp + n3 : NULL;
4718 		word3 = tmp[n5] ? tmp + n5 : NULL;
4719 	
4720 		if (!word1_done) {
4721 			if (cw_num)
4722 				*cw_num = 1;
4723 			if (prev_word)
4724 				*prev_word = NULL;
4725 		} else if (!word2_done) {
4726 			if (cw_num)
4727 				*cw_num = 2;
4728 			if (prev_word)
4729 				*prev_word = g_strdup (word1);
4730 		} else if (!word3_done) {
4731 			if (cw_num)
4732 				*cw_num = 3;
4733 			if (prev_word)
4734 				*prev_word = g_strdup (word2);
4735 		} else {
4736 			if (cw_num)
4737 				*cw_num = 4;
4738 			if (prev_word)
4739 				*prev_word = g_strdup (word3);
4740 		}
4741 	
4742 		if (word1 && matches (word1, cmd) == 0)
4743 			ret = TRUE;
4744 	
4745 		g_free (tmp);
4746 		return ret;
4747 	}
4748 	
4749 	/*
4750 	 * Attempt to complete on the contents of TEXT.  START and END show the
4751 	 * region of TEXT that contains the word to complete.  We can use the
4752 	 * entire line in case we want to do some simple parsing.  Return the
4753 	 * array of matches, or NULL if there aren't any.
4754 	 */
4755 	static char **
4756 	nmcli_editor_tab_completion (char *text, int start, int end)
4757 	{
4758 		char **match_array = NULL;
4759 		const char *line = *edit_lib_symbols.rl_line_buffer_x;
4760 		const char *prompt = *edit_lib_symbols.rl_prompt_x;
4761 		CPFunction *generator_func = NULL;
4762 		gboolean copy_char;
4763 		const char *p1;
4764 		char *p2, *prompt_tmp;
4765 		char *word = NULL;
4766 		size_t n1;
4767 		int num;
4768 	
4769 		/* Restore standard append character to space */
4770 		*edit_lib_symbols.rl_completion_append_character_x = ' ';
4771 	
4772 		/* Filter out possible ANSI color escape sequences */
4773 		p1 = prompt;
4774 		p2 = prompt_tmp = g_strdup (prompt);
4775 		copy_char = TRUE;
4776 		while (*p1) {
4777 			if (*p1 == '\33')
4778 				copy_char = FALSE;
4779 			if (copy_char)
4780 				*p2++ = *p1;
4781 			if (!copy_char && *p1 == 'm')
4782 				copy_char = TRUE;
4783 			p1++;
4784 		}
4785 		*p2 = '\0';
4786 	
4787 		/* Find the first non-space character */
4788 		n1 = strspn (line, " \t");
4789 	
4790 		/* Choose the right generator function */
4791 		if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0)
4792 			generator_func = gen_connection_types;
4793 		else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0)
4794 			generator_func = gen_setting_names;
4795 		else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0)
4796 			generator_func = gen_property_names;
4797 		else if (g_str_has_prefix (prompt_tmp, "nmcli")) {
4798 			if (!strchr (prompt_tmp, '.')) {
4799 				int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1;
4800 				const char *dot = strchr (line, '.');
4801 	
4802 				/* Main menu  - level 0,1 */
4803 				if (start == n1)
4804 					generator_func = gen_nmcli_cmds_menu;
4805 				else {
4806 					if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) {
4807 						if (level == 0 && (!dot || dot >= line + end))
4808 							generator_func = gen_setting_names;
4809 						else
4810 							generator_func = gen_property_names;
4811 					} else if (  (   should_complete_cmd (line, end, "set", &num, NULL)
4812 					              || should_complete_cmd (line, end, "remove", &num, NULL)
4813 					              || should_complete_cmd (line, end, "describe", &num, NULL))
4814 					           && num <= 2) {
4815 						if (level == 0 && (!dot || dot >= line + end)) {
4816 							generator_func = gen_setting_names;
4817 							*edit_lib_symbols.rl_completion_append_character_x = '.';
4818 						} else
4819 							generator_func = gen_property_names;
4820 					} else if (should_complete_cmd (line, end, "nmcli", &num, &word)) {
4821 						if (num < 3)
4822 							generator_func = gen_cmd_nmcli;
4823 						else if (num == 3)
4824 							generator_func = get_gen_func_cmd_nmcli (word);
4825 					} else if (   should_complete_cmd (line, end, "print", &num, NULL)
4826 					           || should_complete_cmd (line, end, "verify", &num, NULL)) {
4827 						if (num <= 2)
4828 							generator_func = gen_cmd_verify0;
4829 					} else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
4830 						generator_func = gen_nmcli_cmds_menu;
4831 				}
4832 			} else {
4833 				/* Submenu - level 2 */
4834 				if (start == n1)
4835 					generator_func = gen_nmcli_cmds_submenu;
4836 				else {
4837 					if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2)
4838 						generator_func = gen_cmd_print2;
4839 					else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
4840 						generator_func = gen_nmcli_cmds_submenu;
4841 				}
4842 			}
4843 		}
4844 	
4845 		if (generator_func)
4846 			match_array = edit_lib_symbols.completion_matches_func (text, generator_func);
4847 	
4848 		/* Disable default filename completion */
4849 		if (!match_array)
4850 			*edit_lib_symbols.rl_attempted_completion_over_x = 1;
4851 	
4852 		g_free (prompt_tmp);
4853 		g_free (word);
4854 		return match_array;
4855 	}
4856 	
4857 	static GModule *
4858 	load_cmd_line_edit_lib (void)
4859 	{
4860 		GModule *module;
4861 		char *lib_path;
4862 		int i;
4863 		static const char * const edit_lib_table[] = {
4864 		    "libreadline.so.6", /* GNU Readline library version 6 - latest */
4865 		    "libreadline.so.5", /* GNU Readline library version 5 - previous */
4866 		    "libedit.so.0",     /* NetBSD Editline library port (http://www.thrysoee.dk/editline/) */
4867 		};
4868 	
4869 		/* Try to load a library for line editing */
4870 		for (i = 0; i < G_N_ELEMENTS (edit_lib_table); i++) {
4871 			lib_path = g_module_build_path (NULL, edit_lib_table[i]);
4872 			module = g_module_open (lib_path, G_MODULE_BIND_LOCAL);
4873 			g_free (lib_path);
4874 			if (module)
4875 				break;
4876 		}
4877 		if (!module)
4878 			return NULL;
4879 	
4880 		if (!g_module_symbol (module, "readline", (gpointer) (&edit_lib_symbols.readline_func)))
4881 			goto error;
4882 		if (!g_module_symbol (module, "add_history", (gpointer) (&edit_lib_symbols.add_history_func)))
4883 			goto error;
4884 		if (!g_module_symbol (module, "history_list", (gpointer) (&edit_lib_symbols.history_list_func)))
4885 			goto error;
4886 		if (!g_module_symbol (module, "rl_insert_text", (gpointer) (&edit_lib_symbols.rl_insert_text_func)))
4887 			goto error;
4888 		if (!g_module_symbol (module, "rl_startup_hook", (gpointer) (&edit_lib_symbols.rl_startup_hook_x)))
4889 			goto error;
4890 		if (!g_module_symbol (module, "rl_attempted_completion_function",
4891 		                      (gpointer) (&edit_lib_symbols.rl_attempted_completion_function_x)))
4892 			goto error;
4893 		if (!g_module_symbol (module, "completion_matches",
4894 		                      (gpointer) (&edit_lib_symbols.completion_matches_func)))
4895 			goto error;
4896 		if (!g_module_symbol (module, "rl_line_buffer",
4897 		                      (gpointer) (&edit_lib_symbols.rl_line_buffer_x)))
4898 			goto error;
4899 		if (!g_module_symbol (module, "rl_prompt",
4900 		                      (gpointer) (&edit_lib_symbols.rl_prompt_x)))
4901 			goto error;
4902 		if (!g_module_symbol (module, "rl_attempted_completion_over",
4903 		                      (gpointer) (&edit_lib_symbols.rl_attempted_completion_over_x)))
4904 			goto error;
4905 		if (!g_module_symbol (module, "rl_completion_append_character",
4906 		                      (gpointer) (&edit_lib_symbols.rl_completion_append_character_x)))
4907 			goto error;
4908 		if (!g_module_symbol (module, "rl_completer_word_break_characters",
4909 		                      (gpointer) (&edit_lib_symbols.rl_completer_word_break_characters_x)))
4910 			goto error;
4911 		if (!g_module_symbol (module, "rl_free_line_state",
4912 		                      (gpointer) (&edit_lib_symbols.rl_free_line_state_func)))
4913 			goto error;
4914 		if (!g_module_symbol (module, "rl_cleanup_after_signal",
4915 		                      (gpointer) (&edit_lib_symbols.rl_cleanup_after_signal_func)))
4916 			goto error;
4917 	
4918 		/* Set a pointer to an alternative function to create matches */
4919 		*edit_lib_symbols.rl_attempted_completion_function_x = (CPPFunction *) nmcli_editor_tab_completion;
4920 	
4921 		/* Use ' ' and '.' as word break characters */
4922 		*edit_lib_symbols.rl_completer_word_break_characters_x = ". ";
4923 	
4924 		return module;
4925 	error:
4926 		g_module_close (module);
4927 		return NULL;
4928 	}
4929 	
4930 	void
4931 	nmc_cleanup_readline (void)
4932 	{
4933 		if (edit_lib_symbols.rl_free_line_state_func)
4934 			edit_lib_symbols.rl_free_line_state_func ();
4935 		if (edit_lib_symbols.rl_cleanup_after_signal_func)
4936 			edit_lib_symbols.rl_cleanup_after_signal_func ();
4937 	}
4938 	
4939 	static char *
4940 	readline_x (const char *prompt)
4941 	{
4942 		char *str;
4943 	
4944 		if (edit_lib_symbols.readline_func) {
4945 			str = edit_lib_symbols.readline_func (prompt);
4946 			/* Return NULL, not empty string */
4947 			if (str && *str == '\0') {
4948 				g_free (str);
4949 				str = NULL;
4950 			}
4951 		} else
4952 			str = nmc_get_user_input (prompt);
4953 	
4954 		if (edit_lib_symbols.add_history_func && str && *str)
4955 			edit_lib_symbols.add_history_func (str);
4956 	
4957 		return str;
4958 	}
4959 	
4960 	
4961 	#define NMCLI_EDITOR_HISTORY ".nmcli-history"
4962 	
4963 	static void
4964 	load_history_cmds (const char *uuid)
4965 	{
4966 		GKeyFile *kf;
4967 		char *filename;
4968 		char **keys;
4969 		char *line;
4970 		size_t i;
4971 		GError *err = NULL;
4972 	
4973 		/* Nothing to do if readline library is not used */
4974 		if (!edit_lib_symbols.add_history_func)
4975 			return;
4976 	
4977 		filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
4978 		kf = g_key_file_new ();
4979 		if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
4980 			if (err->code == G_KEY_FILE_ERROR_PARSE)
4981 				printf ("Warning: %s parse error: %s\n", filename, err->message);
4982 			g_key_file_free (kf);
4983 			g_free (filename);
4984 			return;
4985 		}
4986 		keys = g_key_file_get_keys (kf, uuid, NULL, NULL);
4987 		for (i = 0; keys && keys[i]; i++) {
4988 			line = g_key_file_get_string (kf, uuid, keys[i], NULL);
4989 			if (line && *line)
4990 				edit_lib_symbols.add_history_func (line);
4991 			g_free (line);
4992 		}
4993 		g_strfreev (keys);
4994 		g_key_file_free (kf);
4995 		g_free (filename);
4996 	}
4997 	
4998 	static void
4999 	save_history_cmds (const char *uuid)
5000 	{
5001 		HIST_ENTRY **hist = NULL;
5002 		GKeyFile *kf;
5003 		char *filename;
5004 		size_t i;
5005 		char *key;
5006 		char *data;
5007 		gsize len = 0;
5008 		GError *err = NULL;
5009 	
5010 		if (edit_lib_symbols.history_list_func)
5011 			hist = edit_lib_symbols.history_list_func();
5012 	
5013 		if (hist) {
5014 			filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
5015 			kf = g_key_file_new ();
5016 			if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
5017 				if (   err->code != G_FILE_ERROR_NOENT
5018 				    && err->code != G_KEY_FILE_ERROR_NOT_FOUND) {
5019 					printf ("Warning: %s parse error: %s\n", filename, err->message);
5020 					g_key_file_free (kf);
5021 					g_free (filename);
5022 					g_clear_error (&err);
5023 					return;
5024 				}
5025 				g_clear_error (&err);
5026 			}
5027 	
5028 			/* Remove previous history group and save new history entries */
5029 			g_key_file_remove_group (kf, uuid, NULL);
5030 			for (i = 0; hist[i]; i++)
5031 			{
5032 				key = g_strdup_printf ("%zd", i);
5033 				g_key_file_set_string (kf, uuid, key, hist[i]->line);
5034 				g_free (key);
5035 			}
5036 	
5037 			/* Write history to file */
5038 			data = g_key_file_to_data (kf, &len, NULL);
5039 			if (data) {
5040 				g_file_set_contents (filename, data, len, NULL);
5041 				g_free (data);
5042 			}
5043 			g_key_file_free (kf);
5044 			g_free (filename);
5045 		}
5046 	}
5047 	
5048 	/*----------------------------------------------------------------------------*/
5049 	
5050 	static void
5051 	editor_show_connection (NMConnection *connection, NmCli *nmc)
5052 	{
5053 		nmc->print_output = NMC_PRINT_PRETTY;
5054 		nmc->multiline_output = TRUE;
5055 		nmc->escape_values = 0;
5056 	
5057 		/* Remove any previous data */
5058 		nmc_empty_output_fields (nmc);
5059 	
5060 		nmc_connection_detail (connection, nmc);
5061 	}
5062 	
5063 	static void
5064 	editor_show_setting (NMSetting *setting, NmCli *nmc)
5065 	{
5066 		printf (_("['%s' setting values]\n"),
5067 		        nm_setting_get_name (setting));
5068 	
5069 		nmc->multiline_output = TRUE;
5070 		nmc->escape_values = 0;
5071 	
5072 		/* Remove any previous data */
5073 		nmc_empty_output_fields (nmc);
5074 	
5075 		setting_details (setting, nmc);
5076 	}
5077 	
5078 	typedef enum {
5079 		NMC_EDITOR_MAIN_CMD_UNKNOWN = 0,
5080 		NMC_EDITOR_MAIN_CMD_GOTO,
5081 		NMC_EDITOR_MAIN_CMD_REMOVE,
5082 		NMC_EDITOR_MAIN_CMD_SET,
5083 		NMC_EDITOR_MAIN_CMD_DESCRIBE,
5084 		NMC_EDITOR_MAIN_CMD_PRINT,
5085 		NMC_EDITOR_MAIN_CMD_VERIFY,
5086 		NMC_EDITOR_MAIN_CMD_SAVE,
5087 		NMC_EDITOR_MAIN_CMD_BACK,
5088 		NMC_EDITOR_MAIN_CMD_HELP,
5089 		NMC_EDITOR_MAIN_CMD_NMCLI,
5090 		NMC_EDITOR_MAIN_CMD_QUIT,
5091 	} NmcEditorMainCmd;
5092 	
5093 	static NmcEditorMainCmd
5094 	parse_editor_main_cmd (const char *cmd, char **cmd_arg)
5095 	{
5096 		NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN;
5097 		char **vec;
5098 	
5099 		vec = nmc_strsplit_set (cmd, " \t", 2);
5100 		if (g_strv_length (vec) < 1) {
5101 			if (cmd_arg)
5102 				*cmd_arg = NULL;
5103 			return NMC_EDITOR_MAIN_CMD_UNKNOWN;
5104 		}
5105 	
5106 		if (matches (vec[0], "goto") == 0)
5107 			editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO;
5108 		else if (matches (vec[0], "remove") == 0)
5109 			editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE;
5110 		else if (matches (vec[0], "set") == 0)
5111 			editor_cmd = NMC_EDITOR_MAIN_CMD_SET;
5112 		else if (matches (vec[0], "describe") == 0)
5113 			editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE;
5114 		else if (matches (vec[0], "print") == 0)
5115 			editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT;
5116 		else if (matches (vec[0], "verify") == 0)
5117 			editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY;
5118 		else if (matches (vec[0], "save") == 0)
5119 			editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE;
5120 		else if (matches (vec[0], "back") == 0)
5121 			editor_cmd = NMC_EDITOR_MAIN_CMD_BACK;
5122 		else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
5123 			editor_cmd = NMC_EDITOR_MAIN_CMD_HELP;
5124 		else if (matches (vec[0], "quit") == 0)
5125 			editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT;
5126 		else if (matches (vec[0], "nmcli") == 0)
5127 			editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI;
5128 	
5129 		/* set pointer to command argument */
5130 		if (cmd_arg)
5131 			*cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL;
5132 	
5133 		g_strfreev (vec);
5134 		return editor_cmd;
5135 	}
5136 	
5137 	static void
5138 	editor_main_usage (void)
5139 	{
5140 		printf ("------------------------------------------------------------------------------\n");
5141 		/* TRANSLATORS: do not translate command names and keywords before ::
5142 		 *              However, you should translate terms enclosed in <>.
5143 		 */
5144 		printf (_("---[ Main menu ]---\n"
5145 		          "goto     [<setting> | <prop>]        :: go to a setting or property\n"
5146 		          "remove   <setting>[.<prop>] | <prop> :: remove setting or reset property value\n"
5147 		          "set      [<setting>.<prop> <value>]  :: set property value\n"
5148 		          "describe [<setting>.<prop>]          :: describe property\n"
5149 		          "print    [all]                       :: print the connection\n"
5150 		          "verify   [all]                       :: verify the connection\n"
5151 		          "save                                 :: save the connection\n"
5152 		          "back                                 :: go one level up (back)\n"
5153 		          "help/?   [<command>]                 :: print this help\n"
5154 		          "nmcli    <conf-option> <value>       :: nmcli configuration\n"
5155 		          "quit                                 :: exit nmcli\n"));
5156 		printf ("------------------------------------------------------------------------------\n");
5157 	}
5158 	
5159 	static void
5160 	editor_main_help (const char *command)
5161 	{
5162 		if (!command)
5163 			editor_main_usage ();
5164 		else {
5165 			/* detailed command descriptions */
5166 			NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL);
5167 	
5168 			switch (cmd) {
5169 			case NMC_EDITOR_MAIN_CMD_GOTO:
5170 				printf (_("goto <setting>[.<prop>] | <prop>  :: enter setting/property for editing\n\n"
5171 				          "This command enters into a setting or property for editing it.\n\n"
5172 				          "Examples: nmcli> goto connection\n"
5173 				          "          nmcli connection> goto secondaries\n"
5174 				          "          nmcli> goto ipv4.addresses\n"));
5175 				break;
5176 			case NMC_EDITOR_MAIN_CMD_REMOVE:
5177 				printf (_("remove <setting>[.<prop>]  :: remove setting or reset property value\n\n"
5178 				          "This command removes an entire setting from the connection, or if a property\n"
5179 				          "is given, resets that property to the default value.\n\n"
5180 				          "Examples: nmcli> remove wifi-sec\n"
5181 				          "          nmcli> remove eth.mtu\n"));
5182 				break;
5183 			case NMC_EDITOR_MAIN_CMD_SET:
5184 				printf (_("set [<setting>.<prop> <value>]  :: set property value\n\n"
5185 				          "This command sets property value.\n\n"
5186 				          "Example: nmcli> set con.id My connection\n"));
5187 				break;
5188 			case NMC_EDITOR_MAIN_CMD_DESCRIBE:
5189 				printf (_("describe [<setting>.<prop>]  :: describe property\n\n"
5190 				          "Shows property description. You can consult nm-settings(5) "
5191 				          "manual page to see all NM settings and properties.\n"));
5192 				break;
5193 			case NMC_EDITOR_MAIN_CMD_PRINT:
5194 				printf (_("print [all]  :: print setting or connection values\n\n"
5195 				          "Shows current property or the whole connection.\n\n"
5196 				          "Example: nmcli ipv4> print all\n"));
5197 				break;
5198 			case NMC_EDITOR_MAIN_CMD_VERIFY:
5199 				printf (_("verify [all]  :: verify setting or connection validity\n\n"
5200 				          "Verifies whether the setting or connection is valid and can "
5201 				          "be saved later. It indicates invalid values on error.\n\n"
5202 				          "Examples: nmcli> verify\n"
5203 				          "          nmcli bond> verify\n"));
5204 				break;
5205 			case NMC_EDITOR_MAIN_CMD_SAVE:
5206 				printf (_("save  :: save the connection\n\n"
5207 				          "Sends the connection to NetworkManager that will save it.\n"));
5208 				break;
5209 			case NMC_EDITOR_MAIN_CMD_BACK:
5210 				printf (_("back  :: go to upper menu level\n\n"));
5211 				break;
5212 			case NMC_EDITOR_MAIN_CMD_HELP:
5213 				printf (_("help/? [<command>]  :: help for the nmcli commands\n\n"));
5214 				break;
5215 			case NMC_EDITOR_MAIN_CMD_NMCLI:
5216 				printf (_("nmcli [<conf-option> <value>]  :: nmcli configuration\n\n"
5217 				          "Configures nmcli. The following options are available:\n"
5218 				          "status-line yes | no        [default: no]\n"
5219 				          "save-confirmation yes | no  [default: yes]\n"
5220 				          "prompt-color <0-8>          [default: 0]\n"
5221 				          "  0 = normal\n"
5222 				          "  1 = \33[30mblack\33[0m\n"
5223 				          "  2 = \33[31mred\33[0m\n"
5224 				          "  3 = \33[32mgreen\33[0m\n"
5225 				          "  4 = \33[33myellow\33[0m\n"
5226 				          "  5 = \33[34mblue\33[0m\n"
5227 				          "  6 = \33[35mmagenta\33[0m\n"
5228 				          "  7 = \33[36mcyan\33[0m\n"
5229 				          "  8 = \33[37mwhite\33[0m\n"
5230 				          "\n"
5231 				          "Examples: nmcli> nmcli status-line yes\n"
5232 				          "          nmcli> nmcli save-confirmation no\n"
5233 				          "          nmcli> nmcli prompt-color 3\n"));
5234 				break;
5235 			case NMC_EDITOR_MAIN_CMD_QUIT:
5236 				printf (_("quit  :: exit nmcli\n\n"
5237 				          "This command exits nmcli. When the connection being edited "
5238 				          "is not saved, the user is asked to confirm the action.\n"));
5239 				break;
5240 			default:
5241 				printf (_("Unknown command: '%s'\n"), command);
5242 				break;
5243 			}
5244 		}
5245 	}
5246 	
5247 	typedef enum {
5248 		NMC_EDITOR_SUB_CMD_UNKNOWN = 0,
5249 		NMC_EDITOR_SUB_CMD_SET,
5250 		NMC_EDITOR_SUB_CMD_ADD,
5251 		NMC_EDITOR_SUB_CMD_CHANGE,
5252 		NMC_EDITOR_SUB_CMD_REMOVE,
5253 		NMC_EDITOR_SUB_CMD_DESCRIBE,
5254 		NMC_EDITOR_SUB_CMD_PRINT,
5255 		NMC_EDITOR_SUB_CMD_BACK,
5256 		NMC_EDITOR_SUB_CMD_HELP,
5257 		NMC_EDITOR_SUB_CMD_QUIT
5258 	} NmcEditorSubCmd;
5259 	
5260 	static NmcEditorSubCmd
5261 	parse_editor_sub_cmd (const char *cmd, char **cmd_arg)
5262 	{
5263 		NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN;
5264 		char **vec;
5265 	
5266 		vec = nmc_strsplit_set (cmd, " \t", 2);
5267 		if (g_strv_length (vec) < 1) {
5268 			if (cmd_arg)
5269 				*cmd_arg = NULL;
5270 			return NMC_EDITOR_SUB_CMD_UNKNOWN;
5271 		}
5272 	
5273 		if (matches (vec[0], "set") == 0)
5274 			editor_cmd = NMC_EDITOR_SUB_CMD_SET;
5275 		else if (matches (vec[0], "add") == 0)
5276 			editor_cmd = NMC_EDITOR_SUB_CMD_ADD;
5277 		else if (matches (vec[0], "change") == 0)
5278 			editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE;
5279 		else if (matches (vec[0], "remove") == 0)
5280 			editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE;
5281 		else if (matches (vec[0], "describe") == 0)
5282 			editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE;
5283 		else if (matches (vec[0], "print") == 0)
5284 			editor_cmd = NMC_EDITOR_SUB_CMD_PRINT;
5285 		else if (matches (vec[0], "back") == 0)
5286 			editor_cmd = NMC_EDITOR_SUB_CMD_BACK;
5287 		else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
5288 			editor_cmd = NMC_EDITOR_SUB_CMD_HELP;
5289 		else if (matches (vec[0], "quit") == 0)
5290 			editor_cmd = NMC_EDITOR_SUB_CMD_QUIT;
5291 	
5292 		/* set pointer to command argument */
5293 		if (cmd_arg)
5294 			*cmd_arg = g_strdup (vec[1]);
5295 	
5296 		g_strfreev (vec);
5297 		return editor_cmd;
5298 	}
5299 	
5300 	static void
5301 	editor_sub_help (void)
5302 	{
5303 		printf ("------------------------------------------------------------------------------\n");
5304 		/* TRANSLATORS: do not translate command names and keywords before ::
5305 		 *              However, you should translate terms enclosed in <>.
5306 		 */
5307 		printf (_("---[ Property menu ]---\n"
5308 		          "set      [<value>]               :: set new value\n"
5309 		          "add      [<value>]               :: add new option to the property\n"
5310 		          "change                           :: change current value\n"
5311 		          "remove   [<index> | <option>]    :: delete the value\n"
5312 		          "describe                         :: describe property\n"
5313 		          "print    [setting | connection]  :: print property (setting/connection) value(s)\n"
5314 		          "back                             :: go to upper level\n"
5315 		          "help/?   [<command>]             :: print this help or command description\n"
5316 		          "quit                             :: exit nmcli\n"));
5317 		printf ("------------------------------------------------------------------------------\n");
5318 	}
5319 	
5320 	static void
5321 	editor_sub_usage (const char *command)
5322 	{
5323 	
5324 		if (!command)
5325 			editor_sub_help ();
5326 		else {
5327 			/* detailed command descriptions */
5328 			NmcEditorSubCmd cmdsub = parse_editor_sub_cmd (command, NULL);
5329 	
5330 			switch (cmdsub) {
5331 			case NMC_EDITOR_SUB_CMD_SET:
5332 				printf (_("set [<value>]  :: set new value\n\n"
5333 				          "This command sets provided <value> to this property\n"));
5334 				break;
5335 			case NMC_EDITOR_SUB_CMD_ADD:
5336 				printf (_("add [<value>]  :: add new option to the property\n\n"
5337 				          "This command add provided <value> to this property, if "
5338 				          "the property is of a container type. For single-valued "
5339 				          "properties it replaces the value (same as 'set').\n"));
5340 				break;
5341 			case NMC_EDITOR_SUB_CMD_CHANGE:
5342 				printf (_("change  :: change current value\n\n"
5343 				          "Displays current value and allows editing it.\n"));
5344 				break;
5345 			case NMC_EDITOR_SUB_CMD_REMOVE:
5346 				printf (_("remove [<index>|<option>]  :: delete the value\n\n"
5347 				          "Removes the property value (sets it to default).\n"));
5348 				break;
5349 			case NMC_EDITOR_SUB_CMD_DESCRIBE:
5350 				printf (_("describe  :: describe property\n\n"
5351 				          "Shows property description. You can consult nm-settings(5) "
5352 				          "manual page to see all NM settings and properties.\n"));
5353 				break;
5354 			case NMC_EDITOR_SUB_CMD_PRINT:
5355 				printf (_("print [property|setting|connection]  :: print property (setting, connection) value(s)\n\n"
5356 				          "Shows property value. Providing an argument you can also display "
5357 				          "values for the whole setting or connection.\n"));
5358 				break;
5359 			case NMC_EDITOR_SUB_CMD_BACK:
5360 				printf (_("back  :: go to upper menu level\n\n"));
5361 				break;
5362 			case NMC_EDITOR_SUB_CMD_HELP:
5363 				printf (_("help/? [<command>]  :: help for nmcli commands\n\n"));
5364 				break;
5365 			case NMC_EDITOR_SUB_CMD_QUIT:
5366 				printf (_("quit  :: exit nmcli\n\n"
5367 				          "This command exits nmcli. When the connection being edited "
5368 				          "is not saved, the user is asked to confirm the action.\n"));
5369 				break;
5370 			default:
5371 				printf (_("Unknown command: '%s'\n"), command);
5372 				break;
5373 			}
5374 		}
5375 	}
5376 	
5377 	/*----------------------------------------------------------------------------*/
5378 	
5379 	static gboolean nmc_editor_cb_called;
5380 	static GError *nmc_editor_error;
5381 	static GMutex nmc_editor_mutex;
5382 	static GCond nmc_editor_cond;
5383 	
5384 	/*
5385 	 * Store 'error' to shared 'nmc_editor_error' and signal the condition
5386 	 * so that the 'editor-thread' thread could process that.
5387 	 */
5388 	static void
5389 	set_and_signal_error (GError *error)
5390 	{
5391 		g_mutex_lock (&nmc_editor_mutex);
5392 		nmc_editor_cb_called = TRUE;
5393 		nmc_editor_error = error ? g_error_copy (error) : NULL;
5394 		g_cond_signal (&nmc_editor_cond);
5395 		g_mutex_unlock (&nmc_editor_mutex);
5396 	}
5397 	
5398 	static void
5399 	add_connection_editor_cb (NMRemoteSettings *settings,
5400 	                          NMRemoteConnection *connection,
5401 	                          GError *error,
5402 	                          gpointer user_data)
5403 	{
5404 		set_and_signal_error (error);
5405 	}
5406 	
5407 	static void
5408 	update_connection_editor_cb (NMRemoteConnection *connection,
5409 	                             GError *error,
5410 	                             gpointer user_data)
5411 	{
5412 		set_and_signal_error (error);
5413 	}
5414 	
5415 	/*----------------------------------------------------------------------------*/
5416 	
5417 	static void
5418 	print_property_description (NMSetting *setting, const char *prop_name)
5419 	{
5420 		char *desc;
5421 	
5422 		desc = nmc_setting_get_property_desc (setting, prop_name);
5423 		printf ("\n=== [%s] ===\n%s\n", prop_name, desc);
5424 		g_free (desc);
5425 	}
5426 	
5427 	static void
5428 	print_setting_description (NMSetting *setting)
5429 	{
5430 		/* Show description of all properties */
5431 		char **all_props;
5432 		int i;
5433 	
5434 		all_props = nmc_setting_get_valid_properties (setting);
5435 		printf (("<<< %s >>>\n"), nm_setting_get_name (setting));
5436 		for (i = 0; all_props && all_props[i]; i++)
5437 			print_property_description (setting, all_props[i]);
5438 		g_strfreev (all_props);
5439 	}
5440 	
5441 	static gboolean
5442 	connection_remove_setting (NMConnection *connection, NMSetting *setting)
5443 	{
5444 		gboolean mandatory;
5445 	
5446 		mandatory = is_setting_mandatory (connection, setting);
5447 		if (!mandatory) {
5448 			nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting));
5449 			return TRUE;
5450 		}
5451 		printf (_("Error: setting '%s' is mandatory and cannot be removed.\n"),
5452 		        nm_setting_get_name (setting));
5453 		return FALSE;
5454 	}
5455 	
5456 	static void
5457 	editor_show_status_line (NMConnection *connection, gboolean dirty)
5458 	{
5459 		NMSettingConnection *s_con;
5460 		const char *con_type, *con_id, *con_uuid;
5461 	
5462 		s_con = nm_connection_get_setting_connection (connection);
5463 		g_assert (s_con);
5464 		con_type = nm_setting_connection_get_connection_type (s_con);
5465 		con_id = nm_connection_get_id (connection);
5466 		con_uuid = nm_connection_get_uuid (connection);
5467 	
5468 		/* TRANSLATORS: status line in nmcli connection editor */
5469 		printf (_("[ Connection type: %s | name: %s | UUID: %s | dirty: %s ]\n"),
5470 		        con_type, con_id, con_uuid, dirty ? _("yes") : _("no"));
5471 	}
5472 	
5473 	/*
5474 	 * Submenu for detailed property editing
5475 	 * Return: TRUE - continue;  FALSE - should quit
5476 	 */
5477 	static gboolean
5478 	property_edit_submenu (NmCli *nmc,
5479 	                       NMConnection *connection,
5480 	                       NMRemoteConnection *rem_con,
5481 	                       NMSetting *curr_setting,
5482 	                       const char *prop_name)
5483 	{
5484 		NmcEditorSubCmd cmdsub;
5485 		gboolean cmd_property_loop = TRUE;
5486 		gboolean should_quit = FALSE;
5487 		char *prop_val_user, *tmp_prompt;
5488 		gboolean set_result;
5489 		GError *tmp_err = NULL;
5490 		char *prompt;
5491 		gboolean dirty;
5492 		GValue prop_g_value = G_VALUE_INIT;
5493 	
5494 		prompt = nmc_colorize (nmc->editor_prompt_color, "nmcli %s.%s> ",
5495 		                       nm_setting_get_name (curr_setting), prop_name);
5496 	
5497 		while (cmd_property_loop) {
5498 			char *cmd_property_user;
5499 			char *cmd_property_arg;
5500 	
5501 			/* Connection is dirty? (not saved or differs from the saved) */
5502 			dirty = !nm_connection_compare (connection,
5503 			                                rem_con ? NM_CONNECTION (rem_con) : NULL,
5504 			                                NM_SETTING_COMPARE_FLAG_EXACT);
5505 	
5506 			if (nmc->editor_status_line)
5507 				editor_show_status_line (connection, dirty);
5508 	
5509 			cmd_property_user = readline_x (prompt);
5510 			if (!cmd_property_user || *cmd_property_user == '\0')
5511 				continue;
5512 			cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg);
5513 	
5514 			switch (cmdsub) {
5515 			case NMC_EDITOR_SUB_CMD_SET:
5516 			case NMC_EDITOR_SUB_CMD_ADD:
5517 				/* list, arrays,...: SET replaces the whole property value
5518 				 *                   ADD adds the new value(s)
5519 				 * single values:  : both SET and ADD sets the new value
5520 				 */
5521 				if (!cmd_property_arg) {
5522 					tmp_prompt = g_strdup_printf (_("Enter '%s' value: "), prop_name);
5523 					prop_val_user = readline_x (tmp_prompt);
5524 					g_free (tmp_prompt);
5525 				} else
5526 					prop_val_user = g_strdup (cmd_property_arg);
5527 	
5528 				/* nmc_setting_set_property() only adds new value, thus we have to
5529 				 * remove the original value and save it for error cases.
5530 				 */
5531 				if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
5532 					nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
5533 					nmc_property_set_default_value (curr_setting, prop_name);
5534 				}
5535 	
5536 				set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err);
5537 				g_free (prop_val_user);
5538 				if (!set_result) {
5539 					printf (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
5540 					g_clear_error (&tmp_err);
5541 					if (cmdsub == NMC_EDITOR_SUB_CMD_SET)
5542 						nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
5543 				}
5544 				if (G_IS_VALUE (&prop_g_value))
5545 					g_value_unset (&prop_g_value);
5546 				break;
5547 	
5548 			case NMC_EDITOR_SUB_CMD_CHANGE:
5549 				*edit_lib_symbols.rl_startup_hook_x = set_deftext;
5550 				pre_input_deftext = nmc_setting_get_property_out2in (curr_setting, prop_name, NULL);
5551 				tmp_prompt = g_strdup_printf (_("Edit '%s' value: "), prop_name);
5552 				prop_val_user = readline_x (tmp_prompt);
5553 	
5554 				nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
5555 				nmc_property_set_default_value (curr_setting, prop_name);
5556 	
5557 				if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) {
5558 					printf (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
5559 					g_clear_error (&tmp_err);
5560 					nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
5561 				}
5562 				g_free (prop_val_user);
5563 				g_free (tmp_prompt);
5564 				if (G_IS_VALUE (&prop_g_value))
5565 					g_value_unset (&prop_g_value);
5566 				break;
5567 	
5568 			case NMC_EDITOR_SUB_CMD_REMOVE:
5569 				if (cmd_property_arg) {
5570 					unsigned long val_int = G_MAXUINT32;
5571 					char *option = NULL;
5572 	
5573 					if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int))
5574 						option = g_strdup (cmd_property_arg);
5575 	
5576 					if (!nmc_setting_remove_property_option (curr_setting, prop_name,
5577 					                                         option ? g_strstrip (option) : NULL,
5578 					                                         (guint32) val_int,
5579 					                                         &tmp_err)) {
5580 						printf (_("Error: %s\n"), tmp_err->message);
5581 						g_clear_error (&tmp_err);
5582 					}
5583 					g_free (option);
5584 				} else {
5585 					if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) {
5586 						printf (_("Error: failed to remove value of '%s': %s\n"), prop_name,
5587 						        tmp_err->message);
5588 						g_clear_error (&tmp_err);
5589 					}
5590 				}
5591 				break;
5592 	
5593 			case NMC_EDITOR_SUB_CMD_DESCRIBE:
5594 				/* Show property description */
5595 				print_property_description (curr_setting, prop_name);
5596 				break;
5597 	
5598 			case NMC_EDITOR_SUB_CMD_PRINT:
5599 				/* Print current connection settings/properties */
5600 				if (cmd_property_arg) {
5601 					if (matches (cmd_property_arg, "setting") == 0)
5602 						editor_show_setting (curr_setting, nmc);
5603 					else if (   matches (cmd_property_arg, "connection") == 0
5604 					         || matches (cmd_property_arg, "all") == 0)
5605 						editor_show_connection (connection, nmc);
5606 					else
5607 						printf (_("Unknown command argument: '%s'\n"), cmd_property_arg);
5608 				} else {
5609 					char *prop_val =  nmc_setting_get_property (curr_setting, prop_name, NULL);
5610 					printf ("%s: %s\n", prop_name, prop_val);
5611 					g_free (prop_val);
5612 				}
5613 				break;
5614 	
5615 			case NMC_EDITOR_SUB_CMD_BACK:
5616 				cmd_property_loop = FALSE;
5617 				break;
5618 	
5619 			case NMC_EDITOR_SUB_CMD_HELP:
5620 				editor_sub_usage (cmd_property_arg);
5621 				break;
5622 	
5623 			case NMC_EDITOR_SUB_CMD_QUIT:
5624 				if (dirty) {
5625 					char *tmp_str;
5626 					do {
5627 						tmp_str = nmc_get_user_input (_("The connection is not saved. "
5628 						                                "Do you really want to quit? [y/n]\n"));
5629 					} while (!tmp_str);
5630 					if (matches (tmp_str, "yes") == 0) {
5631 						cmd_property_loop = FALSE;
5632 						should_quit = TRUE;  /* we will quit nmcli */
5633 					}
5634 					g_free (tmp_str);
5635 				} else {
5636 					cmd_property_loop = FALSE;
5637 					should_quit = TRUE;  /* we will quit nmcli */
5638 				}
5639 				break;
5640 	
5641 			case NMC_EDITOR_SUB_CMD_UNKNOWN:
5642 			default:
5643 				printf (_("Unknown command: '%s'\n"), cmd_property_user);
5644 				break;
5645 			}
5646 			g_free (cmd_property_user);
5647 			g_free (cmd_property_arg);
5648 		}
5649 		g_free (prompt);
5650 	
5651 		return !should_quit;
5652 	}
5653 	
5654 	/*
5655 	 * Split 'str' in the following format:  [[[setting.]property] [value]]
5656 	 * and return the components in 'setting', 'property' and 'value'
5657 	 * Use g_free() to deallocate the returned strings.
5658 	 */
5659 	static void
5660 	split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value)
5661 	{
5662 		char **args, **items;
5663 	
5664 		if (!str)
5665 			return;
5666 	
5667 		args = nmc_strsplit_set (str, " \t", 2);
5668 		if (args[0]) {
5669 			items = nmc_strsplit_set (args[0], ".", 2);
5670 			if (g_strv_length (items) == 2) {
5671 				if (setting)
5672 					*setting = g_strdup (items[0]);
5673 				if (property)
5674 					*property = g_strdup (items[1]);
5675 			} else {
5676 				if (property)
5677 					*property = g_strdup (items[0]);
5678 			}
5679 			g_strfreev (items);
5680 	
5681 			if (value && args[1])
5682 				*value = g_strstrip (g_strdup (args[1]));
5683 		}
5684 		g_strfreev (args);
5685 	}
5686 	
5687 	static NMSetting *
5688 	is_setting_valid (NMConnection *connection, const NameItem *valid_settings, char *setting)
5689 	{
5690 		const char *setting_name;
5691 	
5692 		if (!(setting_name = check_valid_name (setting, valid_settings, NULL)))
5693 			return NULL;
5694 		return nm_connection_get_setting_by_name (connection, setting_name);
5695 	}
5696 	
5697 	static char *
5698 	is_property_valid (NMSetting *setting, const char *property, GError **error)
5699 	{
5700 		char **valid_props = NULL;
5701 		const char *prop_name;
5702 		char *ret;
5703 	
5704 		valid_props = nmc_setting_get_valid_properties (setting);
5705 		prop_name = nmc_string_is_valid (property, (const char **) valid_props, error);
5706 		ret = prop_name ? g_strdup (prop_name) : NULL;
5707 		g_strfreev (valid_props);
5708 		return ret;
5709 	}
5710 	
5711 	static NMSetting *
5712 	create_setting_by_name (const char *name, const NameItem *valid_settings)
5713 	{
5714 		const char *setting_name;
5715 		NMSetting *setting = NULL;
5716 	
5717 		/* Get a valid setting name */
5718 		setting_name = check_valid_name (name, valid_settings, NULL);
5719 	
5720 		if (setting_name) {
5721 			setting = nmc_setting_new_for_name (setting_name);
5722 			if (!setting)
5723 				return NULL; /* This should really not happen */
5724 			nmc_setting_custom_init (setting);
5725 		}
5726 		return setting;
5727 	}
5728 	
5729 	static const char *
5730 	ask_check_setting (const char *arg,
5731 	                   const NameItem *valid_settings_arr,
5732 	                   const char *valid_settings_str)
5733 	{
5734 		char *setting_name_user;
5735 		const char *setting_name;
5736 		GError *err = NULL;
5737 	
5738 		if (!arg) {
5739 			printf (_("Available settings: %s\n"), valid_settings_str);
5740 			setting_name_user = nmc_get_user_input (EDITOR_PROMPT_SETTING);
5741 		} else
5742 			setting_name_user = g_strdup (arg);
5743 	
5744 		if (setting_name_user)
5745 			g_strstrip (setting_name_user);
5746 	
5747 		if (!(setting_name = check_valid_name (setting_name_user, valid_settings_arr, &err))) {
5748 			printf (_("Error: invalid setting name; %s\n"), err->message);
5749 			g_clear_error (&err);
5750 		}
5751 		g_free (setting_name_user);
5752 		return setting_name;
5753 	}
5754 	
5755 	static const char *
5756 	ask_check_property (const char *arg,
5757 	                    const char **valid_props,
5758 	                    const char *valid_props_str)
5759 	{
5760 		char *prop_name_user;
5761 		const char *prop_name;
5762 		GError *tmp_err = NULL;
5763 	
5764 		if (!arg) {
5765 			printf (_("Available properties: %s\n"), valid_props_str);
5766 			prop_name_user = readline_x (EDITOR_PROMPT_PROPERTY);
5767 			if (prop_name_user)
5768 				g_strstrip (prop_name_user);
5769 		} else
5770 			prop_name_user = g_strdup (arg);
5771 	
5772 		if (!(prop_name = nmc_string_is_valid (prop_name_user, valid_props, &tmp_err))) {
5773 			printf (_("Error: property %s\n"), tmp_err->message);
5774 			g_clear_error (&tmp_err);
5775 		}
5776 		g_free (prop_name_user);
5777 		return prop_name;
5778 	}
5779 	
5780 	static gboolean
5781 	confirm_connection_saving (NMConnection *local, NMConnection *remote)
5782 	{
5783 		NMSettingConnection *s_con_loc, *s_con_rem;
5784 		gboolean ac_local, ac_remote;
5785 		gboolean confirmed = TRUE;
5786 	
5787 		s_con_loc = nm_connection_get_setting_connection (local);
5788 		g_assert (s_con_loc);
5789 		ac_local = nm_setting_connection_get_autoconnect (s_con_loc);
5790 	
5791 		if (remote) {
5792 			s_con_rem = nm_connection_get_setting_connection (remote);
5793 			g_assert (s_con_rem);
5794 			ac_remote = nm_setting_connection_get_autoconnect (s_con_rem);
5795 		} else
5796 			ac_remote = FALSE;
5797 	
5798 		if (ac_local && !ac_remote) {
5799 			char *answer;
5800 			answer = nmc_get_user_input (_("Saving the connection with 'autoconnect=yes'. "
5801 			                               "That might result in an immediate activation of the connection.\n"
5802 			                               "Do you still want to save? [yes] "));
5803 			if (!answer || matches (answer, "yes") == 0)
5804 				confirmed = TRUE;
5805 			else
5806 				confirmed = FALSE;
5807 			g_free (answer);
5808 		}
5809 		return confirmed;
5810 	}
5811 	
5812 	typedef	struct {
5813 		guint level;
5814 		char *main_prompt;
5815 		NMSetting *curr_setting;
5816 		char **valid_props;
5817 		char *valid_props_str;
5818 	} NmcEditorMenuContext;
5819 	
5820 	static void
5821 	menu_switch_to_level0 (NmcEditorMenuContext *menu_ctx,
5822 	                       const char *prompt,
5823 	                       NmcTermColor prompt_color)
5824 	{
5825 		menu_ctx->level = 0;
5826 		g_free (menu_ctx->main_prompt);
5827 		menu_ctx->main_prompt = nmc_colorize (prompt_color, "%s", prompt);
5828 		menu_ctx->curr_setting = NULL;
5829 		g_strfreev (menu_ctx->valid_props);
5830 		menu_ctx->valid_props = NULL;
5831 		g_free (menu_ctx->valid_props_str);
5832 		menu_ctx->valid_props_str = NULL;
5833 	}
5834 	
5835 	static void
5836 	menu_switch_to_level1 (NmcEditorMenuContext *menu_ctx,
5837 	                       NMSetting *setting,
5838 	                       const char *setting_name,
5839 	                       NmcTermColor prompt_color)
5840 	{
5841 		menu_ctx->level = 1;
5842 		g_free (menu_ctx->main_prompt);
5843 		menu_ctx->main_prompt = nmc_colorize (prompt_color, "nmcli %s> ", setting_name);
5844 		menu_ctx->curr_setting = setting;
5845 		g_strfreev (menu_ctx->valid_props);
5846 		menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting);
5847 		g_free (menu_ctx->valid_props_str);
5848 		menu_ctx->valid_props_str = g_strjoinv (", ", menu_ctx->valid_props);
5849 	}
5850 	
5851 	static gboolean
5852 	editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
5853 	{
5854 		NMRemoteConnection *rem_con = NULL;
5855 		NmcEditorMainCmd cmd;
5856 		char *cmd_user;
5857 		gboolean cmd_loop = TRUE;
5858 		char *cmd_arg = NULL;
5859 		char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v;
5860 		const char *BASE_PROMPT = "nmcli> ";
5861 		const NameItem *valid_settings_arr = NULL;
5862 		char *valid_settings_str = NULL;
5863 		AddConnectionInfo *info = NULL;
5864 		gboolean dirty;
5865 		GError *err1 = NULL;
5866 		NmcEditorMenuContext menu_ctx;
5867 	
5868 		valid_settings_arr = get_valid_settings_array (connection_type);
5869 		valid_settings_str = get_valid_options_string (valid_settings_arr);
5870 		printf (_("You may edit the following settings: %s\n"), valid_settings_str);
5871 	
5872 		menu_ctx.level = 0;
5873 		menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, BASE_PROMPT);
5874 		menu_ctx.curr_setting = NULL;
5875 		menu_ctx.valid_props = NULL;
5876 		menu_ctx.valid_props_str = NULL;
5877 	
5878 		while (cmd_loop) {
5879 			if (!rem_con)
5880 				rem_con = nm_remote_settings_get_connection_by_uuid (nmc->system_settings,
5881 				                                                     nm_connection_get_uuid (connection));
5882 	
5883 			/* Connection is dirty? (not saved or differs from the saved) */
5884 			dirty = !nm_connection_compare (connection,
5885 			                                rem_con ? NM_CONNECTION (rem_con) : NULL,
5886 			                                NM_SETTING_COMPARE_FLAG_EXACT);
5887 			if (nmc->editor_status_line)
5888 				editor_show_status_line (connection, dirty);
5889 	
5890 			cmd_user = readline_x (menu_ctx.main_prompt);
5891 			if (!cmd_user || *cmd_user == '\0')
5892 				continue;
5893 			cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
5894 	
5895 			cmd_arg_s = NULL;
5896 			cmd_arg_p = NULL;
5897 			cmd_arg_v = NULL;
5898 			split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
5899 			switch (cmd) {
5900 			case NMC_EDITOR_MAIN_CMD_SET:
5901 				/* Set property value */
5902 				if (!cmd_arg) {
5903 					if (menu_ctx.level == 1) {
5904 						const char *prop_name;
5905 						char *prop_val_user = NULL;
5906 						char *tmp_prompt;
5907 						const char *avals;
5908 						GError *tmp_err = NULL;
5909 	
5910 						prop_name = ask_check_property (cmd_arg,
5911 						                                (const char **) menu_ctx.valid_props,
5912 						                                menu_ctx.valid_props_str);
5913 						if (!prop_name)
5914 							break;
5915 	
5916 						avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name);
5917 						if (avals)
5918 							printf (_("Allowed values for '%s' property: %s\n"), prop_name, avals);
5919 	
5920 						tmp_prompt = g_strdup_printf (_("Enter '%s' value: "), prop_name);
5921 						prop_val_user = readline_x (tmp_prompt);
5922 						g_free (tmp_prompt);
5923 	
5924 						/* Set property value */
5925 						if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) {
5926 							printf (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
5927 							g_clear_error (&tmp_err);
5928 						}
5929 					} else {
5930 						printf (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
5931 						printf (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n"));
5932 					}
5933 				} else {
5934 					NMSetting *ss = NULL;
5935 					gboolean created_ss = FALSE;
5936 					char *prop_name;
5937 					char *tmp_prompt;
5938 					GError *tmp_err = NULL;
5939 	
5940 					if (cmd_arg_s) {
5941 						/* setting provided as "setting.property" */
5942 						ss = is_setting_valid (connection, valid_settings_arr, cmd_arg_s);
5943 						if (!ss) {
5944 							ss = create_setting_by_name (cmd_arg_s, valid_settings_arr);
5945 							if (!ss) {
5946 								printf (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
5947 								        cmd_arg_s, valid_settings_str);
5948 								break;
5949 							}
5950 							created_ss = TRUE;
5951 						}
5952 					} else {
5953 						if (menu_ctx.curr_setting)
5954 							ss = menu_ctx.curr_setting;
5955 						else {
5956 							printf (_("Error: missing setting for '%s' property\n"), cmd_arg_p);
5957 							break;
5958 						}
5959 					}
5960 	
5961 					prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
5962 					if (!prop_name) {
5963 						printf (_("Error: invalid property: %s\n"), tmp_err->message);
5964 						g_clear_error (&tmp_err);
5965 						if (created_ss)
5966 							g_object_unref (ss);
5967 						break;
5968 					}
5969 	
5970 	
5971 	
5972 					/* Ask for value */
5973 					if (!cmd_arg_v) {
5974 						const char *avals = nmc_setting_get_property_allowed_values (ss, prop_name);
5975 						if (avals)
5976 							printf (_("Allowed values for '%s' property: %s\n"), prop_name, avals);
5977 	
5978 						tmp_prompt = g_strdup_printf (_("Enter '%s' value: "), prop_name);
5979 						cmd_arg_v = readline_x (tmp_prompt);
5980 						g_free (tmp_prompt);
5981 					}
5982 	
5983 					/* Set property value */
5984 					if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) {
5985 						printf (_("Error: failed to set '%s' property: %s\n"),
5986 						        prop_name, tmp_err->message);
5987 						g_clear_error (&tmp_err);
5988 					}
5989 	
5990 					if (created_ss)
5991 						nm_connection_add_setting (connection, ss);
5992 					g_free (prop_name);
5993 				}
5994 				break;
5995 	
5996 			case NMC_EDITOR_MAIN_CMD_GOTO:
5997 				/* cmd_arg_s != NULL means 'setting.property' argument */
5998 				if (menu_ctx.level == 0 || cmd_arg_s) {
5999 					/* in top level - no setting selected yet */
6000 					const char *setting_name;
6001 					NMSetting *setting;
6002 					const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p;
6003 	
6004 					setting_name = ask_check_setting (user_arg, valid_settings_arr, valid_settings_str);
6005 					if (!setting_name)
6006 						break;
6007 	
6008 					setting = nm_connection_get_setting_by_name (connection, setting_name);
6009 					if (!setting) {
6010 						setting = nmc_setting_new_for_name (setting_name);
6011 						if (!setting) {
6012 							printf (_("Error: unknown setting '%s'\n"), setting_name);
6013 							break;
6014 						}
6015 						nmc_setting_custom_init (setting);
6016 						nm_connection_add_setting (connection, setting);
6017 					}
6018 					/* Set global variable for use in TAB completion */
6019 					nmc_completion_setting = setting;
6020 	
6021 					/* Switch to level 1 */
6022 					menu_switch_to_level1 (&menu_ctx, setting, setting_name, nmc->editor_prompt_color);
6023 	
6024 					if (!cmd_arg_s) {
6025 						printf (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str);
6026 						break;
6027 					}
6028 				}
6029 				if (menu_ctx.level == 1 || cmd_arg_s) {
6030 					/* level 1 - setting selected */
6031 					const char *prop_name;
6032 	
6033 					prop_name = ask_check_property (cmd_arg_p,
6034 					                                (const char **) menu_ctx.valid_props,
6035 					                                menu_ctx.valid_props_str);
6036 					if (!prop_name)
6037 						break;
6038 	
6039 					/* submenu - level 2 - editing properties */
6040 					cmd_loop = property_edit_submenu (nmc, connection, rem_con, menu_ctx.curr_setting, prop_name);
6041 				}
6042 				break;
6043 	
6044 			case NMC_EDITOR_MAIN_CMD_REMOVE:
6045 				/* Remove setting from connection, or delete value of a property */
6046 				if (!cmd_arg) {
6047 					if (menu_ctx.level == 1) {
6048 						GError *tmp_err = NULL;
6049 						const char *prop_name;
6050 	
6051 						prop_name = ask_check_property (cmd_arg,
6052 						                                (const char **) menu_ctx.valid_props,
6053 						                                menu_ctx.valid_props_str);
6054 						if (!prop_name)
6055 							break;
6056 	
6057 						/* Delete property value */
6058 						if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) {
6059 							printf (_("Error: failed to remove value of '%s': %s\n"), prop_name,
6060 							        tmp_err->message);
6061 							g_clear_error (&tmp_err);
6062 						}
6063 					} else
6064 						printf (_("Error: no argument given; valid are [%s]\n"), valid_settings_str);
6065 				} else {
6066 					NMSetting *ss = NULL;
6067 					gboolean descr_all;
6068 					char *user_s;
6069 	
6070 					/* cmd_arg_s != NULL means argument is "setting.property" */
6071 					descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
6072 					user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
6073 					if (user_s) {
6074 						ss = is_setting_valid (connection, valid_settings_arr, user_s);
6075 						if (!ss) {
6076 							if (check_valid_name (user_s, valid_settings_arr, NULL))
6077 								printf (_("Setting '%s' is not present in the connection.\n"), user_s);
6078 							else
6079 								printf (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
6080 								        user_s, valid_settings_str);
6081 							break;
6082 						}
6083 					} else
6084 						ss = menu_ctx.curr_setting;
6085 	
6086 					if (descr_all) {
6087 						/* Remove setting from the connection */
6088 						connection_remove_setting (connection, ss);
(1) Event original: "ss == menu_ctx.curr_setting" looks like the original copy.
Also see events: [copy_paste_error]
6089 						if (ss == menu_ctx.curr_setting) {
6090 							/* If we removed the setting we are in, go up */
6091 							menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
6092 							nmc_completion_setting = NULL;  /* for TAB completion */
6093 						}
6094 					} else {
6095 						GError *tmp_err = NULL;
6096 						char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
6097 						if (prop_name) {
6098 							/* Delete property value */
6099 							if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) {
6100 								printf (_("Error: failed to remove value of '%s': %s\n"), prop_name,
6101 								        tmp_err->message);
6102 								g_clear_error (&tmp_err);
6103 							}
6104 						} else {
6105 							/* If the string is not a property, try it as a setting */
6106 							NMSetting *s_tmp;
6107 							s_tmp = is_setting_valid (connection, valid_settings_arr, cmd_arg_p);
6108 							if (s_tmp) {
6109 								/* Remove setting from the connection */
6110 								connection_remove_setting (connection, s_tmp);
(2) Event copy_paste_error: "ss" in "ss == menu_ctx.curr_setting" looks like a copy-paste error. Should it say "s_tmp" instead?
Also see events: [original]
6111 								if (ss == menu_ctx.curr_setting) {
6112 									/* If we removed the setting we are in, go up */
6113 									menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
6114 									nmc_completion_setting = NULL;  /* for TAB completion */
6115 								}
6116 							} else
6117 								printf (_("Error: %s properties, nor it is a setting name.\n"),
6118 								        tmp_err->message);
6119 							g_clear_error (&tmp_err);
6120 						}
6121 						g_free (prop_name);
6122 					}
6123 				}
6124 				break;
6125 	
6126 			case NMC_EDITOR_MAIN_CMD_DESCRIBE:
6127 				/* Print property description */
6128 				if (!cmd_arg) {
6129 					if (menu_ctx.level == 1) {
6130 						const char *prop_name;
6131 	
6132 						prop_name = ask_check_property (cmd_arg,
6133 						                                (const char **) menu_ctx.valid_props,
6134 						                                menu_ctx.valid_props_str);
6135 						if (!prop_name)
6136 							break;
6137 	
6138 						/* Show property description */
6139 						print_property_description (menu_ctx.curr_setting, prop_name);
6140 					} else {
6141 						printf (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
6142 						printf (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n"));
6143 					}
6144 				} else {
6145 					NMSetting *ss = NULL;
6146 					gboolean unref_ss = FALSE;
6147 					gboolean descr_all;
6148 					char *user_s;
6149 	
6150 					/* cmd_arg_s != NULL means argument is "setting.property" */
6151 					descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
6152 					user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
6153 					if (user_s) {
6154 						ss = is_setting_valid (connection, valid_settings_arr, user_s);
6155 						if (!ss) {
6156 							ss = create_setting_by_name (user_s, valid_settings_arr);
6157 							if (!ss) {
6158 								printf (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
6159 								        user_s, valid_settings_str);
6160 								break;
6161 							}
6162 							unref_ss = TRUE;
6163 						}
6164 					} else
6165 						ss = menu_ctx.curr_setting;
6166 	
6167 					if (descr_all) {
6168 						/* Show description for all properties */
6169 						print_setting_description (ss);
6170 					} else {
6171 						GError *tmp_err = NULL;
6172 						char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
6173 						if (prop_name) {
6174 							/* Show property description */
6175 							print_property_description (ss, prop_name);
6176 						} else {
6177 							/* If the string is not a property, try it as a setting */
6178 							NMSetting *s_tmp;
6179 							s_tmp = is_setting_valid (connection, valid_settings_arr, cmd_arg_p);
6180 							if (s_tmp)
6181 								print_setting_description (s_tmp);
6182 							else
6183 								printf (_("Error: invalid property: %s, "
6184 								          "neither a valid setting name.\n"),
6185 								        tmp_err->message);
6186 							g_clear_error (&tmp_err);
6187 						}
6188 						g_free (prop_name);
6189 					}
6190 					if (unref_ss)
6191 						g_object_unref (ss);
6192 				}
6193 				break;
6194 	
6195 			case NMC_EDITOR_MAIN_CMD_PRINT:
6196 				/* Print current connection settings/properties */
6197 				if (cmd_arg) {
6198 					if (strcmp (cmd_arg, "all") == 0)
6199 						editor_show_connection (connection, nmc);
6200 					else {
6201 						const char *s = check_valid_name (cmd_arg, valid_settings_arr, NULL);
6202 						if (s) {
6203 							NMSetting *ss = nm_connection_get_setting_by_name (connection, s);
6204 							if (ss)
6205 								editor_show_setting (ss, nmc);
6206 							else
6207 								printf (_("Error: '%s' setting not present\n"), s);
6208 						}
6209 						else
6210 							printf (_("Error: unknown setting: '%s'\n"), cmd_arg);
6211 					}
6212 				} else {
6213 					if (menu_ctx.curr_setting)
6214 						editor_show_setting (menu_ctx.curr_setting, nmc);
6215 					else
6216 						editor_show_connection (connection, nmc);
6217 				}
6218 				break;
6219 	
6220 			case NMC_EDITOR_MAIN_CMD_VERIFY:
6221 				/* Verify current setting or the whole connection */
6222 				if (   menu_ctx.curr_setting
6223 				    && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) {
6224 					GError *tmp_err = NULL;
6225 					nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err);
6226 					printf (_("Verify setting '%s': %s\n"),
6227 					        nm_setting_get_name (menu_ctx.curr_setting),
6228 					        tmp_err ? tmp_err->message : "OK");
6229 					g_clear_error (&tmp_err);
6230 				} else {
6231 					GError *tmp_err = NULL;
6232 					nm_connection_verify (connection, &tmp_err);
6233 					printf (_("Verify connection: %s\n"),
6234 					        tmp_err ? tmp_err->message : "OK");
6235 					g_clear_error (&tmp_err);
6236 				}
6237 				break;
6238 	
6239 			case NMC_EDITOR_MAIN_CMD_SAVE:
6240 				/* Save the connection */
6241 				if (nm_connection_verify (connection, &err1)) {
6242 					/* Ask for save confirmation if the connection changes to autoconnect=yes */
6243 					if (nmc->editor_save_confirmation)
6244 						if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con)))
6245 							break;
6246 	
6247 					if (!rem_con) {
6248 						/* Tell the settings service to add the new connection */
6249 						info = g_malloc0 (sizeof (AddConnectionInfo));
6250 						info->nmc = nmc;
6251 						info->con_name = g_strdup (nm_connection_get_id (connection));
6252 						nm_remote_settings_add_connection (nmc->system_settings,
6253 						                                   connection,
6254 						                                   add_connection_editor_cb,
6255 						                                   info);
6256 					} else {
6257 						/* Save/update already saved (existing) connection */
6258 						nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con),
6259 						                                                connection,
6260 						                                                NULL);
6261 						nm_remote_connection_commit_changes (rem_con,
6262 						                                     update_connection_editor_cb,
6263 						                                     NULL);
6264 					}
6265 	
6266 					g_mutex_lock (&nmc_editor_mutex);
6267 					//FIXME: add also a timeout for cases the callback is not called
6268 					while (!nmc_editor_cb_called)
6269 						g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
6270 	
6271 					if (nmc_editor_error) {
6272 						printf (_("Error: Failed to save '%s' (%s) connection: (%d) %s\n"),
6273 						        nm_connection_get_id (connection),
6274 						        nm_connection_get_uuid (connection),
6275 						        nmc_editor_error->code, nmc_editor_error->message);
6276 	
6277 						g_error_free (nmc_editor_error);
6278 					} else {
6279 						NMRemoteConnection *con_tmp;
6280 	
6281 						printf (_("Connection '%s' (%s) successfully saved.\n"),
6282 						        nm_connection_get_id (connection),
6283 						        nm_connection_get_uuid (connection));
6284 	
6285 						/* Replace local connection with the remote one to be sure they are equal.
6286 						 * This mitigates problems with plugins not preserving some properties or
6287 						 * adding ipv{4,6} settings when not present.
6288 						 */
6289 						con_tmp = nm_remote_settings_get_connection_by_uuid (nmc->system_settings,
6290 						                                                     nm_connection_get_uuid (connection));
6291 						if (con_tmp)
6292 							nm_connection_replace_settings_from_connection (connection,
6293 							                                                NM_CONNECTION (con_tmp),
6294 							                                                NULL);
6295 					}
6296 	
6297 					nmc_editor_cb_called = FALSE;
6298 					nmc_editor_error = NULL;
6299 					g_mutex_unlock (&nmc_editor_mutex);
6300 				} else
6301 					printf (_("Error: connection verification failed: %s\n"),
6302 					        err1 ? err1->message : _("(unknown error)"));
6303 	
6304 				g_clear_error (&err1);
6305 				break;
6306 	
6307 			case NMC_EDITOR_MAIN_CMD_BACK:
6308 				/* Go back (up) an the menu */
6309 				if (menu_ctx.level == 1) {
6310 					menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
6311 					nmc_completion_setting = NULL;  /* for TAB completion */
6312 				}
6313 				break;
6314 	
6315 			case NMC_EDITOR_MAIN_CMD_HELP:
6316 				/* Print command help */
6317 				editor_main_help (cmd_arg);
6318 				break;
6319 	
6320 			case NMC_EDITOR_MAIN_CMD_NMCLI:
6321 				if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) {
6322 					GError *tmp_err = NULL;
6323 					gboolean bb;
6324 					if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
6325 						printf (_("Error: status-line: %s\n"), tmp_err->message);
6326 						g_clear_error (&tmp_err);
6327 					} else
6328 						nmc->editor_status_line = bb;
6329 				} else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) {
6330 					GError *tmp_err = NULL;
6331 					gboolean bb;
6332 					if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
6333 						printf (_("Error: save-confirmation: %s\n"), tmp_err->message);
6334 						g_clear_error (&tmp_err);
6335 					} else
6336 						nmc->editor_save_confirmation = bb;
6337 				} else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) {
6338 					unsigned long color;
6339 					if (!nmc_string_to_uint (cmd_arg_v ? g_strstrip (cmd_arg_v) : "X",
6340 					                         TRUE, 0, 8, &color))
6341 						printf (_("Error: bad color number: '%s'; use <0-8>\n"),
6342 						        cmd_arg_v ? cmd_arg_v : "");
6343 					else {
6344 						nmc->editor_prompt_color = color;
6345 						g_free (menu_ctx.main_prompt);
6346 						if (menu_ctx.level == 0)
6347 							menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, BASE_PROMPT);
6348 						else
6349 							menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, "nmcli %s> ",
6350 							                                     nm_setting_get_name (menu_ctx.curr_setting));
6351 					}
6352 				} else if (!cmd_arg_p) {
6353 					printf (_("Current nmcli configuration:\n"));
6354 					printf ("status-line: %s\n"
6355 					        "save-confirmation: %s\n"
6356 					        "prompt-color: %d\n",
6357 					        nmc->editor_status_line ? "yes" : "no",
6358 					        nmc->editor_save_confirmation ? "yes" : "no",
6359 					        nmc->editor_prompt_color);
6360 				} else
6361 					printf (_("Invalid configuration option '%s'; allowed [%s]\n"),
6362 					        cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, prompt-color");
6363 	
6364 				break;
6365 	
6366 			case NMC_EDITOR_MAIN_CMD_QUIT:
6367 				if (dirty) {
6368 					char *tmp_str;
6369 					do {
6370 						tmp_str = nmc_get_user_input (_("The connection is not saved. "
6371 						                                "Do you really want to quit? [y/n]\n"));
6372 					} while (!tmp_str);
6373 					if (matches (tmp_str, "yes") == 0)
6374 						cmd_loop = FALSE;  /* quit command loop */
6375 					g_free (tmp_str);
6376 				} else
6377 					cmd_loop = FALSE;  /* quit command loop */
6378 				break;
6379 	
6380 			case NMC_EDITOR_MAIN_CMD_UNKNOWN:
6381 			default:
6382 				printf (_("Unknown command: '%s'\n"), cmd_user);
6383 				break;
6384 			}
6385 	
6386 			g_free (cmd_user);
6387 			g_free (cmd_arg);
6388 			g_free (cmd_arg_s);
6389 			g_free (cmd_arg_p);
6390 			g_free (cmd_arg_v);
6391 		}
6392 		g_free (valid_settings_str);
6393 		g_free (menu_ctx.main_prompt);
6394 		g_strfreev (menu_ctx.valid_props);
6395 		g_free (menu_ctx.valid_props_str);
6396 	
6397 		/* Save history file */
6398 		save_history_cmds (nm_connection_get_uuid (connection));
6399 	
6400 		return TRUE;
6401 	}
6402 	
6403 	static const char *
6404 	get_ethernet_device_name (NmCli *nmc)
6405 	{
6406 		const GPtrArray *devices;
6407 		int i;
6408 	
6409 		nmc->get_client (nmc);
6410 		devices = nm_client_get_devices (nmc->client);
6411 		for (i = 0; devices && (i < devices->len); i++) {
6412 			NMDevice *dev = g_ptr_array_index (devices, i);
6413 			if (NM_IS_DEVICE_ETHERNET (dev))
6414 				return nm_device_get_iface (dev);
6415 		}
6416 		return NULL;
6417 	}
6418 	
6419 	static void
6420 	editor_init_new_connection (NmCli *nmc, NMConnection *connection)
6421 	{
6422 		NMSetting *setting, *base_setting;
6423 		NMSettingConnection *s_con;
6424 		const char *con_type;
6425 		const char *slave_type = NULL;
6426 	
6427 		s_con = nm_connection_get_setting_connection (connection);
6428 		g_assert (s_con);
6429 		con_type = nm_setting_connection_get_connection_type (s_con);
6430 	
6431 		/* Initialize new connection according to its type using sensible defaults. */
6432 	
6433 		if (g_strcmp0 (con_type, "bond-slave") == 0)
6434 			slave_type = NM_SETTING_BOND_SETTING_NAME;
6435 		if (g_strcmp0 (con_type, "team-slave") == 0)
6436 			slave_type = NM_SETTING_TEAM_SETTING_NAME;
6437 		if (g_strcmp0 (con_type, "bridge-slave") == 0)
6438 			slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
6439 	
6440 		if (slave_type) {
6441 			const char *dev_ifname = get_ethernet_device_name (nmc);
6442 	
6443 			/* For bond/team/bridge slaves add 'wired' setting */
6444 			setting = nm_setting_wired_new ();
6445 			nm_connection_add_setting (connection, setting);
6446 	
6447 			g_object_set (s_con,
6448 			              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
6449 			              NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
6450 			              NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
6451 			              NULL);
6452 		} else {
6453 			/* Add a "base" setting to the connection by default */
6454 			base_setting = nmc_setting_new_for_name (con_type);
6455 			if (!base_setting)
6456 				return;
6457 			nm_connection_add_setting (connection, base_setting);
6458 	
6459 			/* Set a sensible bond/bridge interface name by default */
6460 			if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0)
6461 				g_object_set (NM_SETTING_BOND (base_setting),
6462 				              NM_SETTING_BOND_INTERFACE_NAME, "nm-bond",
6463 				              NULL);
6464 			if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0)
6465 				g_object_set (NM_SETTING_BRIDGE (base_setting),
6466 				              NM_SETTING_BRIDGE_INTERFACE_NAME, "nm-bridge",
6467 				              NULL);
6468 	
6469 			/* Set sensible initial VLAN values */
6470 			if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) {
6471 				const char *dev_ifname = get_ethernet_device_name (nmc);
6472 	
6473 				g_object_set (NM_SETTING_VLAN (base_setting),
6474 				              NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0",
6475 				              NM_SETTING_VLAN_ID, 1,
6476 				              NULL);
6477 				g_object_set (s_con,
6478 				              NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
6479 				              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME,
6480 				              NULL);
6481 			}
6482 	
6483 			/* Initialize 'transport-mode' so that 'infiniband' is valid */
6484 			if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0)
6485 				g_object_set (NM_SETTING_INFINIBAND (base_setting),
6486 				              NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
6487 				              NULL);
6488 	
6489 			/* Initialize 'number' so that 'cdma' is valid */
6490 			if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0)
6491 				g_object_set (NM_SETTING_CDMA (base_setting),
6492 				              NM_SETTING_CDMA_NUMBER, "#777",
6493 				              NULL);
6494 	
6495 			/* Initialize 'number' so that 'gsm' is valid */
6496 			if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0)
6497 				g_object_set (NM_SETTING_GSM (base_setting),
6498 				              NM_SETTING_GSM_NUMBER, "*99#",
6499 				              NULL);
6500 	
6501 			/* For Wi-Fi set mode to "infrastructure". Even though mode == NULL
6502 			 * is regarded as "infrastructure", explicit value makes no doubts.
6503 			 */
6504 			if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0)
6505 				g_object_set (NM_SETTING_WIRELESS (base_setting),
6506 				              NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
6507 				              NULL);
6508 	
6509 			/* Always add IPv4 and IPv6 settings for non-slave connections */
6510 			setting = nm_setting_ip4_config_new ();
6511 			nmc_setting_custom_init (setting);
6512 			nm_connection_add_setting (connection, setting);
6513 	
6514 			setting = nm_setting_ip6_config_new ();
6515 			nmc_setting_custom_init (setting);
6516 			nm_connection_add_setting (connection, setting);
6517 		}
6518 	}
6519 	
6520 	static void
6521 	editor_init_existing_connection (NMConnection *connection)
6522 	{
6523 		NMSettingIP4Config *s_ip4;
6524 		NMSettingIP6Config *s_ip6;
6525 	
6526 		s_ip4 = nm_connection_get_setting_ip4_config (connection);
6527 		s_ip6 = nm_connection_get_setting_ip6_config (connection);
6528 	
6529 		if (s_ip4)
6530 			nmc_setting_ip4_connect_handlers (s_ip4);
6531 		if (s_ip6)
6532 			nmc_setting_ip6_connect_handlers (s_ip6);
6533 	}
6534 	
6535 	static NMCResultCode
6536 	do_connection_edit (NmCli *nmc, int argc, char **argv)
6537 	{
6538 		NMConnection *connection = NULL;
6539 		NMSettingConnection *s_con;
6540 		const char *connection_type;
6541 		char *uuid;
6542 		char *default_name = NULL;
6543 		const char *type = NULL;
6544 		char *type_ask = NULL;
6545 		const char *con_name = NULL;
6546 		const char *con = NULL;
6547 		const char *con_id = NULL;
6548 		const char *con_uuid = NULL;
6549 		const char *con_path = NULL;
6550 		const char *selector = NULL;
6551 		char *tmp_str;
6552 		GError *error = NULL;
6553 		GError *err1 = NULL;
6554 		GModule *edit_lib_module = NULL;
6555 		nmc_arg_t exp_args[] = { {"type",     TRUE, &type,     FALSE},
6556 		                         {"con-name", TRUE, &con_name, FALSE},
6557 		                         {"id",       TRUE, &con_id,   FALSE},
6558 		                         {"uuid",     TRUE, &con_uuid, FALSE},
6559 		                         {"path",     TRUE, &con_path, FALSE},
6560 		                         {NULL} };
6561 	
6562 		nmc->return_value = NMC_RESULT_SUCCESS;
6563 	
6564 		if (argc == 1)
6565 			con = *argv;
6566 		else {
6567 			if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) {
6568 				g_string_assign (nmc->return_text, error->message);
6569 				nmc->return_value = error->code;
6570 				g_clear_error (&error);
6571 				goto error;
6572 			}
6573 		}
6574 	
6575 		/* Load line editing library */
6576 		if (!(edit_lib_module = load_cmd_line_edit_lib ())) {
6577 			printf (_(">>> Command-line editing is not available. "
6578 			          "Consider installing a line editing library to enable the feature. <<<\n"
6579 			          "Supported libraries are:\n"
6580 			          "  - GNU Readline    (libreadline) http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html\n"
6581 			          "  - NetBSD Editline (libedit)     http://www.thrysoee.dk/editline/\n"));
6582 			edit_lib_symbols.readline_func = NULL;
6583 			edit_lib_symbols.add_history_func = NULL;
6584 			edit_lib_symbols.history_list_func = NULL;
6585 			edit_lib_symbols.rl_insert_text_func = NULL;
6586 			edit_lib_symbols.rl_startup_hook_x = NULL;
6587 		}
6588 	
6589 		if (!con) {
6590 			if (con_id && !con_uuid && !con_path) {
6591 				con = con_id;
6592 				selector = "id";
6593 			} else if (con_uuid && !con_id && !con_path) {
6594 				con = con_uuid;
6595 				selector = "uuid";
6596 			} else if (con_path && !con_id && !con_uuid) {
6597 				con = con_path;
6598 				selector = "path";
6599 			} else if (!con_path && !con_id && !con_uuid) {
6600 				/* no-op */
6601 			} else {
6602 				g_string_printf (nmc->return_text,
6603 				                 _("Error: only one of 'id', uuid, or 'path' can be provided."));
6604 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6605 				goto error;
6606 			}
6607 		}
6608 	
6609 		if (con) {
6610 			/* Existing connection */
6611 			NMConnection *found_con;
6612 	
6613 			found_con = find_connection (nmc->system_connections, selector, con);
6614 			if (!found_con) {
6615 				g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
6616 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6617 				goto error;
6618 			}
6619 	
6620 			/* Duplicate the connection and use that so that we need not
6621 			 * differentiate existing vs. new later
6622 			 */
6623 			connection = nm_connection_duplicate (found_con);
6624 	
6625 			s_con = nm_connection_get_setting_connection (connection);
6626 			g_assert (s_con);
6627 			connection_type = nm_setting_connection_get_connection_type (s_con);
6628 	
6629 			if (type)
6630 				printf (_("Warning: editing existing connection '%s'; 'type' argument is ignored\n"),
6631 				        nm_connection_get_id (connection));
6632 			if (con_name)
6633 				printf (_("Warning: editing existing connection '%s'; 'con-name' argument is ignored\n"),
6634 				        nm_connection_get_id (connection));
6635 	
6636 			/* Load previously saved history commands for the connection */
6637 			load_history_cmds (nm_connection_get_uuid (connection));
6638 	
6639 			editor_init_existing_connection (connection);
6640 		} else {
6641 			/* New connection */
6642 			connection_type = check_valid_name (type, nmc_valid_connection_types, &err1);
6643 			tmp_str = get_valid_options_string (nmc_valid_connection_types);
6644 	
6645 			while (!connection_type) {
6646 				if (!type)
6647 					printf (_("Valid connection types: %s\n"), tmp_str);
6648 				else
6649 					printf (_("Error: invalid connection type; %s\n"), err1->message);
6650 				g_clear_error (&err1);
6651 	
6652 				type_ask = readline_x (EDITOR_PROMPT_CON_TYPE);
6653 				type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
6654 				connection_type = check_valid_name (type_ask, nmc_valid_connection_types, &err1);
6655 				g_free (type_ask);
6656 			}
6657 			g_free (tmp_str);
6658 	
6659 			/* Create a new connection object */
6660 			connection = nm_connection_new ();
6661 	
6662 			/* Build up the 'connection' setting */
6663 			s_con = (NMSettingConnection *) nm_setting_connection_new ();
6664 			uuid = nm_utils_uuid_generate ();
6665 			if (con_name)
6666 				default_name = g_strdup (con_name);
6667 			else
6668 				default_name = unique_connection_name (nmc->system_connections,
6669 				                                       get_name_alias (connection_type, nmc_valid_connection_types));
6670 	
6671 			g_object_set (s_con,
6672 			              NM_SETTING_CONNECTION_ID, default_name,
6673 			              NM_SETTING_CONNECTION_UUID, uuid,
6674 			              NM_SETTING_CONNECTION_TYPE, connection_type,
6675 			              NULL);
6676 			g_free (uuid);
6677 			g_free (default_name);
6678 			nm_connection_add_setting (connection, NM_SETTING (s_con));
6679 	
6680 			/* Initialize the new connection so that it is valid from the start */
6681 			editor_init_new_connection (nmc, connection);
6682 		}
6683 	
6684 		printf ("\n");
6685 		printf (_("===| nmcli interactive connection editor |==="));
6686 		printf ("\n\n");
6687 		if (con)
6688 			printf (_("Editing existing '%s' connection: '%s'"), connection_type, con);
6689 		else
6690 			printf (_("Adding a new '%s' connection"), connection_type);
6691 		printf ("\n\n");
6692 		printf (_("Type 'help' or '?' for available commands."));
6693 		printf ("\n");
6694 		printf (_("Type 'describe [<setting>.<prop>]' for detailed property description."));
6695 		printf ("\n\n");
6696 	
6697 		/* Set global variable for use in TAB completion */
6698 		nmc_completion_con_type = connection_type;
6699 	
6700 		/* Run menu loop */
6701 		editor_menu_main (nmc, connection, connection_type);
6702 	
6703 		if (edit_lib_module)
6704 			g_module_close (edit_lib_module);
6705 	
6706 		if (connection)
6707 			g_object_unref (connection);
6708 	
6709 		nmc->should_wait = TRUE;
6710 		return nmc->return_value;
6711 	
6712 	error:
6713 		if (connection)
6714 			g_object_unref (connection);
6715 		g_free (type_ask);
6716 	
6717 		nmc->should_wait = FALSE;
6718 		return nmc->return_value;
6719 	}
6720 	
6721 	
6722 	static void
6723 	modify_connection_cb (NMRemoteConnection *connection,
6724 	                      GError *error,
6725 	                      gpointer user_data)
6726 	{
6727 		NmCli *nmc = (NmCli *) user_data;
6728 	
6729 	        if (error) {
6730 			g_string_printf (nmc->return_text,
6731 			                 _("Error: Failed to modify connection '%s': (%d) %s"),
6732 			                 nm_connection_get_id (NM_CONNECTION (connection)),
6733 			                 error->code, error->message);
6734 			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
6735 		} else {
6736 			if (nmc->print_output == NMC_PRINT_PRETTY)
6737 				printf (_("Connection '%s' (%s) successfully modified.\n"),
6738 			                nm_connection_get_id (NM_CONNECTION (connection)),
6739 			                nm_connection_get_uuid (NM_CONNECTION (connection)));
6740 		}
6741 		quit ();
6742 	}
6743 	
6744 	static NMCResultCode
6745 	do_connection_modify (NmCli *nmc, int argc, char **argv)
6746 	{
6747 		NMConnection *connection = NULL;
6748 		NMRemoteConnection *rc = NULL;
6749 		NMSetting *setting;
6750 		NMSettingConnection *s_con;
6751 		const char *con_type;
6752 		const char *name;
6753 		const char *selector = NULL;
6754 		const char *set_prop;
6755 		char *value = NULL;
6756 		char **strv = NULL;
6757 		const char *setting_name;
6758 		char *property_name = NULL;
6759 		GError *error = NULL;
6760 	
6761 		nmc->should_wait = FALSE;
6762 	
6763 		if (argc == 0) {
6764 			g_string_printf (nmc->return_text, _("Error: No arguments provided."));
6765 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6766 			goto finish;
6767 		}
6768 		if (   strcmp (*argv, "id") == 0
6769 		    || strcmp (*argv, "uuid") == 0
6770 		    || strcmp (*argv, "path") == 0) {
6771 	
6772 			selector = *argv;
6773 			if (next_arg (&argc, &argv) != 0) {
6774 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
6775 				                 selector);
6776 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6777 				goto finish;
6778 			}
6779 			name = *argv;
6780 		}
6781 		name = *argv;
6782 		next_arg (&argc, &argv);
6783 		set_prop = *argv;
6784 		next_arg (&argc, &argv);
6785 		value = g_strjoinv (" ", argv);
6786 	
6787 		if (!name) {
6788 			g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
6789 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6790 			goto finish;
6791 		}
6792 		if (!set_prop) {
6793 			g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing."));
6794 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6795 			goto finish;
6796 		}
6797 		/* NULL value means deleting/setting default property value */
6798 	
6799 		/* create NMClient */
6800 		nmc->get_client (nmc);
6801 	
6802 		if (!nm_client_get_manager_running (nmc->client)) {
6803 			g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
6804 			nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
6805 			goto finish;
6806 		}
6807 	
6808 		connection = find_connection (nmc->system_connections, selector, name);
6809 		if (!connection) {
6810 			g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
6811 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6812 			goto finish;
6813 		}
6814 		strv = g_strsplit (set_prop, ".", 2);
6815 		if (g_strv_length (strv) != 2) {
6816 			g_string_printf (nmc->return_text, _("Error: invalid <setting>.<property> '%s'."),
6817 			                 set_prop);
6818 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6819 			goto finish;
6820 		}
6821 	
6822 		rc = nm_remote_settings_get_connection_by_uuid (nmc->system_settings,
6823 		                                                nm_connection_get_uuid (connection));
6824 	
6825 		s_con = nm_connection_get_setting_connection (NM_CONNECTION (rc));
6826 		g_assert (s_con);
6827 		con_type = nm_setting_connection_get_connection_type (s_con);
6828 	
6829 		setting_name = check_valid_name (strv[0], get_valid_settings_array (con_type), &error);
6830 		if (!setting_name) {
6831 			g_string_printf (nmc->return_text, _("Error: invalid or not allowed setting '%s': %s."),
6832 			                 strv[0], error->message);
6833 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6834 			goto finish;
6835 		}
6836 		setting = nm_connection_get_setting_by_name (NM_CONNECTION (rc), setting_name);
6837 		if (!setting) {
6838 			setting = nmc_setting_new_for_name (setting_name);
6839 			if (!setting) {
6840 				/* This should really not happen */
6841 				g_string_printf (nmc->return_text,
6842 				                 "Error: don't know how to create '%s' setting.",
6843 				                 setting_name);
6844 				nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
6845 				goto finish;
6846 			}
6847 			nm_connection_add_setting (NM_CONNECTION (rc), setting);
6848 		}
6849 	
6850 		property_name = is_property_valid (setting, strv[1], &error);
6851 		if (!property_name) {
6852 			g_string_printf (nmc->return_text, _("Error: invalid property '%s': %s."),
6853 			                 strv[1], error->message);
6854 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6855 			goto finish;
6856 		}
6857 		if (!nmc_setting_set_property (setting, property_name, value, &error)) {
6858 			g_string_printf (nmc->return_text, _("Error: failed to modify %s.%s: %s."),
6859 			                 strv[0], strv[1], error->message);
6860 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6861 			goto finish;
6862 		}
6863 	
6864 		nm_remote_connection_commit_changes (rc,
6865 		                                     modify_connection_cb,
6866 		                                     nmc);
6867 	finish:
6868 		nmc->should_wait = (nmc->return_value == NMC_RESULT_SUCCESS);
6869 		g_free (value);
6870 		g_free (property_name);
6871 		g_strfreev (strv);
6872 		g_clear_error (&error);
6873 		return nmc->return_value;
6874 	}
6875 	
6876 	
6877 	typedef struct {
6878 		NmCli *nmc;
6879 		int counter;
6880 	} DeleteStateInfo;
6881 	
6882 	static void
6883 	delete_cb (NMRemoteConnection *con, GError *err, gpointer user_data)
6884 	{
6885 		DeleteStateInfo *info = (DeleteStateInfo *) user_data;
6886 	
6887 		if (err) {
6888 			g_string_printf (info->nmc->return_text, _("Error: Connection deletion failed: %s"), err->message);
6889 			info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL;
6890 		}
6891 	
6892 		info->counter--;
6893 		if (info->counter == 0) {
6894 			g_free (info);
6895 			quit ();
6896 		}
6897 	}
6898 	
6899 	static NMCResultCode
6900 	do_connection_delete (NmCli *nmc, int argc, char **argv)
6901 	{
6902 		NMConnection *connection = NULL;
6903 		DeleteStateInfo *del_info = NULL;
6904 		char *line = NULL;
6905 		char **arg_arr = NULL;
6906 		char **arg_ptr = argv;
6907 		int arg_num = argc;
6908 		GString *invalid_cons = NULL;
6909 		gboolean del_info_free = FALSE;
6910 	
6911 		nmc->return_value = NMC_RESULT_SUCCESS;
6912 		nmc->should_wait = FALSE;
6913 	
6914 		/* create NMClient */
6915 		nmc->get_client (nmc);
6916 	
6917 		if (!nm_client_get_manager_running (nmc->client)) {
6918 			g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
6919 			nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
6920 			goto finish;
6921 		}
6922 	
6923 		if (argc == 0) {
6924 			if (nmc->ask) {
6925 				line = nmc_get_user_input (_("Connection (name, UUID, or path): "));
6926 				nmc_string_to_arg_array (line, "", &arg_arr, &arg_num);
6927 				arg_ptr = arg_arr;
6928 			}
6929 			if (arg_num == 0) {
6930 				g_string_printf (nmc->return_text, _("Error: No connection specified."));
6931 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6932 				goto finish;
6933 			}
6934 		}
6935 	
6936 		del_info = g_malloc0 (sizeof (DeleteStateInfo));
6937 		del_info->nmc = nmc;
6938 		del_info->counter = 0;
6939 		del_info_free = TRUE;
6940 	
6941 		while (arg_num > 0) {
6942 			const char *selector = NULL;
6943 	
6944 			if (   strcmp (*arg_ptr, "id") == 0
6945 			    || strcmp (*arg_ptr, "uuid") == 0
6946 			    || strcmp (*arg_ptr, "path") == 0) {
6947 				selector = *arg_ptr;
6948 				if (next_arg (&arg_num, &arg_ptr) != 0) {
6949 					g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
6950 					nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6951 					goto finish;
6952 				}
6953 			}
6954 	
6955 			connection = find_connection (nmc->system_connections, selector, *arg_ptr);
6956 			if (!connection) {
6957 				if (nmc->print_output != NMC_PRINT_TERSE)
6958 					printf (_("Error: unknown connection: %s\n"), *arg_ptr);
6959 	
6960 				if (!invalid_cons)
6961 					invalid_cons = g_string_new (NULL);
6962 				g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr);
6963 	
6964 				/* take the next argument and continue */
6965 				next_arg (&arg_num, &arg_ptr);
6966 				continue;
6967 			}
6968 	
6969 			/* We need to wait a bit so that nmcli's permissions can be checked.
6970 			 * We will exit when D-Bus return (error) messages are received.
6971 			 */
6972 			nmc->should_wait = TRUE;
6973 	
6974 			/* del_info deallocation is handled in delete_cb() */
6975 			del_info_free = FALSE;
6976 	
6977 			del_info->counter++;
6978 	
6979 			/* Delete the connection */
6980 			nm_remote_connection_delete (NM_REMOTE_CONNECTION (connection), delete_cb, del_info);
6981 	
6982 			next_arg (&arg_num, &arg_ptr);
6983 		}
6984 	
6985 	finish:
6986 		if (del_info_free)
6987 			g_free (del_info);
6988 		g_strfreev (arg_arr);
6989 	
6990 		if (invalid_cons) {
6991 			g_string_truncate (invalid_cons, invalid_cons->len-2);  /* truncate trailing ", " */
6992 			g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."),
6993 			                 invalid_cons->str);
6994 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6995 			g_string_free (invalid_cons, TRUE);
6996 		}
6997 		return nmc->return_value;
6998 	}
6999 	
7000 	static NMCResultCode
7001 	do_connection_reload (NmCli *nmc, int argc, char **argv)
7002 	{
7003 		GError *error = NULL;
7004 	
7005 		nmc->return_value = NMC_RESULT_SUCCESS;
7006 		nmc->should_wait = FALSE;
7007 	
7008 		if (!nm_client_get_manager_running (nmc->client)) {
7009 			g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
7010 			nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
7011 			return nmc->return_value;
7012 		}
7013 	
7014 		if (!nm_remote_settings_reload_connections (nmc->system_settings, &error)) {
7015 			g_string_printf (nmc->return_text, _("Error: %s."), error->message);
7016 			if (error->code == NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE)
7017 				nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
7018 			else
7019 				nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
7020 			g_clear_error (&error);
7021 		}
7022 	
7023 		return nmc->return_value;
7024 	}
7025 	
7026 	
7027 	typedef struct {
7028 		NmCli *nmc;
7029 		int argc;
7030 		char **argv;
7031 	} NmcEditorThreadData;
7032 	
7033 	static GThread *editor_thread;
7034 	static NmcEditorThreadData editor_thread_data;
7035 	
7036 	/*
7037 	 * We need to run do_connection_edit() in a thread so that
7038 	 * glib main loop is not blocked and could receive and process D-Bus
7039 	 * return messages.
7040 	 */
7041 	static gpointer
7042 	connection_editor_thread_func (gpointer data)
7043 	{
7044 		NmcEditorThreadData *td = (NmcEditorThreadData *) data;
7045 	
7046 		/* run editor for editing/adding connections */
7047 		td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv);
7048 	
7049 		/* quit glib main loop now that we are with this thread */
7050 		quit ();
7051 	
7052 		return NULL;
7053 	}
7054 	
7055 	static NMCResultCode
7056 	parse_cmd (NmCli *nmc, int argc, char **argv)
7057 	{
7058 		GError *error = NULL;
7059 		int arg_ret;
7060 	
7061 		if (argc == 0) {
7062 			if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
7063 				goto opt_error;
7064 			nmc->return_value = do_connections_show (nmc, argc, argv);
7065 		} else {
7066 			if (matches (*argv, "show") == 0) {
7067 				arg_ret = next_arg (&argc, &argv);
7068 				if (arg_ret != 0 || matches (*argv, "configured") == 0) {
7069 					next_arg (&argc, &argv);
7070 					nmc->return_value = do_connections_show (nmc, argc, argv);
7071 				} else if (matches (*argv, "active") == 0) {
7072 					if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
7073 						goto opt_error;
7074 					nmc->return_value = do_connections_show_active (nmc, argc-1, argv+1);
7075 				} else {
7076 					g_string_printf (nmc->return_text, _("Error: 'configured' or 'active' command is expected for 'connection show'."));
7077 					nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7078 					nmc->should_wait = FALSE;
7079 				}
7080 			}
7081 			else if (matches(*argv, "up") == 0) {
7082 				nmc->return_value = do_connection_up (nmc, argc-1, argv+1);
7083 			}
7084 			else if (matches(*argv, "down") == 0) {
7085 				nmc->return_value = do_connection_down (nmc, argc-1, argv+1);
7086 			}
7087 			else if (matches(*argv, "add") == 0) {
7088 				if (nmc_arg_is_help (*(argv+1))) {
7089 					usage_connection_add ();
7090 					nmc->should_wait = FALSE;
7091 				} else
7092 					nmc->return_value = do_connection_add (nmc, argc-1, argv+1);
7093 			}
7094 			else if (matches(*argv, "edit") == 0) {
7095 				editor_thread_data.nmc = nmc;
7096 				editor_thread_data.argc = argc - 1;
7097 				editor_thread_data.argv = argv + 1;
7098 				editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data);
7099 				g_thread_unref (editor_thread);
7100 			}
7101 			else if (matches(*argv, "delete") == 0) {
7102 				nmc->return_value = do_connection_delete (nmc, argc-1, argv+1);
7103 			}
7104 			else if (matches(*argv, "reload") == 0) {
7105 				nmc->return_value = do_connection_reload (nmc, argc-1, argv+1);
7106 			}
7107 			else if (matches (*argv, "modify") == 0) {
7108 				nmc->return_value = do_connection_modify (nmc, argc-1, argv+1);
7109 			}
7110 			else if (nmc_arg_is_help (*argv)) {
7111 				usage ();
7112 				nmc->should_wait = FALSE;
7113 			}
7114 			else {
7115 				usage ();
7116 				g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
7117 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7118 				nmc->should_wait = FALSE;
7119 			}
7120 		}
7121 	
7122 		return nmc->return_value;
7123 	
7124 	opt_error:
7125 		g_string_printf (nmc->return_text, _("Error: %s."), error->message);
7126 		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7127 		nmc->should_wait = FALSE;
7128 		g_error_free (error);
7129 		return nmc->return_value;
7130 	}
7131 	
7132 	/* callback called when connections are obtained from the settings service */
7133 	static void
7134 	get_connections_cb (NMRemoteSettings *settings, gpointer user_data)
7135 	{
7136 		ArgsInfo *args = (ArgsInfo *) user_data;
7137 	
7138 		/* Get the connection list */
7139 		args->nmc->system_connections = nm_remote_settings_list_connections (settings);
7140 	
7141 		parse_cmd (args->nmc, args->argc, args->argv);
7142 	
7143 		if (!args->nmc->should_wait)
7144 			quit ();
7145 	}
7146 	
7147 	/* Entry point function for connections-related commands: 'nmcli connection' */
7148 	NMCResultCode
7149 	do_connections (NmCli *nmc, int argc, char **argv)
7150 	{
7151 		int i = 0;
7152 		gboolean real_cmd = FALSE;
7153 	
7154 		if (argc == 0)
7155 			real_cmd = TRUE;
7156 		else {
7157 			while (real_con_commands[i] && matches (*argv, real_con_commands[i]) != 0)
7158 				i++;
7159 			if (real_con_commands[i] != NULL)
7160 				real_cmd = TRUE;
7161 		}
7162 	
7163 		if (!real_cmd) {
7164 			/* no real execution command - no need to get connections */
7165 			return parse_cmd (nmc, argc, argv);
7166 		} else {
7167 			if (!nmc_versions_match (nmc))
7168 				return nmc->return_value;
7169 	
7170 			/* Get NMClient object early */
7171 			nmc->get_client (nmc);
7172 	
7173 			nmc->should_wait = TRUE;
7174 	
7175 			args_info.nmc = nmc;
7176 			args_info.argc = argc;
7177 			args_info.argv = argv;
7178 	
7179 			/* get system settings */
7180 			if (!(nmc->system_settings = nm_remote_settings_new (NULL))) {
7181 				g_string_printf (nmc->return_text, _("Error: Could not get system settings."));
7182 				nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
7183 				nmc->should_wait = FALSE;
7184 				return nmc->return_value;
7185 			}
7186 	
7187 			/* find out whether settings service is running */
7188 			g_object_get (nmc->system_settings, NM_REMOTE_SETTINGS_SERVICE_RUNNING, &nmc->system_settings_running, NULL);
7189 	
7190 			if (!nmc->system_settings_running) {
7191 				g_string_printf (nmc->return_text, _("Error: Can't obtain connections: settings service is not running."));
7192 				nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
7193 				nmc->should_wait = FALSE;
7194 				return nmc->return_value;
7195 			}
7196 	
7197 			/* connect to signal "connections-read" - emitted when connections are fetched and ready */
7198 			g_signal_connect (nmc->system_settings, NM_REMOTE_SETTINGS_CONNECTIONS_READ,
7199 					  G_CALLBACK (get_connections_cb), &args_info);
7200 	
7201 			/* The rest will be done in get_connection_cb() callback.
7202 			 * We need to wait for signals that connections are read.
7203 			 */
7204 			return NMC_RESULT_SUCCESS;
7205 		}
7206 	}
7207