cli: fix setting 'slave-type' for `nmcli connection add type *-slave`
[NetworkManager.git] / clients / cli / connections.c
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  * Copyright 2010 - 2015 Red Hat, Inc.
18  */
19
20 #include "nm-default.h"
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <signal.h>
28 #include <netinet/ether.h>
29 #include <readline/readline.h>
30 #include <readline/history.h>
31
32 #include "utils.h"
33 #include "common.h"
34 #include "settings.h"
35 #include "connections.h"
36 #include "nm-secret-agent-simple.h"
37 #include "polkit-agent.h"
38 #include "nm-vpn-helpers.h"
39
40 /* define some prompts for connection editor */
41 #define EDITOR_PROMPT_SETTING  _("Setting name? ")
42 #define EDITOR_PROMPT_PROPERTY _("Property name? ")
43 #define EDITOR_PROMPT_CON_TYPE _("Enter connection type: ")
44
45 /* define some other prompts */
46 #define PROMPT_CON_TYPE    _("Connection type: ")
47 #define PROMPT_VPN_TYPE    _("VPN type: ")
48 #define PROMPT_MASTER      _("Master: ")
49 #define PROMPT_CONNECTION  _("Connection (name, UUID, or path): ")
50 #define PROMPT_VPN_CONNECTION  _("VPN connection (name, UUID, or path): ")
51 #define PROMPT_CONNECTIONS _("Connection(s) (name, UUID, or path): ")
52 #define PROMPT_ACTIVE_CONNECTIONS _("Connection(s) (name, UUID, path or apath): ")
53 #define PROMPT_IP_TUNNEL_MODE _("Tunnel mode: ")
54 #define PROMPT_MACVLAN_MODE _("MACVLAN mode: ")
55
56 static const char *nmc_known_vpns[] = {
57         "openvpn",
58         "vpnc",
59         "pptp",
60         "openconnect",
61         "openswan",
62         "libreswan",
63         "strongswan",
64         "ssh",
65         "l2tp",
66         "iodine",
67         "fortisslvpn",
68         NULL
69 };
70
71 /* Available fields for 'connection show' */
72 static NmcOutputField nmc_fields_con_show[] = {
73         {"NAME",                 N_("NAME")},                  /* 0 */
74         {"UUID",                 N_("UUID")},                  /* 1 */
75         {"TYPE",                 N_("TYPE")},                  /* 2 */
76         {"TIMESTAMP",            N_("TIMESTAMP")},             /* 3 */
77         {"TIMESTAMP-REAL",       N_("TIMESTAMP-REAL")},        /* 4 */
78         {"AUTOCONNECT",          N_("AUTOCONNECT")},           /* 5 */
79         {"AUTOCONNECT-PRIORITY", N_("AUTOCONNECT-PRIORITY")},  /* 6 */
80         {"READONLY",             N_("READONLY")},              /* 7 */
81         {"DBUS-PATH",            N_("DBUS-PATH")},             /* 8 */
82         {"ACTIVE",               N_("ACTIVE")},                /* 9 */
83         {"DEVICE",               N_("DEVICE")},                /* 10 */
84         {"STATE",                N_("STATE")},                 /* 11 */
85         {"ACTIVE-PATH",          N_("ACTIVE-PATH")},           /* 12 */
86         {NULL, NULL}
87 };
88 #define NMC_FIELDS_CON_SHOW_ALL     "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,AUTOCONNECT-PRIORITY,READONLY,DBUS-PATH,"\
89                                     "ACTIVE,DEVICE,STATE,ACTIVE-PATH"
90 #define NMC_FIELDS_CON_SHOW_COMMON  "NAME,UUID,TYPE,DEVICE"
91
92 /* Helper macro to define fields */
93 #define SETTING_FIELD(setting, props) { setting, N_(setting), 0, props, NULL, FALSE, FALSE, 0 }
94
95 /* defined in settings.c */
96 extern NmcOutputField nmc_fields_setting_connection[];
97 extern NmcOutputField nmc_fields_setting_wired[];
98 extern NmcOutputField nmc_fields_setting_8021X[];
99 extern NmcOutputField nmc_fields_setting_wireless[];
100 extern NmcOutputField nmc_fields_setting_wireless_security[];
101 extern NmcOutputField nmc_fields_setting_ip4_config[];
102 extern NmcOutputField nmc_fields_setting_ip6_config[];
103 extern NmcOutputField nmc_fields_setting_serial[];
104 extern NmcOutputField nmc_fields_setting_ppp[];
105 extern NmcOutputField nmc_fields_setting_pppoe[];
106 extern NmcOutputField nmc_fields_setting_adsl[];
107 extern NmcOutputField nmc_fields_setting_gsm[];
108 extern NmcOutputField nmc_fields_setting_cdma[];
109 extern NmcOutputField nmc_fields_setting_bluetooth[];
110 extern NmcOutputField nmc_fields_setting_olpc_mesh[];
111 extern NmcOutputField nmc_fields_setting_vpn[];
112 extern NmcOutputField nmc_fields_setting_wimax[];
113 extern NmcOutputField nmc_fields_setting_infiniband[];
114 extern NmcOutputField nmc_fields_setting_bond[];
115 extern NmcOutputField nmc_fields_setting_vlan[];
116 extern NmcOutputField nmc_fields_setting_bridge[];
117 extern NmcOutputField nmc_fields_setting_bridge_port[];
118 extern NmcOutputField nmc_fields_setting_team[];
119 extern NmcOutputField nmc_fields_setting_team_port[];
120 extern NmcOutputField nmc_fields_setting_dcb[];
121 extern NmcOutputField nmc_fields_setting_tun[];
122 extern NmcOutputField nmc_fields_setting_ip_tunnel[];
123 extern NmcOutputField nmc_fields_setting_macvlan[];
124 extern NmcOutputField nmc_fields_setting_vxlan[];
125
126 /* Available settings for 'connection show <con>' - profile part */
127 static NmcOutputField nmc_fields_settings_names[] = {
128         SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME,        nmc_fields_setting_connection + 1),        /* 0 */
129         SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME,             nmc_fields_setting_wired + 1),             /* 1 */
130         SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME,            nmc_fields_setting_8021X + 1),             /* 2 */
131         SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME,          nmc_fields_setting_wireless + 1),          /* 3 */
132         SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, nmc_fields_setting_wireless_security + 1), /* 4 */
133         SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME,        nmc_fields_setting_ip4_config + 1),        /* 5 */
134         SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME,        nmc_fields_setting_ip6_config + 1),        /* 6 */
135         SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME,            nmc_fields_setting_serial + 1),            /* 7 */
136         SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME,               nmc_fields_setting_ppp + 1),               /* 8 */
137         SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME,             nmc_fields_setting_pppoe + 1),             /* 9 */
138         SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME,               nmc_fields_setting_gsm + 1),               /* 10 */
139         SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME,              nmc_fields_setting_cdma + 1),              /* 11 */
140         SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME,         nmc_fields_setting_bluetooth + 1),         /* 12 */
141         SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME,         nmc_fields_setting_olpc_mesh + 1),         /* 13 */
142         SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME,               nmc_fields_setting_vpn + 1),               /* 14 */
143         SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME,             nmc_fields_setting_wimax + 1),             /* 15 */
144         SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME,        nmc_fields_setting_infiniband + 1),        /* 16 */
145         SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME,              nmc_fields_setting_bond + 1),              /* 17 */
146         SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME,              nmc_fields_setting_vlan + 1),              /* 18 */
147         SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME,              nmc_fields_setting_adsl + 1),              /* 19 */
148         SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME,            nmc_fields_setting_bridge + 1),            /* 20 */
149         SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME,       nmc_fields_setting_bridge_port + 1),       /* 21 */
150         SETTING_FIELD (NM_SETTING_TEAM_SETTING_NAME,              nmc_fields_setting_team + 1),              /* 22 */
151         SETTING_FIELD (NM_SETTING_TEAM_PORT_SETTING_NAME,         nmc_fields_setting_team_port + 1),         /* 23 */
152         SETTING_FIELD (NM_SETTING_DCB_SETTING_NAME,               nmc_fields_setting_dcb + 1),               /* 24 */
153         SETTING_FIELD (NM_SETTING_TUN_SETTING_NAME,               nmc_fields_setting_tun + 1),               /* 25 */
154         SETTING_FIELD (NM_SETTING_IP_TUNNEL_SETTING_NAME,         nmc_fields_setting_ip_tunnel + 1),         /* 26 */
155         SETTING_FIELD (NM_SETTING_MACVLAN_SETTING_NAME,           nmc_fields_setting_macvlan + 1),           /* 27 */
156         SETTING_FIELD (NM_SETTING_VXLAN_SETTING_NAME,             nmc_fields_setting_vxlan + 1),             /* 28 */
157         {NULL, NULL, 0, NULL, NULL, FALSE, FALSE, 0}
158 };
159 #define NMC_FIELDS_SETTINGS_NAMES_ALL_X  NM_SETTING_CONNECTION_SETTING_NAME","\
160                                          NM_SETTING_WIRED_SETTING_NAME","\
161                                          NM_SETTING_802_1X_SETTING_NAME","\
162                                          NM_SETTING_WIRELESS_SETTING_NAME","\
163                                          NM_SETTING_WIRELESS_SECURITY_SETTING_NAME","\
164                                          NM_SETTING_IP4_CONFIG_SETTING_NAME","\
165                                          NM_SETTING_IP6_CONFIG_SETTING_NAME","\
166                                          NM_SETTING_SERIAL_SETTING_NAME","\
167                                          NM_SETTING_PPP_SETTING_NAME","\
168                                          NM_SETTING_PPPOE_SETTING_NAME","\
169                                          NM_SETTING_ADSL_SETTING_NAME","\
170                                          NM_SETTING_GSM_SETTING_NAME","\
171                                          NM_SETTING_CDMA_SETTING_NAME","\
172                                          NM_SETTING_BLUETOOTH_SETTING_NAME","\
173                                          NM_SETTING_OLPC_MESH_SETTING_NAME","\
174                                          NM_SETTING_VPN_SETTING_NAME","\
175                                          NM_SETTING_INFINIBAND_SETTING_NAME","\
176                                          NM_SETTING_BOND_SETTING_NAME","\
177                                          NM_SETTING_VLAN_SETTING_NAME","\
178                                          NM_SETTING_BRIDGE_SETTING_NAME","\
179                                          NM_SETTING_BRIDGE_PORT_SETTING_NAME","\
180                                          NM_SETTING_TEAM_SETTING_NAME","\
181                                          NM_SETTING_TEAM_PORT_SETTING_NAME"," \
182                                          NM_SETTING_DCB_SETTING_NAME"," \
183                                          NM_SETTING_TUN_SETTING_NAME"," \
184                                          NM_SETTING_IP_TUNNEL_SETTING_NAME"," \
185                                          NM_SETTING_MACVLAN_SETTING_NAME"," \
186                                          NM_SETTING_VXLAN_SETTING_NAME
187 #define NMC_FIELDS_SETTINGS_NAMES_ALL    NMC_FIELDS_SETTINGS_NAMES_ALL_X
188
189 /* Active connection data */
190 /* Available fields for GENERAL group */
191 static NmcOutputField nmc_fields_con_active_details_general[] = {
192         {"GROUP",         N_("GROUP")},        /* 0 */
193         {"NAME",          N_("NAME")},         /* 1 */
194         {"UUID",          N_("UUID")},         /* 2 */
195         {"DEVICES",       N_("DEVICES")},      /* 3 */
196         {"STATE",         N_("STATE")},        /* 4 */
197         {"DEFAULT",       N_("DEFAULT")},      /* 5 */
198         {"DEFAULT6",      N_("DEFAULT6")},     /* 6 */
199         {"SPEC-OBJECT",   N_("SPEC-OBJECT")},  /* 7 */
200         {"VPN",           N_("VPN")},          /* 8 */
201         {"DBUS-PATH",     N_("DBUS-PATH")},    /* 9 */
202         {"CON-PATH",      N_("CON-PATH")},     /* 10 */
203         {"ZONE",          N_("ZONE")},         /* 11 */
204         {"MASTER-PATH",   N_("MASTER-PATH")},  /* 12 */
205         {NULL, NULL}
206 };
207 #define NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL  "GROUP,NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,"\
208                                                    "VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH"
209
210 /* IP group is handled by common.c */
211
212 /* Available fields for VPN group */
213 static NmcOutputField nmc_fields_con_active_details_vpn[] = {
214         {"GROUP",     N_("GROUP")},      /* 0 */
215         {"TYPE",      N_("TYPE")},       /* 1 */
216         {"USERNAME",  N_("USERNAME")},   /* 2 */
217         {"GATEWAY",   N_("GATEWAY")},    /* 3 */
218         {"BANNER",    N_("BANNER")},     /* 4 */
219         {"VPN-STATE", N_("VPN-STATE")},  /* 5 */
220         {"CFG",       N_("CFG")},        /* 6 */
221         {NULL, NULL}
222 };
223 #define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL  "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG"
224
225 /* defined in common.c */
226 extern NmcOutputField nmc_fields_ip4_config[];
227 extern NmcOutputField nmc_fields_ip6_config[];
228 extern NmcOutputField nmc_fields_dhcp4_config[];
229 extern NmcOutputField nmc_fields_dhcp6_config[];
230
231 /* Available fields for 'connection show <con>' - active part */
232 static NmcOutputField nmc_fields_con_active_details_groups[] = {
233         {"GENERAL",  N_("GENERAL"), 0, nmc_fields_con_active_details_general + 1},  /* 0 */
234         {"IP4",      N_("IP4"),     0, nmc_fields_ip4_config + 1                },  /* 1 */
235         {"DHCP4",    N_("DHCP4"),   0, nmc_fields_dhcp4_config + 1              },  /* 2 */
236         {"IP6",      N_("IP6"),     0, nmc_fields_ip6_config + 1                },  /* 3 */
237         {"DHCP6",    N_("DHCP6"),   0, nmc_fields_dhcp6_config + 1              },  /* 4 */
238         {"VPN",      N_("VPN"),     0, nmc_fields_con_active_details_vpn + 1    },  /* 5 */
239         {NULL, NULL, 0, NULL}
240 };
241 #define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL  "GENERAL,IP4,DHCP4,IP6,DHCP6,VPN"
242
243 /* Pseudo group names for 'connection show <con>' */
244 /* e.g.: nmcli -f profile con show my-eth0 */
245 /* e.g.: nmcli -f active con show my-eth0 */
246 #define CON_SHOW_DETAIL_GROUP_PROFILE "profile"
247 #define CON_SHOW_DETAIL_GROUP_ACTIVE  "active"
248
249 /* glib main loop variable - defined in nmcli.c */
250 extern GMainLoop *loop;
251
252 static guint progress_id = 0;  /* ID of event source for displaying progress */
253
254 /* for readline TAB completion in editor */
255 typedef struct {
256         NmCli *nmc;
257         char *con_type;
258         NMConnection *connection;
259         NMSetting *setting;
260         const char *property;
261 } TabCompletionInfo;
262 static TabCompletionInfo nmc_tab_completion = {NULL, NULL, NULL, NULL};
263
264 /* Global variable defined in nmcli.c - used for TAB completion */
265 extern NmCli nm_cli;
266
267 static char *gen_connection_types (const char *text, int state);
268
269 static void
270 usage (void)
271 {
272         g_printerr (_("Usage: nmcli connection { COMMAND | help }\n\n"
273                       "COMMAND := { show | up | down | add | modify | edit | delete | monitor | reload | load }\n\n"
274                       "  show [--active] [--order <order spec>]\n"
275                       "  show [--active] [id | uuid | path | apath] <ID> ...\n\n"
276                       "  up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [passwd-file <file with passwords>]\n\n"
277                       "  down [id | uuid | path | apath] <ID> ...\n\n"
278                       "  add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- ([+|-]<setting>.<property> <value>)+]\n\n"
279                       "  modify [--temporary] [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n\n"
280                       "  clone [--temporary] [id | uuid | path ] <ID> <new name>\n\n"
281                       "  edit [id | uuid | path] <ID>\n"
282                       "  edit [type <new_con_type>] [con-name <new_con_name>]\n\n"
283                       "  delete [id | uuid | path] <ID>\n\n"
284                       "  monitor [id | uuid | path] <ID> ...\n\n"
285                       "  reload\n\n"
286                       "  load <filename> [ <filename>... ]\n\n"
287                       "  import [--temporary] type <type> file <file to import>\n\n"
288                       "  export [id | uuid | path] <ID> [<output file>]\n\n"));
289 }
290
291 static void
292 usage_connection_show (void)
293 {
294         g_printerr (_("Usage: nmcli connection show { ARGUMENTS | help }\n"
295                       "\n"
296                       "ARGUMENTS := [--active] [--order <order spec>]\n"
297                       "\n"
298                       "List in-memory and on-disk connection profiles, some of which may also be\n"
299                       "active if a device is using that connection profile. Without a parameter, all\n"
300                       "profiles are listed. When --active option is specified, only the active\n"
301                       "profiles are shown. --order allows custom connection ordering (see manual page).\n"
302                       "\n"
303                       "ARGUMENTS := [--active] [id | uuid | path | apath] <ID> ...\n"
304                       "\n"
305                       "Show details for specified connections. By default, both static configuration\n"
306                       "and active connection data are displayed. It is possible to filter the output\n"
307                       "using global '--fields' option. Refer to the manual page for more information.\n"
308                       "When --active option is specified, only the active profiles are taken into\n"
309                       "account. Use global --show-secrets option to reveal associated secrets as well.\n"));
310 }
311
312 static void
313 usage_connection_up (void)
314 {
315         g_printerr (_("Usage: nmcli connection up { ARGUMENTS | help }\n"
316                       "\n"
317                       "ARGUMENTS := [id | uuid | path] <ID> [ifname <ifname>] [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n"
318                       "\n"
319                       "Activate a connection on a device. The profile to activate is identified by its\n"
320                       "name, UUID or D-Bus path.\n"
321                       "\n"
322                       "ARGUMENTS := ifname <ifname> [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n"
323                       "\n"
324                       "Activate a device with a connection. The connection profile is selected\n"
325                       "automatically by NetworkManager.\n"
326                       "\n"
327                       "ifname      - specifies the device to active the connection on\n"
328                       "ap          - specifies AP to connect to (only valid for Wi-Fi)\n"
329                       "nsp         - specifies NSP to connect to (only valid for WiMAX)\n"
330                       "passwd-file - file with password(s) required to activate the connection\n\n"));
331 }
332
333 static void
334 usage_connection_down (void)
335 {
336         g_printerr (_("Usage: nmcli connection down { ARGUMENTS | help }\n"
337                       "\n"
338                       "ARGUMENTS := [id | uuid | path | apath] <ID> ...\n"
339                       "\n"
340                       "Deactivate a connection from a device (without preventing the device from\n"
341                       "further auto-activation). The profile to deactivate is identified by its name,\n"
342                       "UUID or D-Bus path.\n\n"));
343 }
344
345 static void
346 usage_connection_add (void)
347 {
348         g_printerr (_("Usage: nmcli connection add { ARGUMENTS | help }\n"
349                       "\n"
350                       "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- ([+|-]<setting>.<property> <value>)+]\n\n"
351                       "  COMMON_OPTIONS:\n"
352                       "                  type <type>\n"
353                       "                  ifname <interface name> | \"*\"\n"
354                       "                  [con-name <connection name>]\n"
355                       "                  [autoconnect yes|no]\n"
356                       "                  [save yes|no]\n"
357                       "                  [master <master (ifname, or connection UUID or name)>]\n"
358                       "                  [slave-type <master connection type>]\n\n"
359                       "  TYPE_SPECIFIC_OPTIONS:\n"
360                       "    ethernet:     [mac <MAC address>]\n"
361                       "                  [cloned-mac <cloned MAC address>]\n"
362                       "                  [mtu <MTU>]\n\n"
363                       "    wifi:         ssid <SSID>\n"
364                       "                  [mac <MAC address>]\n"
365                       "                  [cloned-mac <cloned MAC address>]\n"
366                       "                  [mtu <MTU>]\n"
367                       "                  [mode infrastructure|ap|adhoc]\n\n"
368                       "    wimax:        [mac <MAC address>]\n"
369                       "                  [nsp <NSP>]\n\n"
370                       "    pppoe:        username <PPPoE username>\n"
371                       "                  [password <PPPoE password>]\n"
372                       "                  [service <PPPoE service name>]\n"
373                       "                  [mtu <MTU>]\n"
374                       "                  [mac <MAC address>]\n\n"
375                       "    gsm:          apn <APN>\n"
376                       "                  [user <username>]\n"
377                       "                  [password <password>]\n\n"
378                       "    cdma:         [user <username>]\n"
379                       "                  [password <password>]\n\n"
380                       "    infiniband:   [mac <MAC address>]\n"
381                       "                  [mtu <MTU>]\n"
382                       "                  [transport-mode datagram | connected]\n"
383                       "                  [parent <ifname>]\n"
384                       "                  [p-key <IPoIB P_Key>]\n\n"
385                       "    bluetooth:    [addr <bluetooth address>]\n"
386                       "                  [bt-type panu|dun-gsm|dun-cdma]\n\n"
387                       "    vlan:         dev <parent device (connection UUID, ifname, or MAC)>\n"
388                       "                  id <VLAN ID>\n"
389                       "                  [flags <VLAN flags>]\n"
390                       "                  [ingress <ingress priority mapping>]\n"
391                       "                  [egress <egress priority mapping>]\n"
392                       "                  [mtu <MTU>]\n\n"
393                       "    bond:         [mode balance-rr (0) | active-backup (1) | balance-xor (2) | broadcast (3) |\n"
394                       "                        802.3ad    (4) | balance-tlb   (5) | balance-alb (6)]\n"
395                       "                  [primary <ifname>]\n"
396                       "                  [miimon <num>]\n"
397                       "                  [downdelay <num>]\n"
398                       "                  [updelay <num>]\n"
399                       "                  [arp-interval <num>]\n"
400                       "                  [arp-ip-target <num>]\n"
401                       "                  [lacp-rate slow (0) | fast (1)]\n\n"
402                       "    bond-slave:   master <master (ifname, or connection UUID or name)>\n\n"
403                       "    team:         [config <file>|<raw JSON data>]\n\n"
404                       "    team-slave:   master <master (ifname, or connection UUID or name)>\n"
405                       "                  [config <file>|<raw JSON data>]\n\n"
406                       "    bridge:       [stp yes|no]\n"
407                       "                  [priority <num>]\n"
408                       "                  [forward-delay <2-30>]\n"
409                       "                  [hello-time <1-10>]\n"
410                       "                  [max-age <6-40>]\n"
411                       "                  [ageing-time <0-1000000>]\n"
412                       "                  [multicast-snooping yes|no]\n"
413                       "                  [mac <MAC address>]\n\n"
414                       "    bridge-slave: master <master (ifname, or connection UUID or name)>\n"
415                       "                  [priority <0-63>]\n"
416                       "                  [path-cost <1-65535>]\n"
417                       "                  [hairpin yes|no]\n\n"
418                       "    vpn:          vpn-type vpnc|openvpn|pptp|openconnect|openswan|libreswan|ssh|l2tp|iodine|...\n"
419                       "                  [user <username>]\n\n"
420                       "    olpc-mesh:    ssid <SSID>\n"
421                       "                  [channel <1-13>]\n"
422                       "                  [dhcp-anycast <MAC address>]\n\n"
423                       "    adsl:         username <username>\n"
424                       "                  protocol pppoa|pppoe|ipoatm\n"
425                       "                  [password <password>]\n"
426                       "                  [encapsulation vcmux|llc]\n\n"
427                       "    tun:          mode tun|tap\n"
428                       "                  [owner <UID>]\n"
429                       "                  [group <GID>]\n"
430                       "                  [pi yes|no]\n"
431                       "                  [vnet-hdr yes|no]\n"
432                       "                  [multi-queue yes|no]\n\n"
433                       "    ip-tunnel:    mode ipip|gre|sit|isatap|vti|ip6ip6|ipip6|ip6gre|vti6\n"
434                       "                  remote <remote endpoint IP>\n"
435                       "                  [local <local endpoint IP>]\n"
436                       "                  [dev <parent device (ifname or connection UUID)>]\n\n"
437                       "    macvlan:      dev <parent device (connection UUID, ifname, or MAC)>\n"
438                       "                  mode vepa|bridge|private|passthru|source\n"
439                       "                  [tap yes|no]\n\n"
440                       "    vxlan:        id <VXLAN ID>\n"
441                       "                  remote <IP of multicast group or remote address>\n"
442                       "                  [local <source IP>]\n"
443                       "                  [dev <parent device (ifname or connection UUID)>]\n"
444                       "                  [source-port-min <0-65535>]\n"
445                       "                  [source-port-max <0-65535>]\n"
446                       "                  [destination-port <0-65535>]\n\n"
447                       "  SLAVE_OPTIONS:\n"
448                       "    bridge:       [priority <0-63>]\n"
449                       "                  [path-cost <1-65535>]\n"
450                       "                  [hairpin yes|no]\n\n"
451                       "    team:         [config <file>|<raw JSON data>]\n\n"
452                       "  IP_OPTIONS:\n"
453                       "                  [ip4 <IPv4 address>] [gw4 <IPv4 gateway>]\n"
454                       "                  [ip6 <IPv6 address>] [gw6 <IPv6 gateway>]\n\n"));
455 }
456
457 static void
458 usage_connection_modify (void)
459 {
460         g_printerr (_("Usage: nmcli connection modify { ARGUMENTS | help }\n"
461                       "\n"
462                       "ARGUMENTS := [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n"
463                       "\n"
464                       "Modify one or more properties of the connection profile.\n"
465                       "The profile is identified by its name, UUID or D-Bus path. For multi-valued\n"
466                       "properties you can use optional '+' or '-' prefix to the property name.\n"
467                       "The '+' sign allows appending items instead of overwriting the whole value.\n"
468                       "The '-' sign allows removing selected items instead of the whole value.\n"
469                       "\n"
470                       "Examples:\n"
471                       "nmcli con mod home-wifi wifi.ssid rakosnicek\n"
472                       "nmcli con mod em1-1 ipv4.method manual ipv4.addr \"192.168.1.2/24, 10.10.1.5/8\"\n"
473                       "nmcli con mod em1-1 +ipv4.dns 8.8.4.4\n"
474                       "nmcli con mod em1-1 -ipv4.dns 1\n"
475                       "nmcli con mod em1-1 -ipv6.addr \"abbe::cafe/56\"\n"
476                       "nmcli con mod bond0 +bond.options mii=500\n"
477                       "nmcli con mod bond0 -bond.options downdelay\n\n"));
478 }
479
480 static void
481 usage_connection_clone (void)
482 {
483         g_printerr (_("Usage: nmcli connection clone { ARGUMENTS | help }\n"
484                       "\n"
485                       "ARGUMENTS := [--temporary] [id | uuid | path] <ID> <new name>\n"
486                       "\n"
487                       "Clone an existing connection profile. The newly created connection will be\n"
488                       "the exact copy of the <ID>, except the uuid property (will be generated) and\n"
489                       "id (provided as <new name> argument).\n\n"));
490 }
491
492 static void
493 usage_connection_edit (void)
494 {
495         g_printerr (_("Usage: nmcli connection edit { ARGUMENTS | help }\n"
496                       "\n"
497                       "ARGUMENTS := [id | uuid | path] <ID>\n"
498                       "\n"
499                       "Edit an existing connection profile in an interactive editor.\n"
500                       "The profile is identified by its name, UUID or D-Bus path\n"
501                       "\n"
502                       "ARGUMENTS := [type <new connection type>] [con-name <new connection name>]\n"
503                       "\n"
504                       "Add a new connection profile in an interactive editor.\n\n"));
505 }
506
507 static void
508 usage_connection_delete (void)
509 {
510         g_printerr (_("Usage: nmcli connection delete { ARGUMENTS | help }\n"
511                       "\n"
512                       "ARGUMENTS := [id | uuid | path] <ID>\n"
513                       "\n"
514                       "Delete a connection profile.\n"
515                       "The profile is identified by its name, UUID or D-Bus path.\n\n"));
516 }
517
518 static void
519 usage_connection_monitor (void)
520 {
521         g_printerr (_("Usage: nmcli connection monitor { ARGUMENTS | help }\n"
522                       "\n"
523                       "ARGUMENTS := [id | uuid | path] <ID> ...\n"
524                       "\n"
525                       "Monitor connection profile activity.\n"
526                       "This command prints a line whenever the specified connection changes.\n"
527                       "Monitors all connection profiles in case none is specified.\n\n"));
528 }
529
530 static void
531 usage_connection_reload (void)
532 {
533         g_printerr (_("Usage: nmcli connection reload { help }\n"
534                       "\n"
535                       "Reload all connection files from disk.\n\n"));
536 }
537
538 static void
539 usage_connection_load (void)
540 {
541         g_printerr (_("Usage: nmcli connection load { ARGUMENTS | help }\n"
542                       "\n"
543                       "ARGUMENTS := <filename> [<filename>...]\n"
544                       "\n"
545                       "Load/reload one or more connection files from disk. Use this after manually\n"
546                       "editing a connection file to ensure that NetworkManager is aware of its latest\n"
547                       "state.\n\n"));
548 }
549
550 static void
551 usage_connection_import (void)
552 {
553         g_printerr (_("Usage: nmcli connection import { ARGUMENTS | help }\n"
554                       "\n"
555                       "ARGUMENTS := [--temporary] type <type> file <file to import>\n"
556                       "\n"
557                       "Import an external/foreign configuration as a NetworkManager connection profile.\n"
558                       "The type of the input file is specified by type option.\n"
559                       "Only VPN configurations are supported at the moment. The configuration\n"
560                       "is imported by NetworkManager VPN plugins.\n\n"));
561 }
562
563 static void
564 usage_connection_export (void)
565 {
566         g_printerr (_("Usage: nmcli connection export { ARGUMENTS | help }\n"
567                       "\n"
568                       "ARGUMENTS := [id | uuid | path] <ID> [<output file>]\n"
569                       "\n"
570                       "Export a connection. Only VPN connections are supported at the moment.\n"
571                       "The data are directed to standard output or to a file if a name is given.\n\n"));
572 }
573
574 static gboolean
575 usage_connection_second_level (const char *cmd)
576 {
577         gboolean ret = TRUE;
578
579         if (matches (cmd, "show") == 0)
580                 usage_connection_show ();
581         else if (matches (cmd, "up") == 0)
582                 usage_connection_up ();
583         else if (matches (cmd, "down") == 0)
584                 usage_connection_down ();
585         else if (matches (cmd, "add") == 0)
586                 usage_connection_add ();
587         else if (matches (cmd, "modify") == 0)
588                 usage_connection_modify ();
589         else if (matches (cmd, "clone") == 0)
590                 usage_connection_clone ();
591         else if (matches (cmd, "edit") == 0)
592                 usage_connection_edit ();
593         else if (matches (cmd, "delete") == 0)
594                 usage_connection_delete ();
595         else if (matches (cmd, "monitor") == 0)
596                 usage_connection_monitor ();
597         else if (matches (cmd, "reload") == 0)
598                 usage_connection_reload ();
599         else if (matches (cmd, "load") == 0)
600                 usage_connection_load ();
601         else if (matches (cmd, "import") == 0)
602                 usage_connection_import ();
603         else if (matches (cmd, "export") == 0)
604                 usage_connection_export ();
605         else
606                 ret = FALSE;
607         return ret;
608 }
609
610 /* quit main loop */
611 static void
612 quit (void)
613 {
614         if (progress_id) {
615                 g_source_remove (progress_id);
616                 progress_id = 0;
617                 nmc_terminal_erase_line ();
618         }
619
620         g_main_loop_quit (loop);  /* quit main loop */
621 }
622
623 static const char *
624 construct_header_name (const char *base, const char *spec)
625 {
626         static char header_name[128];
627
628         if (spec == NULL)
629                 return base;
630
631         g_strlcpy (header_name, base, sizeof (header_name));
632         g_strlcat (header_name, " (", sizeof (header_name));
633         g_strlcat (header_name, spec, sizeof (header_name));
634         g_strlcat (header_name, ")", sizeof (header_name));
635
636         return header_name;
637 }
638
639 static const char *
640 active_connection_state_to_string (NMActiveConnectionState state)
641 {
642         switch (state) {
643         case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
644                 return _("activating");
645         case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
646                 return _("activated");
647         case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
648                 return _("deactivating");
649         case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
650                 return _("deactivated");
651         case NM_ACTIVE_CONNECTION_STATE_UNKNOWN:
652         default:
653                 return _("unknown");
654         }
655 }
656
657 static const char *
658 vpn_connection_state_to_string (NMVpnConnectionState state)
659 {
660         switch (state) {
661         case NM_VPN_CONNECTION_STATE_PREPARE:
662                 return _("VPN connecting (prepare)");
663         case NM_VPN_CONNECTION_STATE_NEED_AUTH:
664                 return _("VPN connecting (need authentication)");
665         case NM_VPN_CONNECTION_STATE_CONNECT:
666                 return _("VPN connecting");
667         case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
668                 return _("VPN connecting (getting IP configuration)");
669         case NM_VPN_CONNECTION_STATE_ACTIVATED:
670                 return _("VPN connected");
671         case NM_VPN_CONNECTION_STATE_FAILED:
672                 return _("VPN connection failed");
673         case NM_VPN_CONNECTION_STATE_DISCONNECTED:
674                 return _("VPN disconnected");
675         default:
676                 return _("unknown");
677         }
678 }
679
680 /* Caller has to free the returned string */
681 static char *
682 get_ac_device_string (NMActiveConnection *active)
683 {
684         GString *dev_str;
685         const GPtrArray *devices;
686         int i;
687
688         if (!active)
689                 return NULL;
690
691         /* Get devices of the active connection */
692         dev_str = g_string_new (NULL);
693         devices = nm_active_connection_get_devices (active);
694         for (i = 0; i < devices->len; i++) {
695                 NMDevice *device = g_ptr_array_index (devices, i);
696                 const char *dev_iface = nm_device_get_iface (device);
697
698                 if (dev_iface) {
699                         g_string_append (dev_str, dev_iface);
700                         g_string_append_c (dev_str, ',');
701                 }
702         }
703         if (dev_str->len > 0)
704                 g_string_truncate (dev_str, dev_str->len - 1);  /* Cut off last ',' */
705
706         return g_string_free (dev_str, FALSE);
707 }
708
709 static NMActiveConnection *
710 get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection)
711 {
712         const char *con_path, *ac_con_path;
713         int i;
714         NMActiveConnection *ac = NULL;
715
716         /* Is the connection active? */
717         con_path = nm_connection_get_path (connection);
718         for (i = 0; i < active_cons->len; i++) {
719                 NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
720                 NMRemoteConnection *con;
721
722                 con = nm_active_connection_get_connection (candidate);
723                 ac_con_path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
724                 if (!g_strcmp0 (ac_con_path, con_path)) {
725                         ac = candidate;
726                         break;
727                 }
728         }
729         return ac;
730 }
731
732 /* Put secrets into local connection. */
733 static void
734 update_secrets_in_connection (NMRemoteConnection *remote, NMConnection *local)
735 {
736         GVariant *secrets;
737         int i;
738         GError *error = NULL;
739
740         for (i = 0; nmc_fields_settings_names[i].name; i++) {
741                 secrets = nm_remote_connection_get_secrets (remote, nmc_fields_settings_names[i].name, NULL, NULL);
742                 if (secrets) {
743                         if (!nm_connection_update_secrets (local, NULL, secrets, &error) && error) {
744                                 g_printerr (_("Error updating secrets for %s: %s\n"),
745                                             nmc_fields_settings_names[i].name,
746                                             error->message);
747                                 g_clear_error (&error);
748                         }
749                         g_variant_unref (secrets);
750                 }
751         }
752 }
753
754 static gboolean
755 nmc_connection_profile_details (NMConnection *connection, NmCli *nmc, gboolean secrets)
756 {
757         GError *error = NULL;
758         GArray *print_settings_array;
759         GPtrArray *prop_array = NULL;
760         int i;
761         char *fields_str;
762         char *fields_all =    NMC_FIELDS_SETTINGS_NAMES_ALL;
763         char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL;
764         const char *base_hdr = _("Connection profile details");
765         gboolean was_output = FALSE;
766
767         if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
768                 fields_str = fields_common;
769         else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
770                 fields_str = fields_all;
771         else
772                 fields_str = nmc->required_fields;
773
774         print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, TRUE, &prop_array, &error);
775         if (error) {
776                 g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
777                 g_error_free (error);
778                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
779                 return FALSE;
780         }
781         g_assert (print_settings_array);
782
783         /* Main header */
784         nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_connection_get_id (connection));
785         nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL,
786                                                          nmc_fields_settings_names, FALSE, NULL, NULL);
787
788         nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
789         print_required_fields (nmc, nmc_fields_settings_names);
790
791         /* Loop through the required settings and print them. */
792         for (i = 0; i < print_settings_array->len; i++) {
793                 NMSetting *setting;
794                 int section_idx = g_array_index (print_settings_array, int, i);
795                 const char *prop_name = (const char *) g_ptr_array_index (prop_array, i);
796
797                 if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
798                         g_print ("\n"); /* Empty line */
799
800                 was_output = FALSE;
801
802                 /* Remove any previous data */
803                 nmc_empty_output_fields (nmc);
804
805                 setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name);
806                 if (setting) {
807                         setting_details (setting, nmc, prop_name, secrets);
808                         was_output = TRUE;
809                         continue;
810                 }
811         }
812
813         g_array_free (print_settings_array, TRUE);
814         if (prop_array)
815                 g_ptr_array_free (prop_array, TRUE);
816
817         return TRUE;
818 }
819
820 static NMActiveConnection *
821 find_active_connection (const GPtrArray *active_cons,
822                         const GPtrArray *cons,
823                         const char *filter_type,
824                         const char *filter_val,
825                         int *idx)
826 {
827         int i;
828         int start = (idx && *idx > 0) ? *idx : 0;
829         const char *path, *a_path, *path_num, *a_path_num;
830         const char *id;
831         const char *uuid;
832         NMRemoteConnection *con;
833         NMActiveConnection *found = NULL;
834
835         for (i = start; i < active_cons->len; i++) {
836                 NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
837
838                 con = nm_active_connection_get_connection (candidate);
839
840                 id = nm_active_connection_get_id (candidate);
841                 uuid = nm_active_connection_get_uuid (candidate);
842                 path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
843                 path_num = path ? strrchr (path, '/') + 1 : NULL;
844                 a_path = nm_object_get_path (NM_OBJECT (candidate));
845                 a_path_num = a_path ? strrchr (a_path, '/') + 1 : NULL;
846
847                 /* When filter_type is NULL, compare connection ID (filter_val)
848                  * against all types. Otherwise, only compare against the specific
849                  * type. If 'path' or 'apath' filter types are specified, comparison
850                  * against numeric index (in addition to the whole path) is allowed.
851                  */
852                 if (   (   (!filter_type || strcmp (filter_type, "id")  == 0)
853                         && strcmp (filter_val, id) == 0)
854                     || (   (!filter_type || strcmp (filter_type, "uuid") == 0)
855                         && strcmp (filter_val, uuid) == 0)
856                     || (   (!filter_type || strcmp (filter_type, "path") == 0)
857                         && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)))
858                     || (   (!filter_type || strcmp (filter_type, "apath") == 0)
859                         && (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0)))) {
860                         if (!idx)
861                                 return candidate;
862                         if (found) {
863                                 *idx = i;
864                                 return found;
865                         }
866                         found = candidate;
867                 }
868         }
869
870         if (idx)
871                 *idx = 0;
872         return found;
873 }
874
875 static void
876 fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_only)
877 {
878         NMSettingConnection *s_con;
879         guint64 timestamp;
880         time_t timestamp_real;
881         char *timestamp_str;
882         char *timestamp_real_str = "";
883         char *prio_str;
884         NmcOutputField *arr;
885         NMActiveConnection *ac = NULL;
886         const char *ac_path = NULL;
887         const char *ac_state = NULL;
888         NMActiveConnectionState ac_state_int = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
889         char *ac_dev = NULL;
890
891         s_con = nm_connection_get_setting_connection (connection);
892         g_assert (s_con);
893
894         ac = get_ac_for_connection (nm_client_get_active_connections (nmc->client), connection);
895         if (active_only && !ac)
896                 return;
897
898         if (ac) {
899                 ac_path = nm_object_get_path (NM_OBJECT (ac));
900                 ac_state_int = nm_active_connection_get_state (ac);
901                 ac_state = active_connection_state_to_string (ac_state_int);
902                 ac_dev = get_ac_device_string (ac);
903         }
904
905         /* Obtain field values */
906         timestamp = nm_setting_connection_get_timestamp (s_con);
907         timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
908         if (timestamp) {
909                 timestamp_real = timestamp;
910                 timestamp_real_str = g_malloc0 (64);
911                 strftime (timestamp_real_str, 64, "%c", localtime (&timestamp_real));
912         }
913         prio_str = g_strdup_printf ("%u", nm_setting_connection_get_autoconnect_priority (s_con));
914
915         arr = nmc_dup_fields_array (nmc_fields_con_show,
916                                     sizeof (nmc_fields_con_show),
917                                     0);
918         /* Show active connections in color */
919         if (ac) {
920                 if (ac_state_int == NM_ACTIVE_CONNECTION_STATE_ACTIVATING)
921                         set_val_color_all (arr, NMC_TERM_COLOR_YELLOW);
922                 else if (ac_state_int == NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
923                         set_val_color_all (arr, NMC_TERM_COLOR_GREEN);
924                 else if (ac_state_int > NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
925                         set_val_color_all (arr, NMC_TERM_COLOR_RED);
926         }
927
928         set_val_strc (arr, 0, nm_setting_connection_get_id (s_con));
929         set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con));
930         set_val_strc (arr, 2, nm_setting_connection_get_connection_type (s_con));
931         set_val_str  (arr, 3, timestamp_str);
932         set_val_str  (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never")));
933         set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no"));
934         set_val_str  (arr, 6, prio_str);
935         set_val_strc (arr, 7, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no"));
936         set_val_strc (arr, 8, nm_connection_get_path (connection));
937         set_val_strc (arr, 9, ac ? _("yes") : _("no"));
938         set_val_str  (arr, 10, ac_dev);
939         set_val_strc (arr, 11, ac_state);
940         set_val_strc (arr, 12, ac_path);
941
942         g_ptr_array_add (nmc->output_data, arr);
943 }
944
945 static void
946 fill_output_connection_for_invisible (NMActiveConnection *ac, NmCli *nmc)
947 {
948         NmcOutputField *arr;
949         const char *ac_path = NULL;
950         const char *ac_state = NULL;
951         char *name, *ac_dev = NULL;
952
953         name = g_strdup_printf ("<invisible> %s", nm_active_connection_get_id (ac));
954         ac_path = nm_object_get_path (NM_OBJECT (ac));
955         ac_state = active_connection_state_to_string (nm_active_connection_get_state (ac));
956         ac_dev = get_ac_device_string (ac);
957
958         arr = nmc_dup_fields_array (nmc_fields_con_show,
959                                     sizeof (nmc_fields_con_show),
960                                     0);
961
962         set_val_str  (arr, 0, name);
963         set_val_strc (arr, 1, nm_active_connection_get_uuid (ac));
964         set_val_strc (arr, 2, nm_active_connection_get_connection_type (ac));
965         set_val_strc (arr, 3, NULL);
966         set_val_strc (arr, 4, NULL);
967         set_val_strc (arr, 5, NULL);
968         set_val_strc (arr, 6, NULL);
969         set_val_strc (arr, 7, NULL);
970         set_val_strc (arr, 8, NULL);
971         set_val_strc (arr, 9, _("yes"));
972         set_val_str  (arr, 10, ac_dev);
973         set_val_strc (arr, 11, ac_state);
974         set_val_strc (arr, 12, ac_path);
975
976         set_val_color_fmt_all (arr, NMC_TERM_FORMAT_DIM);
977
978         g_ptr_array_add (nmc->output_data, arr);
979 }
980
981 static void
982 fill_output_active_connection (NMActiveConnection *active,
983                                NmCli *nmc,
984                                gboolean with_group,
985                                guint32 o_flags)
986 {
987         NMRemoteConnection *con;
988         NMSettingConnection *s_con;
989         const GPtrArray *devices;
990         GString *dev_str;
991         NMActiveConnectionState state;
992         NMDevice *master;
993         const char *con_path = NULL, *con_zone = NULL;
994         int i;
995         NmcOutputField *tmpl, *arr;
996         size_t tmpl_len;
997         int idx_start = with_group ? 0 : 1;
998
999         con = nm_active_connection_get_connection (active);
1000         if (con) {
1001                 con_path = nm_connection_get_path (NM_CONNECTION (con));
1002                 s_con = nm_connection_get_setting_connection (NM_CONNECTION (con));
1003                 g_assert (s_con != NULL);
1004                 con_zone = nm_setting_connection_get_zone (s_con);
1005         }
1006
1007         state = nm_active_connection_get_state (active);
1008         master = nm_active_connection_get_master (active);
1009
1010         /* Get devices of the active connection */
1011         dev_str = g_string_new (NULL);
1012         devices = nm_active_connection_get_devices (active);
1013         for (i = 0; i < devices->len; i++) {
1014                 NMDevice *device = g_ptr_array_index (devices, i);
1015                 const char *dev_iface = nm_device_get_iface (device);
1016
1017                 if (dev_iface) {
1018                         g_string_append (dev_str, dev_iface);
1019                         g_string_append_c (dev_str, ',');
1020                 }
1021         }
1022         if (dev_str->len > 0)
1023                 g_string_truncate (dev_str, dev_str->len - 1);  /* Cut off last ',' */
1024
1025         tmpl = nmc_fields_con_active_details_general;
1026         tmpl_len = sizeof (nmc_fields_con_active_details_general);
1027         if (!with_group) {
1028                 tmpl++;
1029                 tmpl_len -= sizeof (NmcOutputField);
1030         }
1031
1032         /* Fill field values */
1033         arr = nmc_dup_fields_array (tmpl, tmpl_len, o_flags);
1034         if (with_group)
1035                 set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0].name);
1036         set_val_strc (arr, 1-idx_start, nm_active_connection_get_id (active));
1037         set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active));
1038         set_val_str  (arr, 3-idx_start, dev_str->str);
1039         set_val_strc (arr, 4-idx_start, active_connection_state_to_string (state));
1040         set_val_strc (arr, 5-idx_start, nm_active_connection_get_default (active) ? _("yes") : _("no"));
1041         set_val_strc (arr, 6-idx_start, nm_active_connection_get_default6 (active) ? _("yes") : _("no"));
1042         set_val_strc (arr, 7-idx_start, nm_active_connection_get_specific_object_path (active));
1043         set_val_strc (arr, 8-idx_start, NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no"));
1044         set_val_strc (arr, 9-idx_start, nm_object_get_path (NM_OBJECT (active)));
1045         set_val_strc (arr, 10-idx_start, con_path);
1046         set_val_strc (arr, 11-idx_start, con_zone);
1047         set_val_strc (arr, 12-idx_start, master ? nm_object_get_path (NM_OBJECT (master)) : NULL);
1048
1049         g_ptr_array_add (nmc->output_data, arr);
1050
1051         g_string_free (dev_str, FALSE);
1052 }
1053
1054 typedef struct {
1055         char **array;
1056         guint32 idx;
1057 } FillVPNDataInfo;
1058
1059 static void
1060 fill_vpn_data_item (const char *key, const char *value, gpointer user_data)
1061 {
1062         FillVPNDataInfo *info = (FillVPNDataInfo *) user_data;
1063
1064         info->array[info->idx++] = g_strdup_printf ("%s = %s", key, value);
1065 }
1066
1067 // FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c),
1068 // and in gnome-control-center as well. It could probably be shared somehow.
1069 static char *
1070 get_vpn_connection_type (NMConnection *connection)
1071 {
1072         const char *type, *p;
1073
1074         /* The service type is in form of "org.freedesktop.NetworkManager.vpnc".
1075          * Extract end part after last dot, e.g. "vpnc"
1076          */
1077         type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
1078         p = strrchr (type, '.');
1079         return g_strdup (p ? p + 1 : type);
1080 }
1081
1082 /* VPN parameters can be found at:
1083  * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h
1084  * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h
1085  * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h
1086  * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h
1087  * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h
1088  * See also 'properties' directory in these plugins.
1089  */
1090 static const gchar *
1091 find_vpn_gateway_key (const char *vpn_type)
1092 {
1093         if (g_strcmp0 (vpn_type, "openvpn") == 0)     return "remote";
1094         if (g_strcmp0 (vpn_type, "vpnc") == 0)        return "IPSec gateway";
1095         if (g_strcmp0 (vpn_type, "pptp") == 0)        return "gateway";
1096         if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway";
1097         if (g_strcmp0 (vpn_type, "openswan") == 0)    return "right";
1098         if (g_strcmp0 (vpn_type, "libreswan") == 0)   return "right";
1099         if (g_strcmp0 (vpn_type, "ssh") == 0)         return "remote";
1100         if (g_strcmp0 (vpn_type, "l2tp") == 0)        return "gateway";
1101         return "";
1102 }
1103
1104 static const gchar *
1105 find_vpn_username_key (const char *vpn_type)
1106 {
1107         if (g_strcmp0 (vpn_type, "openvpn") == 0)     return "username";
1108         if (g_strcmp0 (vpn_type, "vpnc") == 0)        return "Xauth username";
1109         if (g_strcmp0 (vpn_type, "pptp") == 0)        return "user";
1110         if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username";
1111         if (g_strcmp0 (vpn_type, "openswan") == 0)    return "leftxauthusername";
1112         if (g_strcmp0 (vpn_type, "libreswan") == 0)   return "leftxauthusername";
1113         if (g_strcmp0 (vpn_type, "l2tp") == 0)        return "user";
1114         return "";
1115 }
1116
1117 enum VpnDataItem {
1118         VPN_DATA_ITEM_GATEWAY,
1119         VPN_DATA_ITEM_USERNAME
1120 };
1121
1122 static const gchar *
1123 get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item)
1124 {
1125         const char *key;
1126         char *type = get_vpn_connection_type (connection);
1127
1128         switch (vpn_data_item) {
1129         case VPN_DATA_ITEM_GATEWAY:
1130                 key = find_vpn_gateway_key (type);
1131                 break;
1132         case VPN_DATA_ITEM_USERNAME:
1133                 key = find_vpn_username_key (type);
1134                 break;
1135         default:
1136                 key = "";
1137                 break;
1138         }
1139         g_free (type);
1140
1141         return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key);
1142 }
1143 /* FIXME end */
1144
1145 static gboolean
1146 nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
1147 {
1148         GError *error = NULL;
1149         GArray *print_groups;
1150         GPtrArray *group_fields = NULL;
1151         int i;
1152         char *fields_str;
1153         char *fields_all =    NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
1154         char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
1155         NmcOutputField *tmpl, *arr;
1156         size_t tmpl_len;
1157         const char *base_hdr = _("Activate connection details");
1158         gboolean was_output = FALSE;
1159
1160         if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
1161                 fields_str = fields_common;
1162         else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
1163                 fields_str = fields_all;
1164         else
1165                 fields_str = nmc->required_fields;
1166
1167         print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, TRUE, &group_fields, &error);
1168         if (error) {
1169                 g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
1170                 g_error_free (error);
1171                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1172                 return FALSE;
1173         }
1174         g_assert (print_groups);
1175
1176         /* Main header */
1177         nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_active_connection_get_uuid (acon));
1178         nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL,
1179                                                          nmc_fields_con_active_details_groups, FALSE, NULL, NULL);
1180
1181         nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
1182         print_required_fields (nmc, nmc_fields_con_active_details_groups);
1183
1184         /* Loop through the groups and print them. */
1185         for (i = 0; i < print_groups->len; i++) {
1186                 int group_idx = g_array_index (print_groups, int, i);
1187                 char *group_fld = (char *) g_ptr_array_index (group_fields, i);
1188
1189                 if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
1190                         g_print ("\n"); /* Empty line */
1191
1192                 was_output = FALSE;
1193
1194                 /* Remove any previous data */
1195                 nmc_empty_output_fields (nmc);
1196
1197                 /* GENERAL */
1198                 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) {
1199                         /* Add field names */
1200                         tmpl = nmc_fields_con_active_details_general;
1201                         tmpl_len = sizeof (nmc_fields_con_active_details_general);
1202                         nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL,
1203                                                                          tmpl, FALSE, NULL, NULL);
1204                         arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
1205                         g_ptr_array_add (nmc->output_data, arr);
1206
1207                         /* Fill in values */
1208                         fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX);
1209
1210                         print_data (nmc);  /* Print all data */
1211
1212                         was_output = TRUE;
1213                 }
1214
1215                 /* IP4 */
1216                 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[1].name) == 0) {
1217                         gboolean b1 = FALSE;
1218                         NMIPConfig *cfg4 = nm_active_connection_get_ip4_config (acon);
1219
1220                         b1 = print_ip4_config (cfg4, nmc, "IP4", group_fld);
1221                         was_output = was_output || b1;
1222                 }
1223
1224                 /* DHCP4 */
1225                 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[2].name) == 0) {
1226                         gboolean b1 = FALSE;
1227                         NMDhcpConfig *dhcp4 = nm_active_connection_get_dhcp4_config (acon);
1228
1229                         b1 = print_dhcp4_config (dhcp4, nmc, "DHCP4", group_fld);
1230                         was_output = was_output || b1;
1231                 }
1232
1233                 /* IP6 */
1234                 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[3].name) == 0) {
1235                         gboolean b1 = FALSE;
1236                         NMIPConfig *cfg6 = nm_active_connection_get_ip6_config (acon);
1237
1238                         b1 = print_ip6_config (cfg6, nmc, "IP6", group_fld);
1239                         was_output = was_output || b1;
1240                 }
1241
1242                 /* DHCP6 */
1243                 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[4].name) == 0) {
1244                         gboolean b1 = FALSE;
1245                         NMDhcpConfig *dhcp6 = nm_active_connection_get_dhcp6_config (acon);
1246
1247                         b1 = print_dhcp6_config (dhcp6, nmc, "DHCP6", group_fld);
1248                         was_output = was_output || b1;
1249                 }
1250
1251                 /* VPN */
1252                 if (NM_IS_VPN_CONNECTION (acon) &&
1253                     strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[5].name) == 0) {
1254                         NMConnection *con;
1255                         NMSettingConnection *s_con;
1256                         NMSettingVpn *s_vpn;
1257                         NMVpnConnectionState vpn_state;
1258                         char *type_str, *banner_str = NULL, *vpn_state_str;
1259                         const char *banner;
1260                         const char *username = NULL;
1261                         char **vpn_data_array = NULL;
1262                         guint32 items_num;
1263
1264                         con = NM_CONNECTION (nm_active_connection_get_connection (acon));
1265
1266                         s_con = nm_connection_get_setting_connection (con);
1267                         g_assert (s_con != NULL);
1268
1269                         tmpl = nmc_fields_con_active_details_vpn;
1270                         tmpl_len = sizeof (nmc_fields_con_active_details_vpn);
1271                         nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL,
1272                                                                          tmpl, FALSE, NULL, NULL);
1273                         arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
1274                         g_ptr_array_add (nmc->output_data, arr);
1275
1276                         s_vpn = nm_connection_get_setting_vpn (con);
1277                         if (s_vpn) {
1278                                 items_num = nm_setting_vpn_get_num_data_items (s_vpn);
1279                                 if (items_num > 0) {
1280                                         FillVPNDataInfo info;
1281
1282                                         vpn_data_array = g_new (char *, items_num + 1);
1283                                         info.array = vpn_data_array;
1284                                         info.idx = 0;
1285                                         nm_setting_vpn_foreach_data_item (s_vpn, &fill_vpn_data_item, &info);
1286                                         vpn_data_array[items_num] = NULL;
1287                                 }
1288                                 username = nm_setting_vpn_get_user_name (s_vpn);
1289                         }
1290
1291                         type_str = get_vpn_connection_type (con);
1292                         banner = nm_vpn_connection_get_banner (NM_VPN_CONNECTION (acon));
1293                         if (banner)
1294                                 banner_str = g_strescape (banner, "");
1295                         vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon));
1296                         vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state));
1297
1298                         /* Add values */
1299                         arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX);
1300                         set_val_strc (arr, 0, nmc_fields_con_active_details_groups[5].name);
1301                         set_val_str  (arr, 1, type_str);
1302                         set_val_strc (arr, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME));
1303                         set_val_strc (arr, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY));
1304                         set_val_str  (arr, 4, banner_str);
1305                         set_val_str  (arr, 5, vpn_state_str);
1306                         set_val_arr  (arr, 6, vpn_data_array);
1307                         g_ptr_array_add (nmc->output_data, arr);
1308
1309                         print_data (nmc);  /* Print all data */
1310                         was_output = TRUE;
1311                 }
1312         }
1313
1314         g_array_free (print_groups, TRUE);
1315         if (group_fields)
1316                 g_ptr_array_free (group_fields, TRUE);
1317
1318         return TRUE;
1319 }
1320
1321 static gboolean
1322 split_required_fields_for_con_show (const char *input,
1323                                     char **profile_flds,
1324                                     char **active_flds,
1325                                     GError **error)
1326 {
1327         char **fields, **iter;
1328         char *dot;
1329         GString *str1, *str2;
1330         gboolean found;
1331         gboolean group_profile = FALSE;
1332         gboolean group_active = FALSE;
1333         gboolean success = TRUE;
1334         gboolean is_all, is_common;
1335         int i;
1336
1337         if (!input) {
1338                 *profile_flds = NULL;
1339                 *active_flds = NULL;
1340                 return TRUE;
1341         }
1342
1343         str1 = g_string_new (NULL);
1344         str2 = g_string_new (NULL);
1345
1346         /* Split supplied fields string */
1347         fields = g_strsplit_set (input, ",", -1);
1348         for (iter = fields; iter && *iter; iter++) {
1349                 g_strstrip (*iter);
1350                 dot = strchr (*iter, '.');
1351                 if (dot)
1352                         *dot = '\0';
1353
1354                 is_all = !dot && strcasecmp (*iter, "all") == 0;
1355                 is_common = !dot && strcasecmp (*iter, "common") == 0;
1356
1357                 found = FALSE;
1358
1359                 for (i = 0; nmc_fields_settings_names[i].name; i++) {
1360                         if (   is_all || is_common
1361                             || !strcasecmp (*iter, nmc_fields_settings_names[i].name)) {
1362                                 if (dot)
1363                                         *dot = '.';
1364                                 g_string_append (str1, *iter);
1365                                 g_string_append_c (str1, ',');
1366                                 found = TRUE;
1367                                 break;
1368                         }
1369                 }
1370                 if (found)
1371                         continue;
1372                 for (i = 0; nmc_fields_con_active_details_groups[i].name; i++) {
1373                         if (   is_all || is_common
1374                             || !strcasecmp (*iter, nmc_fields_con_active_details_groups[i].name)) {
1375                                 if (dot)
1376                                         *dot = '.';
1377                                 g_string_append (str2, *iter);
1378                                 g_string_append_c (str2, ',');
1379                                 found = TRUE;
1380                                 break;
1381                         }
1382                 }
1383                 if (!found) {
1384                         if (dot)
1385                                 *dot = '.';
1386                         if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_PROFILE))
1387                                 group_profile = TRUE;
1388                         else if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_ACTIVE))
1389                                 group_active = TRUE;
1390                         else {
1391                                 char *allowed1 = nmc_get_allowed_fields (nmc_fields_settings_names, -1);
1392                                 char *allowed2 = nmc_get_allowed_fields (nmc_fields_con_active_details_groups, -1);
1393                                 g_set_error (error, NMCLI_ERROR, 0, _("invalid field '%s'; allowed fields: %s and %s, or %s,%s"),
1394                                              *iter, allowed1, allowed2, CON_SHOW_DETAIL_GROUP_PROFILE, CON_SHOW_DETAIL_GROUP_ACTIVE);
1395                                 g_free (allowed1);
1396                                 g_free (allowed2);
1397                                 success = FALSE;
1398                                 break;
1399                         }
1400                 }
1401         }
1402         if (fields)
1403                 g_strfreev (fields);
1404
1405         /* Handle pseudo groups: profile, active */
1406         if (success && group_profile) {
1407                 if (str1->len > 0) {
1408                         g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"),
1409                                      CON_SHOW_DETAIL_GROUP_PROFILE);
1410                         success = FALSE;
1411                 } else
1412                         g_string_assign (str1, "all,");
1413         }
1414         if (success && group_active) {
1415                 if (str2->len > 0) {
1416                         g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"),
1417                                      CON_SHOW_DETAIL_GROUP_ACTIVE);
1418                         success = FALSE;
1419                 } else
1420                         g_string_assign (str2, "all,");
1421         }
1422
1423         if (success) {
1424                 if (str1->len > 0)
1425                         g_string_truncate (str1, str1->len - 1);
1426                 if (str2->len > 0)
1427                         g_string_truncate (str2, str2->len - 1);
1428                 *profile_flds = g_string_free (str1, str1->len == 0);
1429                 *active_flds = g_string_free (str2, str2->len == 0);
1430         } else {
1431                 g_string_free (str1, TRUE);
1432                 g_string_free (str2, TRUE);
1433         }
1434         return success;
1435 }
1436
1437 typedef enum {
1438         NMC_SORT_ACTIVE     =  1,
1439         NMC_SORT_ACTIVE_INV = -1,
1440         NMC_SORT_NAME       =  2,
1441         NMC_SORT_NAME_INV   = -2,
1442         NMC_SORT_TYPE       =  3,
1443         NMC_SORT_TYPE_INV   = -3,
1444         NMC_SORT_PATH       =  4,
1445         NMC_SORT_PATH_INV   = -4,
1446 } NmcSortOrder;
1447
1448 typedef struct {
1449         NmCli *nmc;
1450         const GArray *order;
1451 } NmcSortInfo;
1452
1453 static int
1454 compare_connections (gconstpointer a, gconstpointer b, gpointer user_data)
1455 {
1456         NMConnection *ca = *(NMConnection **)a;
1457         NMConnection *cb = *(NMConnection **)b;
1458         NMActiveConnection *aca, *acb;
1459         NmcSortInfo *info = (NmcSortInfo *) user_data;
1460         GArray *default_order = NULL;
1461         const GArray *order;
1462         NmcSortOrder item;
1463         int cmp = 0, i;
1464         const char *tmp1, *tmp2;
1465         unsigned long tmp1_int, tmp2_int;
1466
1467         if (info->order )
1468                 order = info->order;
1469         else {
1470                 NmcSortOrder def[] = { NMC_SORT_ACTIVE, NMC_SORT_NAME, NMC_SORT_PATH };
1471                 int num = G_N_ELEMENTS (def);
1472                 default_order = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), num);
1473                 g_array_append_vals (default_order, def, num);
1474                 order = default_order;
1475         }
1476
1477         for (i = 0; i < order->len; i++) {
1478                 item = g_array_index (order, NmcSortOrder, i); 
1479                 switch (item) {
1480                 case NMC_SORT_ACTIVE:
1481                 case NMC_SORT_ACTIVE_INV:
1482                         aca = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), ca);
1483                         acb = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), cb);
1484                         cmp = (aca && !acb) ? -1 : (!aca && acb) ? 1 : 0;
1485                         if (item == NMC_SORT_ACTIVE_INV)
1486                                 cmp = -(cmp);
1487                         break;
1488                 case NMC_SORT_TYPE:
1489                 case NMC_SORT_TYPE_INV:
1490                         cmp = g_strcmp0 (nm_connection_get_connection_type (ca),
1491                                          nm_connection_get_connection_type (cb));
1492                         if (item == NMC_SORT_TYPE_INV)
1493                                 cmp = -(cmp);
1494                         break;
1495                 case NMC_SORT_NAME:
1496                 case NMC_SORT_NAME_INV:
1497                         cmp = g_strcmp0 (nm_connection_get_id (ca),
1498                                          nm_connection_get_id (cb));
1499                         if (item == NMC_SORT_NAME_INV)
1500                                 cmp = -(cmp);
1501                         break;
1502                 case NMC_SORT_PATH:
1503                 case NMC_SORT_PATH_INV:
1504                         tmp1 = nm_connection_get_path (ca);
1505                         tmp2 = nm_connection_get_path (cb);
1506                         tmp1 = tmp1 ? strrchr (tmp1, '/') : "0";
1507                         tmp2 = tmp2 ? strrchr (tmp2, '/') : "0";
1508                         nmc_string_to_uint (tmp1 ? tmp1+1 : "0", FALSE, 0, 0, &tmp1_int);
1509                         nmc_string_to_uint (tmp2 ? tmp2+1 : "0", FALSE, 0, 0, &tmp2_int);
1510                         cmp = (int) tmp1_int - tmp2_int;
1511                         if (item == NMC_SORT_PATH_INV)
1512                                 cmp = -(cmp);
1513                         break;
1514                 default:
1515                         cmp = 0;
1516                         break;
1517                 }
1518                 if (cmp != 0)
1519                         goto end;
1520         }
1521 end:
1522         if (default_order)
1523                 g_array_unref (default_order);
1524         return cmp;
1525 }
1526
1527 static GPtrArray *
1528 sort_connections (const GPtrArray *cons, NmCli *nmc, const GArray *order)
1529 {
1530         GPtrArray *sorted;
1531         int i;
1532         NmcSortInfo compare_info;
1533
1534         if (!cons)
1535                 return NULL;
1536
1537         compare_info.nmc = nmc;
1538         compare_info.order = order;
1539
1540         sorted = g_ptr_array_sized_new (cons->len);
1541         for (i = 0; i < cons->len; i++)
1542                 g_ptr_array_add (sorted, cons->pdata[i]);
1543         g_ptr_array_sort_with_data (sorted, compare_connections, &compare_info);
1544         return sorted;
1545 }
1546
1547 static int
1548 compare_ac_connections (gconstpointer a, gconstpointer b, gpointer user_data)
1549 {
1550         NMActiveConnection *ca = *(NMActiveConnection **)a;
1551         NMActiveConnection *cb = *(NMActiveConnection **)b;
1552         int cmp;
1553
1554         /* Sort states first */
1555         cmp = nm_active_connection_get_state (cb) - nm_active_connection_get_state (ca);
1556         if (cmp != 0)
1557                 return cmp;
1558
1559         cmp = g_strcmp0 (nm_active_connection_get_id (ca),
1560                          nm_active_connection_get_id (cb));
1561         if (cmp != 0)
1562                 return cmp;
1563
1564         return g_strcmp0 (nm_active_connection_get_connection_type (ca),
1565                           nm_active_connection_get_connection_type (cb));
1566 }
1567
1568 static GPtrArray *
1569 get_invisible_active_connections (NmCli *nmc)
1570 {
1571         const GPtrArray *acons;
1572         GPtrArray *invisibles;
1573         int a, c;
1574
1575         g_return_val_if_fail (nmc != NULL, NULL);
1576
1577         invisibles = g_ptr_array_new ();
1578         acons = nm_client_get_active_connections (nmc->client);
1579         for (a = 0; a < acons->len; a++) {
1580                 gboolean found = FALSE;
1581                 NMActiveConnection *acon = g_ptr_array_index (acons, a);
1582                 const char *a_uuid = nm_active_connection_get_uuid (acon);
1583
1584                 for (c = 0; c < nmc->connections->len; c++) {
1585                         NMConnection *con = g_ptr_array_index (nmc->connections, c);
1586                         const char *c_uuid = nm_connection_get_uuid (con);
1587
1588                         if (strcmp (a_uuid, c_uuid) == 0) {
1589                                 found = TRUE;
1590                                 break;
1591                         }
1592                 }
1593                 /* Active connection is not in connections array, add it to  */
1594                 if (!found)
1595                         g_ptr_array_add (invisibles, acon);
1596         }
1597         g_ptr_array_sort_with_data (invisibles, compare_ac_connections, NULL);
1598         return invisibles;
1599 }
1600
1601 static NMCResultCode
1602 do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets,
1603                      const GArray *order, int argc, char **argv)
1604 {
1605         GError *err = NULL;
1606         char *profile_flds = NULL, *active_flds = NULL;
1607         GPtrArray *invisibles, *sorted_cons;
1608
1609         if (argc == 0) {
1610                 char *fields_str;
1611                 char *fields_all =    NMC_FIELDS_CON_SHOW_ALL;
1612                 char *fields_common = NMC_FIELDS_CON_SHOW_COMMON;
1613                 NmcOutputField *tmpl, *arr;
1614                 size_t tmpl_len;
1615                 int i;
1616
1617                 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
1618                         fields_str = fields_common;
1619                 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
1620                         fields_str = fields_all;
1621                 else
1622                         fields_str = nmc->required_fields;
1623
1624                 tmpl = nmc_fields_con_show;
1625                 tmpl_len = sizeof (nmc_fields_con_show);
1626                 nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err);
1627                 if (err) {
1628                         goto finish;
1629                 }
1630                 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &err))
1631                         goto finish;
1632
1633                 /* Add headers */
1634                 nmc->print_fields.header_name = active_only ? _("NetworkManager active profiles") :
1635                                                               _("NetworkManager connection profiles");
1636                 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES);
1637                 g_ptr_array_add (nmc->output_data, arr);
1638
1639                 /* There might be active connections not present in connection list
1640                  * (e.g. private connections of a different user). Show them as well. */
1641                 invisibles = get_invisible_active_connections (nmc);
1642                 for (i = 0; i < invisibles->len; i++)
1643                         fill_output_connection_for_invisible (invisibles->pdata[i], nmc);
1644                 g_ptr_array_free (invisibles, TRUE);
1645
1646                 /* Sort the connections and fill the output data */
1647                 sorted_cons = sort_connections (nmc->connections, nmc, order);
1648                 for (i = 0; i < sorted_cons->len; i++)
1649                         fill_output_connection (sorted_cons->pdata[i], nmc, active_only);
1650                 g_ptr_array_free (sorted_cons, TRUE);
1651
1652                 print_data (nmc);  /* Print all data */
1653         } else {
1654                 gboolean new_line = FALSE;
1655                 gboolean without_fields = (nmc->required_fields == NULL);
1656                 const GPtrArray *active_cons = nm_client_get_active_connections (nmc->client);
1657                 int pos = 0;
1658
1659                 /* multiline mode is default for 'connection show <ID>' */
1660                 if (!nmc->mode_specified)
1661                         nmc->multiline_output = TRUE;
1662
1663                 /* Split required fields into the settings and active ones. */
1664                 if (!split_required_fields_for_con_show (nmc->required_fields, &profile_flds, &active_flds, &err))
1665                         goto finish;
1666                 g_free (nmc->required_fields);
1667                 nmc->required_fields = NULL;
1668
1669                 while (argc > 0) {
1670                         gboolean res;
1671                         NMConnection *con;
1672                         NMActiveConnection *acon = NULL;
1673                         const char *selector = NULL;
1674
1675                         if (   strcmp (*argv, "id") == 0
1676                             || strcmp (*argv, "uuid") == 0
1677                             || strcmp (*argv, "path") == 0
1678                             || strcmp (*argv, "apath") == 0) {
1679                                 selector = *argv;
1680                                 if (next_arg (&argc, &argv) != 0) {
1681                                         g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1682                                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1683                                         goto finish;
1684                                 }
1685                         }
1686
1687                         /* Find connection by id, uuid, path or apath */
1688                         con = nmc_find_connection (nmc->connections, selector, *argv, &pos);
1689                         if (!con) {
1690                                 acon = find_active_connection (active_cons, nmc->connections, selector, *argv, NULL);
1691                                 if (acon)
1692                                         con = NM_CONNECTION (nm_active_connection_get_connection (acon));
1693                         }
1694                         
1695                         if (!con && !acon) {
1696                                 g_string_printf (nmc->return_text, _("Error: %s - no such connection profile."), *argv);
1697                                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
1698                                 goto finish;
1699                         }
1700
1701                         /* Print connection details:
1702                          * Usually we have both static and active connection.
1703                          * But when a connection is private to a user, another user
1704                          * may see only the active connection.
1705                          */
1706
1707                         /* Filter only active connections */
1708                         if (!acon)
1709                                 acon = get_ac_for_connection (active_cons, con);
1710                         if (active_only && !acon) {
1711                                 next_arg (&argc, &argv);
1712                                 continue;
1713                         }
1714
1715                         /* Show an empty line between connections */
1716                         if (new_line)
1717                                 g_print ("\n");
1718
1719                         /* Show profile configuration */
1720                         if (without_fields || profile_flds) {
1721                                 if (con) {
1722                                         nmc->required_fields = profile_flds;
1723                                         if (show_secrets)
1724                                                 update_secrets_in_connection (NM_REMOTE_CONNECTION (con), con);
1725                                         res = nmc_connection_profile_details (con, nmc, show_secrets);
1726                                         nmc->required_fields = NULL;
1727                                         if (!res)
1728                                                 goto finish;
1729                                 }
1730                         }
1731
1732                         /* If the profile is active, print also active details */
1733                         if (without_fields || active_flds) {
1734                                 if (acon) {
1735                                         nmc->required_fields = active_flds;
1736                                         res = nmc_active_connection_details (acon, nmc);
1737                                         nmc->required_fields = NULL;
1738                                         if (!res)
1739                                                 goto finish;
1740                                 }
1741                         }
1742                         new_line = TRUE;
1743                         
1744                         /* Take next argument.
1745                          * But for pos != NULL we have more connections of the same name,
1746                          * so process the same argument again.
1747                          */
1748                         if (!pos)
1749                                 next_arg (&argc, &argv);
1750                 }
1751         }
1752
1753 finish:
1754         if (err) {
1755                 g_string_printf (nmc->return_text, _("Error: %s."), err->message);
1756                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1757                 g_error_free (err);
1758         }
1759         g_free (profile_flds);
1760         g_free (active_flds);
1761         return nmc->return_value;
1762 }
1763
1764 static NMActiveConnection *
1765 get_default_active_connection (NmCli *nmc, NMDevice **device)
1766 {
1767         NMActiveConnection *default_ac = NULL;
1768         NMDevice *non_default_device = NULL;
1769         NMActiveConnection *non_default_ac = NULL;
1770         const GPtrArray *connections;
1771         int i;
1772
1773         g_return_val_if_fail (nmc != NULL, NULL);
1774         g_return_val_if_fail (device != NULL, NULL);
1775         g_return_val_if_fail (*device == NULL, NULL);
1776
1777         connections = nm_client_get_active_connections (nmc->client);
1778         for (i = 0; i < connections->len; i++) {
1779                 NMActiveConnection *candidate = g_ptr_array_index (connections, i);
1780                 const GPtrArray *devices;
1781
1782                 devices = nm_active_connection_get_devices (candidate);
1783                 if (!devices->len)
1784                         continue;
1785
1786                 if (nm_active_connection_get_default (candidate)) {
1787                         if (!default_ac) {
1788                                 *device = g_ptr_array_index (devices, 0);
1789                                 default_ac = candidate;
1790                         }
1791                 } else {
1792                         if (!non_default_ac) {
1793                                 non_default_device = g_ptr_array_index (devices, 0);
1794                                 non_default_ac = candidate;
1795                         }
1796                 }
1797         }
1798
1799         /* Prefer the default connection if one exists, otherwise return the first
1800          * non-default connection.
1801          */
1802         if (!default_ac && non_default_ac) {
1803                 default_ac = non_default_ac;
1804                 *device = non_default_device;
1805         }
1806         return default_ac;
1807 }
1808
1809 /* Find a device to activate the connection on.
1810  * IN:  connection:  connection to activate
1811  *      iface:       device interface name to use (optional)
1812  *      ap:          access point to use (optional; valid just for 802-11-wireless)
1813  *      nsp:         Network Service Provider to use (option; valid only for wimax)
1814  * OUT: device:      found device
1815  *      spec_object: specific_object path of NMAccessPoint
1816  * RETURNS: TRUE when a device is found, FALSE otherwise.
1817  */
1818 static gboolean
1819 find_device_for_connection (NmCli *nmc,
1820                             NMConnection *connection,
1821                             const char *iface,
1822                             const char *ap,
1823                             const char *nsp,
1824                             NMDevice **device,
1825                             const char **spec_object,
1826                             GError **error)
1827 {
1828         NMSettingConnection *s_con;
1829         const char *con_type;
1830         int i, j;
1831
1832         g_return_val_if_fail (nmc != NULL, FALSE);
1833         g_return_val_if_fail (iface || ap || nsp, FALSE);
1834         g_return_val_if_fail (device != NULL && *device == NULL, FALSE);
1835         g_return_val_if_fail (spec_object != NULL && *spec_object == NULL, FALSE);
1836         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1837
1838         s_con = nm_connection_get_setting_connection (connection);
1839         g_assert (s_con);
1840         con_type = nm_setting_connection_get_connection_type (s_con);
1841
1842         if (strcmp (con_type, NM_SETTING_VPN_SETTING_NAME) == 0) {
1843                 /* VPN connections */
1844                 NMActiveConnection *active = NULL;
1845                 if (iface) {
1846                         *device = nm_client_get_device_by_iface (nmc->client, iface);
1847                         if (*device)
1848                                 active = nm_device_get_active_connection (*device);
1849
1850                         if (!active) {
1851                                 g_set_error (error, NMCLI_ERROR, 0, _("no active connection on device '%s'"), iface);
1852                                 return FALSE;
1853                         }
1854                         *spec_object = nm_object_get_path (NM_OBJECT (active));
1855                         return TRUE;
1856                 } else {
1857                         active = get_default_active_connection (nmc, device);
1858                         if (!active) {
1859                                 g_set_error_literal (error, NMCLI_ERROR, 0, _("no active connection or device"));
1860                                 return FALSE;
1861                         }
1862                         *spec_object = nm_object_get_path (NM_OBJECT (active));
1863                         return TRUE;
1864                 }
1865         } else {
1866                 /* Other connections */
1867                 NMDevice *found_device = NULL;
1868                 const GPtrArray *devices = nm_client_get_devices (nmc->client);
1869
1870                 for (i = 0; i < devices->len && !found_device; i++) {
1871                         NMDevice *dev = g_ptr_array_index (devices, i);
1872
1873                         if (iface) {
1874                                 const char *dev_iface = nm_device_get_iface (dev);
1875                                 if (   !g_strcmp0 (dev_iface, iface)
1876                                     && nm_device_connection_compatible (dev, connection, NULL)) {
1877                                         found_device = dev;
1878                                 }
1879                         } else {
1880                                 if (nm_device_connection_compatible (dev, connection, NULL)) {
1881                                         found_device = dev;
1882                                 }
1883                         }
1884
1885                         if (found_device && ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) {
1886                                 char *bssid_up = g_ascii_strup (ap, -1);
1887                                 const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev));
1888                                 found_device = NULL;  /* Mark as not found; set to the device again later, only if AP matches */
1889
1890                                 for (j = 0; j < aps->len; j++) {
1891                                         NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j);
1892                                         const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap);
1893
1894                                         if (!strcmp (bssid_up, candidate_bssid)) {
1895                                                 found_device = dev;
1896                                                 *spec_object = nm_object_get_path (NM_OBJECT (candidate_ap));
1897                                                 break;
1898                                         }
1899                                 }
1900                                 g_free (bssid_up);
1901                         }
1902
1903                 }
1904
1905                 if (found_device) {
1906                         *device = found_device;
1907                         return TRUE;
1908                 } else {
1909                         if (iface)
1910                                 g_set_error (error, NMCLI_ERROR, 0, _("device '%s' not compatible with connection '%s'"),
1911                                              iface, nm_setting_connection_get_id (s_con));
1912                         else
1913                                 g_set_error (error, NMCLI_ERROR, 0, _("no device found for connection '%s'"),
1914                                              nm_setting_connection_get_id (s_con));
1915                         return FALSE;
1916                 }
1917         }
1918 }
1919
1920 static const char *
1921 vpn_connection_state_reason_to_string (NMVpnConnectionStateReason reason)
1922 {
1923         switch (reason) {
1924         case NM_VPN_CONNECTION_STATE_REASON_UNKNOWN:
1925                 return _("unknown reason");
1926         case NM_VPN_CONNECTION_STATE_REASON_NONE:
1927                 return _("none");
1928         case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED:
1929                 return _("the user was disconnected");
1930         case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED:
1931                 return _("the base network connection was interrupted");
1932         case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED:
1933                 return _("the VPN service stopped unexpectedly");
1934         case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID:
1935                 return _("the VPN service returned invalid configuration");
1936         case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT:
1937                 return _("the connection attempt timed out");
1938         case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT:
1939                 return _("the VPN service did not start in time");
1940         case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED:
1941                 return _("the VPN service failed to start");
1942         case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS:
1943                 return _("no valid VPN secrets");
1944         case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED:
1945                 return _("invalid VPN secrets");
1946         case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED:
1947                 return _("the connection was removed");
1948         default:
1949                 return _("unknown");
1950         }
1951 }
1952
1953 static void
1954 device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data)
1955 {
1956         NmCli *nmc = (NmCli *) user_data;
1957         NMActiveConnection *active;
1958         NMDeviceState state;
1959         NMActiveConnectionState ac_state;
1960
1961         active = nm_device_get_active_connection (device);
1962         state = nm_device_get_state (device);
1963
1964         ac_state = active ? nm_active_connection_get_state (active) : NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
1965
1966         if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1967                 if (nmc->print_output == NMC_PRINT_PRETTY)
1968                         nmc_terminal_erase_line ();
1969                 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
1970                          nm_object_get_path (NM_OBJECT (active)));
1971                 quit ();
1972         } else if (   ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING
1973                    && state >= NM_DEVICE_STATE_IP_CONFIG) {
1974                 if (nmc->print_output == NMC_PRINT_PRETTY)
1975                         nmc_terminal_erase_line ();
1976                 g_print (_("Connection successfully activated (master waiting for slaves) (D-Bus active path: %s)\n"),
1977                          nm_object_get_path (NM_OBJECT (active)));
1978                 quit ();
1979         } else if (active && ac_state != NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
1980                 g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
1981                 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1982                 quit ();
1983         }
1984 }
1985
1986 static void
1987 active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data)
1988 {
1989         NmCli *nmc = (NmCli *) user_data;
1990         NMActiveConnectionState state;
1991
1992         state = nm_active_connection_get_state (active);
1993
1994         if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1995                 if (nmc->print_output == NMC_PRINT_PRETTY)
1996                         nmc_terminal_erase_line ();
1997                 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
1998                          nm_object_get_path (NM_OBJECT (active)));
1999                 g_object_unref (active);
2000                 quit ();
2001         } else if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
2002                 g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
2003                 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
2004                 g_object_unref (active);
2005                 quit ();
2006         } else if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
2007                 /* activating master connection does not automatically activate any slaves, so their
2008                  * active connection state will not progress beyond ACTIVATING state.
2009                  * Monitor the device instead. */
2010                 const GPtrArray *devices;
2011                 NMDevice *device;
2012
2013                 if (nmc->secret_agent) {
2014                         NMRemoteConnection *connection = nm_active_connection_get_connection (active);
2015
2016                         nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent),
2017                                                        nm_connection_get_path (NM_CONNECTION (connection)));
2018                 }
2019
2020                 devices = nm_active_connection_get_devices (active);
2021                 device = devices->len ? g_ptr_array_index (devices, 0) : NULL;
2022                 if (   device
2023                     && (   NM_IS_DEVICE_BOND (device)
2024                         || NM_IS_DEVICE_TEAM (device)
2025                         || NM_IS_DEVICE_BRIDGE (device))) {
2026                         g_signal_handlers_disconnect_by_func (active, G_CALLBACK (active_connection_state_cb), nmc);
2027                         g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state_cb), nmc);
2028
2029                         device_state_cb (device, NULL, nmc);
2030                 }
2031         }
2032 }
2033
2034 static void
2035 vpn_connection_state_cb (NMVpnConnection *vpn,
2036                          NMVpnConnectionState state,
2037                          NMVpnConnectionStateReason reason,
2038                          gpointer user_data)
2039 {
2040         NmCli *nmc = (NmCli *) user_data;
2041
2042         switch (state) {
2043         case NM_VPN_CONNECTION_STATE_PREPARE:
2044         case NM_VPN_CONNECTION_STATE_NEED_AUTH:
2045         case NM_VPN_CONNECTION_STATE_CONNECT:
2046         case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
2047                 /* no operation */
2048                 break;
2049
2050         case NM_VPN_CONNECTION_STATE_ACTIVATED:
2051                 if (nmc->print_output == NMC_PRINT_PRETTY)
2052                         nmc_terminal_erase_line ();
2053                 g_print (_("VPN connection successfully activated (D-Bus active path: %s)\n"),
2054                          nm_object_get_path (NM_OBJECT (vpn)));
2055                 g_object_unref (vpn);
2056                 quit ();
2057                 break;
2058
2059         case NM_VPN_CONNECTION_STATE_FAILED:
2060         case NM_VPN_CONNECTION_STATE_DISCONNECTED:
2061                 g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."),
2062                                  vpn_connection_state_reason_to_string (reason));
2063                 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
2064                 g_object_unref (vpn);
2065                 quit ();
2066                 break;
2067
2068         default:
2069                 break;
2070         }
2071 }
2072
2073 static gboolean
2074 timeout_cb (gpointer user_data)
2075 {
2076         /* Time expired -> exit nmcli */
2077
2078         NmCli *nmc = (NmCli *) user_data;
2079
2080         g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout);
2081         nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED;
2082         quit ();
2083         return FALSE;
2084 }
2085
2086 static gboolean
2087 progress_cb (gpointer user_data)
2088 {
2089         const char *str = (const char *) user_data;
2090
2091         nmc_terminal_show_progress (str);
2092
2093         return TRUE;
2094 }
2095
2096 static gboolean
2097 progress_device_cb (gpointer user_data)
2098 {
2099         NMDevice *device = (NMDevice *) user_data;
2100
2101         nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : "");
2102
2103         return TRUE;
2104 }
2105
2106 static gboolean
2107 progress_vpn_cb (gpointer user_data)
2108 {
2109         NMVpnConnection *vpn = (NMVpnConnection *) user_data;
2110         const char *str;
2111
2112         str = NM_IS_VPN_CONNECTION (vpn) ?
2113                 vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) :
2114                 "";
2115
2116         nmc_terminal_show_progress (str);
2117
2118         return TRUE;
2119 }
2120
2121 typedef struct {
2122         NmCli *nmc;
2123         NMDevice *device;
2124 } ActivateConnectionInfo;
2125
2126 static void
2127 activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_data)
2128 {
2129         ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
2130         NmCli *nmc = info->nmc;
2131         NMDevice *device = info->device;
2132         NMActiveConnection *active;
2133         NMActiveConnectionState state;
2134         const GPtrArray *ac_devs;
2135         GError *error = NULL;
2136
2137         active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
2138
2139         if (error) {
2140                 g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"),
2141                                  error->message);
2142                 g_error_free (error);
2143                 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
2144                 quit ();
2145         } else {
2146                 state = nm_active_connection_get_state (active);
2147                 if (!device) {
2148                         /* device could be NULL for virtual devices. Fill it here. */
2149                         ac_devs = nm_active_connection_get_devices (active);
2150                         info->device = device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
2151                 }
2152
2153                 if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
2154                         /* User doesn't want to wait or already activated */
2155                         if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
2156                                 if (nmc->print_output == NMC_PRINT_PRETTY)
2157                                         nmc_terminal_erase_line ();
2158                                 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
2159                                          nm_object_get_path (NM_OBJECT (active)));
2160                         }
2161                         g_object_unref (active);
2162                         quit ();
2163                 } else {
2164                         if (NM_IS_VPN_CONNECTION (active)) {
2165                                 /* Monitor VPN state */
2166                                 g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc);
2167
2168                                 /* Start progress indication showing VPN states */
2169                                 if (nmc->print_output == NMC_PRINT_PRETTY) {
2170                                         if (progress_id)
2171                                                 g_source_remove (progress_id);
2172                                         progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active));
2173                                 }
2174                         } else {
2175                                 g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc);
2176                                 active_connection_state_cb (active, NULL, nmc);
2177
2178                                 /* Start progress indication showing device states */
2179                                 if (nmc->print_output == NMC_PRINT_PRETTY) {
2180                                         if (progress_id)
2181                                                 g_source_remove (progress_id);
2182                                         progress_id = g_timeout_add (120, progress_device_cb, device);
2183                                 }
2184                         }
2185
2186                         /* Start timer not to loop forever when signals are not emitted */
2187                         g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);
2188                 }
2189         }
2190         g_free (info);
2191 }
2192
2193 /**
2194  * parse_passwords:
2195  * @passwd_file: file with passwords to parse
2196  * @error: location to store error, or %NULL
2197  *
2198  * Parse passwords given in @passwd_file and insert them into a hash table.
2199  * Example of @passwd_file contents:
2200  *   wifi.psk:tajne heslo
2201  *   802-1x.password:krakonos
2202  *   802-11-wireless-security:leap-password:my leap password
2203  *
2204  * Returns: hash table with parsed passwords, or %NULL on an error
2205  */
2206 static GHashTable *
2207 parse_passwords (const char *passwd_file, GError **error)
2208 {
2209         GHashTable *pwds_hash;
2210         char *contents = NULL;
2211         gsize len = 0;
2212         GError *local_err = NULL;
2213         char **lines, **iter;
2214         char *pwd_spec, *pwd, *prop;
2215         const char *setting;
2216
2217         pwds_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2218
2219         if (!passwd_file)
2220                 return pwds_hash;
2221
2222         /* Read the passwords file */
2223         if (!g_file_get_contents (passwd_file, &contents, &len, &local_err)) {
2224                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2225                              _("failed to read passwd-file '%s': %s"),
2226                              passwd_file, local_err->message);
2227                 g_error_free (local_err);
2228                 g_hash_table_destroy (pwds_hash);
2229                 return NULL;
2230         }
2231
2232         lines = nmc_strsplit_set (contents, "\r\n", -1);
2233         for (iter = lines; *iter; iter++) {
2234                 pwd = strchr (*iter, ':');
2235                 if (!pwd) {
2236                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2237                                      _("missing colon in 'password' entry '%s'"), *iter);
2238                         goto failure;
2239                 }
2240                 *(pwd++) = '\0';
2241
2242                 prop = strchr (*iter, '.');
2243                 if (!prop) {
2244                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2245                                      _("missing dot in 'password' entry '%s'"), *iter);
2246                         goto failure;
2247                 }
2248                 *(prop++) = '\0';
2249
2250                 setting = *iter;
2251                 while (g_ascii_isspace (*setting))
2252                         setting++;
2253                 /* Accept wifi-sec or wifi instead of cumbersome '802-11-wireless-security' */
2254                 if (!strcmp (setting, "wifi-sec") || !strcmp (setting, "wifi"))
2255                         setting = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
2256                 if (nm_setting_lookup_type (setting) == G_TYPE_INVALID) {
2257                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2258                                      _("invalid setting name in 'password' entry '%s'"), setting);
2259                         goto failure;
2260                 }
2261
2262                 pwd_spec = g_strdup_printf ("%s.%s", setting, prop);
2263                 g_hash_table_insert (pwds_hash, pwd_spec, g_strdup (pwd));
2264         }
2265         g_strfreev (lines);
2266         g_free (contents);
2267         return pwds_hash;
2268
2269 failure:
2270         g_strfreev (lines);
2271         g_free (contents);
2272         g_hash_table_destroy (pwds_hash);
2273         return NULL;
2274 }
2275
2276
2277
2278 static gboolean
2279 nmc_activate_connection (NmCli *nmc,
2280                          NMConnection *connection,
2281                          const char *ifname,
2282                          const char *ap,
2283                          const char *nsp,
2284                          const char *pwds,
2285                          GAsyncReadyCallback callback,
2286                          GError **error)
2287 {
2288         ActivateConnectionInfo *info;
2289
2290         GHashTable *pwds_hash;
2291         NMDevice *device = NULL;
2292         const char *spec_object = NULL;
2293         gboolean device_found;
2294         GError *local = NULL;
2295
2296         g_return_val_if_fail (nmc != NULL, FALSE);
2297         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2298
2299         if (connection && (ifname || ap || nsp)) {
2300                 device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &local);
2301
2302                 /* Virtual connection may not have their interfaces created yet */
2303                 if (!device_found && !nm_connection_is_virtual (connection)) {
2304                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION,
2305                                      "%s", local->message);
2306                         g_clear_error (&local);
2307                         return FALSE;
2308                 }
2309                 g_clear_error (&local);
2310         } else if (ifname) {
2311                 device = nm_client_get_device_by_iface (nmc->client, ifname);
2312                 if (!device) {
2313                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND,
2314                                      _("unknown device '%s'."), ifname);
2315                         return FALSE;
2316                 }
2317         } else if (!connection) {
2318                 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND,
2319                                      _("neither a valid connection nor device given"));
2320                 return FALSE;
2321         }
2322
2323         /* Parse passwords given in passwords file */
2324         pwds_hash = parse_passwords (pwds, &local);
2325         if (local) {
2326                 g_propagate_error (error, local);
2327                 return FALSE;
2328         }
2329         if (nmc->pwds_hash)
2330                 g_hash_table_destroy (nmc->pwds_hash);
2331         nmc->pwds_hash = pwds_hash;
2332
2333         /* Create secret agent */
2334         nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-connect");
2335         if (nmc->secret_agent) {
2336                 g_signal_connect (nmc->secret_agent, "request-secrets", G_CALLBACK (nmc_secrets_requested), nmc);
2337                 if (connection) {
2338                         nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent),
2339                                                        nm_object_get_path (NM_OBJECT (connection)));
2340                 }
2341         }
2342
2343         info = g_malloc0 (sizeof (ActivateConnectionInfo));
2344         info->nmc = nmc;
2345         info->device = device;
2346
2347         nm_client_activate_connection_async (nmc->client,
2348                                              connection,
2349                                              device,
2350                                              spec_object,
2351                                              NULL,
2352                                              callback,
2353                                              info);
2354         return TRUE;
2355 }
2356
2357 static NMCResultCode
2358 do_connection_up (NmCli *nmc, int argc, char **argv)
2359 {
2360         NMConnection *connection = NULL;
2361         const char *ifname = NULL;
2362         const char *ap = NULL;
2363         const char *nsp = NULL;
2364         const char *pwds = NULL;
2365         GError *error = NULL;
2366         const char *selector = NULL;
2367         const char *name = NULL;
2368         char *line = NULL;
2369
2370         /*
2371          * Set default timeout for connection activation.
2372          * Activation can take quite a long time, use 90 seconds.
2373          */
2374         if (nmc->timeout == -1)
2375                 nmc->timeout = 90;
2376
2377         if (argc == 0) {
2378                 if (nmc->ask) {
2379                         line = nmc_readline (PROMPT_CONNECTION);
2380                         name = line ? line : "";
2381                 }
2382         } else if (strcmp (*argv, "ifname") != 0) {
2383                 if (   strcmp (*argv, "id") == 0
2384                     || strcmp (*argv, "uuid") == 0
2385                     || strcmp (*argv, "path") == 0) {
2386
2387                         selector = *argv;
2388                         if (next_arg (&argc, &argv) != 0) {
2389                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
2390                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2391                                 goto error;
2392                         }
2393                         name = *argv;
2394                 }
2395                 name = *argv;
2396                 next_arg (&argc, &argv);
2397         }
2398
2399         if (name) {
2400                 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
2401                 if (!connection) {
2402                         g_string_printf (nmc->return_text, _("Error: Connection '%s' does not exist."), name);
2403                         nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
2404                         goto error;
2405                 }
2406         }
2407
2408         while (argc > 0) {
2409                 if (strcmp (*argv, "ifname") == 0) {
2410                         if (next_arg (&argc, &argv) != 0) {
2411                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
2412                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2413                                 goto error;
2414                         }
2415
2416                         ifname = *argv;
2417                 }
2418                 else if (strcmp (*argv, "ap") == 0) {
2419                         if (next_arg (&argc, &argv) != 0) {
2420                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
2421                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2422                                 goto error;
2423                         }
2424
2425                         ap = *argv;
2426                 }
2427                 else if (strcmp (*argv, "passwd-file") == 0) {
2428                         if (next_arg (&argc, &argv) != 0) {
2429                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
2430                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2431                                 goto error;
2432                         }
2433
2434                         pwds = *argv;
2435                 }
2436                 else {
2437                         g_printerr (_("Unknown parameter: %s\n"), *argv);
2438                 }
2439
2440                 argc--;
2441                 argv++;
2442         }
2443
2444         /* Use nowait_flag instead of should_wait because exiting has to be postponed till
2445          * active_connection_state_cb() is called. That gives NM time to check our permissions
2446          * and we can follow activation progress.
2447          */
2448         nmc->nowait_flag = (nmc->timeout == 0);
2449         nmc->should_wait++;
2450
2451         if (!nmc_activate_connection (nmc, connection, ifname, ap, nsp, pwds, activate_connection_cb, &error)) {
2452                 g_string_printf (nmc->return_text, _("Error: %s."),
2453                                  error->message);
2454                 nmc->return_value = error->code;
2455                 g_clear_error (&error);
2456                 nmc->should_wait--;
2457                 goto error;
2458         }
2459
2460         /* Start progress indication */
2461         if (nmc->print_output == NMC_PRINT_PRETTY)
2462                 progress_id = g_timeout_add (120, progress_cb, _("preparing"));
2463
2464 error:
2465         g_free (line);
2466         return nmc->return_value;
2467 }
2468
2469 typedef struct {
2470         NmCli *nmc;
2471         GSList *queue;
2472         guint timeout_id;
2473 } ConnectionCbInfo;
2474
2475 static void connection_cb_info_finish (ConnectionCbInfo *info,
2476                                        gpointer connection);
2477
2478 static void
2479 connection_removed_cb (NMClient *client, NMConnection *connection, ConnectionCbInfo *info)
2480 {
2481         if (!g_slist_find (info->queue, connection))
2482                 return;
2483         g_print (_("Connection '%s' (%s) successfully deleted.\n"),
2484                  nm_connection_get_id (connection),
2485                  nm_connection_get_uuid (connection));
2486         connection_cb_info_finish (info, connection);
2487 }
2488
2489 static void
2490 down_active_connection_state_cb (NMActiveConnection *active,
2491                                  GParamSpec *pspec,
2492                                  ConnectionCbInfo *info)
2493 {
2494         if (nm_active_connection_get_state (active) < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)
2495                 return;
2496
2497         if (info->nmc->print_output == NMC_PRINT_PRETTY)
2498                 nmc_terminal_erase_line ();
2499         g_print (_("Connection '%s' successfully deactivated (D-Bus active path: %s)\n"),
2500                  nm_active_connection_get_id (active), nm_object_get_path (NM_OBJECT (active)));
2501
2502         g_signal_handlers_disconnect_by_func (G_OBJECT (active),
2503                                               down_active_connection_state_cb,
2504                                               info);
2505         connection_cb_info_finish (info, active);
2506 }
2507
2508 static gboolean
2509 connection_op_timeout_cb (gpointer user_data)
2510 {
2511         ConnectionCbInfo *info = user_data;
2512
2513         timeout_cb (info->nmc);
2514         connection_cb_info_finish (info, NULL);
2515         return G_SOURCE_REMOVE;
2516 }
2517
2518 static void
2519 destroy_queue_element (gpointer data)
2520 {
2521         g_signal_handlers_disconnect_matched (data, G_SIGNAL_MATCH_FUNC, 0, 0, 0,
2522                                               down_active_connection_state_cb, NULL);
2523         g_object_unref (data);
2524 }
2525
2526 static void
2527 connection_cb_info_finish (ConnectionCbInfo *info, gpointer connection)
2528 {
2529         if (connection) {
2530                 info->queue = g_slist_remove (info->queue, connection);
2531                 g_object_unref (G_OBJECT (connection));
2532         } else {
2533                 g_slist_free_full (info->queue, destroy_queue_element);
2534                 info->queue = NULL;
2535         }
2536
2537         if (info->queue)
2538                 return;
2539
2540         if (info->timeout_id)
2541                 g_source_remove (info->timeout_id);
2542         g_signal_handlers_disconnect_by_func (info->nmc->client, connection_removed_cb, info);
2543         g_slice_free (ConnectionCbInfo, info);
2544         quit ();
2545 }
2546
2547 static NMCResultCode
2548 do_connection_down (NmCli *nmc, int argc, char **argv)
2549 {
2550         NMActiveConnection *active;
2551         ConnectionCbInfo *info = NULL;
2552         const GPtrArray *active_cons;
2553         GSList *queue = NULL, *iter;
2554         char **arg_arr = NULL;
2555         char **arg_ptr = argv;
2556         int arg_num = argc;
2557         int idx = 0;
2558
2559         if (nmc->timeout == -1)
2560                 nmc->timeout = 10;
2561
2562         if (argc == 0) {
2563                 if (nmc->ask) {
2564                         char *line = nmc_readline (PROMPT_ACTIVE_CONNECTIONS);
2565                         nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
2566                         g_free (line);
2567                         arg_ptr = arg_arr;
2568                 }
2569                 if (arg_num == 0) {
2570                         g_string_printf (nmc->return_text, _("Error: No connection specified."));
2571                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2572                         goto error;
2573                 }
2574         }
2575
2576         /* Get active connections */
2577         active_cons = nm_client_get_active_connections (nmc->client);
2578         while (arg_num > 0) {
2579                 const char *selector = NULL;
2580
2581                 if (   strcmp (*arg_ptr, "id") == 0
2582                     || strcmp (*arg_ptr, "uuid") == 0
2583                     || strcmp (*arg_ptr, "path") == 0
2584                     || strcmp (*arg_ptr, "apath") == 0) {
2585
2586                         selector = *arg_ptr;
2587                         if (next_arg (&arg_num, &arg_ptr) != 0) {
2588                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
2589                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2590                                 goto error;
2591                         }
2592                 }
2593
2594                 active = find_active_connection (active_cons, nmc->connections, selector, *arg_ptr, &idx);
2595                 if (active) {
2596                         /* Check if the connection is unique. */
2597                         /* Calling down for the same connection repeatedly would result in
2598                          * NM responding for the last D-Bus call only and we would stall. */
2599                         if (!g_slist_find (queue, active))
2600                                 queue = g_slist_prepend (queue, g_object_ref (active));
2601                 } else {
2602                         g_printerr (_("Error: '%s' is not an active connection.\n"), *arg_ptr);
2603                         g_string_printf (nmc->return_text, _("Error: not all active connections found."));
2604                         nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
2605                 }
2606
2607                 if (idx == 0)
2608                         next_arg (&arg_num, &arg_ptr);
2609         }
2610
2611         if (!queue) {
2612                 g_string_printf (nmc->return_text, _("Error: no active connection provided."));
2613                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
2614                 goto error;
2615         }
2616         queue = g_slist_reverse (queue);
2617
2618         if (nmc->timeout > 0) {
2619                 nmc->should_wait++;
2620
2621                 info = g_slice_new0 (ConnectionCbInfo);
2622                 info->nmc = nmc;
2623                 info->queue = queue;
2624                 info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
2625         }
2626
2627         for (iter = queue; iter; iter = g_slist_next (iter)) {
2628                 active = iter->data;
2629
2630                 if (info)
2631                         g_signal_connect (active,
2632                                           "notify::" NM_ACTIVE_CONNECTION_STATE,
2633                                           G_CALLBACK (down_active_connection_state_cb),
2634                                           info);
2635
2636                 /* Now deactivate the connection */
2637                 nm_client_deactivate_connection (nmc->client, active, NULL, NULL);
2638         }
2639
2640 error:
2641         g_strfreev (arg_arr);
2642         return nmc->return_value;
2643 }
2644
2645 /*----------------------------------------------------------------------------*/
2646
2647 typedef struct NameItem {
2648         const char *name;
2649         const char *alias;
2650         const struct NameItem *settings;
2651         gboolean mandatory;
2652 } NameItem;
2653
2654 static const NameItem nmc_generic_settings [] = {
2655         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2656         { NULL, NULL, NULL, FALSE }
2657 };
2658
2659 static const NameItem nmc_ethernet_settings [] = {
2660         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2661         { NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, TRUE  },
2662         { NM_SETTING_802_1X_SETTING_NAME,     NULL,       NULL, FALSE },
2663         { NM_SETTING_DCB_SETTING_NAME,        NULL,       NULL, FALSE },
2664         { NULL, NULL, NULL, FALSE }
2665 };
2666
2667 static const NameItem nmc_infiniband_settings [] = {
2668         { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE  },
2669         { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL, TRUE  },
2670         { NULL, NULL, NULL, FALSE }
2671 };
2672
2673 static const NameItem nmc_wifi_settings [] = {
2674         { NM_SETTING_CONNECTION_SETTING_NAME,        NULL,       NULL, TRUE  },
2675         { NM_SETTING_WIRELESS_SETTING_NAME,          "wifi",     NULL, TRUE  },
2676         { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-sec", NULL, FALSE },
2677         { NM_SETTING_802_1X_SETTING_NAME,            NULL,       NULL, FALSE },
2678         { NULL, NULL, NULL, FALSE }
2679 };
2680
2681 static const NameItem nmc_wimax_settings [] = {
2682         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
2683         { NM_SETTING_WIMAX_SETTING_NAME,      NULL,   NULL, TRUE  },
2684         { NULL, NULL, NULL, FALSE }
2685 };
2686
2687 static const NameItem nmc_gsm_settings [] = {
2688         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2689         { NM_SETTING_GSM_SETTING_NAME,        NULL,       NULL, TRUE  },
2690         { NM_SETTING_SERIAL_SETTING_NAME,     NULL,       NULL, FALSE },
2691         { NM_SETTING_PPP_SETTING_NAME,        NULL,       NULL, FALSE },
2692         { NULL, NULL, NULL, FALSE }
2693 };
2694
2695 static const NameItem nmc_cdma_settings [] = {
2696         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2697         { NM_SETTING_CDMA_SETTING_NAME,       NULL,       NULL, TRUE  },
2698         { NM_SETTING_SERIAL_SETTING_NAME,     NULL,       NULL, FALSE },
2699         { NM_SETTING_PPP_SETTING_NAME,        NULL,       NULL, FALSE },
2700         { NULL, NULL, NULL, FALSE }
2701 };
2702
2703 static const NameItem nmc_bluetooth_settings [] = {
2704         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
2705         { NM_SETTING_BLUETOOTH_SETTING_NAME,  NULL,   NULL, TRUE  },
2706         { NULL, NULL, NULL, FALSE }
2707 };
2708
2709 static const NameItem nmc_adsl_settings [] = {
2710         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
2711         { NM_SETTING_ADSL_SETTING_NAME,       NULL,   NULL, TRUE  },
2712         { NULL, NULL, NULL, FALSE }
2713 };
2714
2715 /* PPPoE is a base connection type from historical reasons.
2716  * See libnm-core/nm-setting.c:_nm_setting_is_base_type()
2717  */
2718 static const NameItem nmc_pppoe_settings [] = {
2719         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2720         { NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, TRUE  },
2721         { NM_SETTING_PPPOE_SETTING_NAME,      NULL,       NULL, TRUE  },
2722         { NM_SETTING_PPP_SETTING_NAME,        NULL,       NULL, FALSE },
2723         { NM_SETTING_802_1X_SETTING_NAME,     NULL,       NULL, FALSE },
2724         { NULL, NULL, NULL, FALSE }
2725 };
2726
2727 static const NameItem nmc_olpc_mesh_settings [] = {
2728         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,        NULL, TRUE  },
2729         { NM_SETTING_OLPC_MESH_SETTING_NAME,  "olpc-mesh", NULL, TRUE  },
2730         { NULL, NULL, NULL, FALSE }
2731 };
2732
2733 static const NameItem nmc_vpn_settings [] = {
2734         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
2735         { NM_SETTING_VPN_SETTING_NAME,        NULL,   NULL, TRUE  },
2736         { NULL, NULL, NULL, FALSE }
2737 };
2738
2739 static const NameItem nmc_vlan_settings [] = {
2740         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2741         { NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
2742         { NM_SETTING_VLAN_SETTING_NAME,       NULL,       NULL, TRUE  },
2743         { NULL, NULL, NULL, FALSE }
2744 };
2745
2746 static const NameItem nmc_bond_settings [] = {
2747         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2748         { NM_SETTING_BOND_SETTING_NAME,       NULL,       NULL, TRUE  },
2749         { NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
2750         { NULL, NULL, NULL, FALSE }
2751 };
2752
2753 static const NameItem nmc_team_settings [] = {
2754         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2755         { NM_SETTING_TEAM_SETTING_NAME,       NULL,       NULL, TRUE  },
2756         { NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
2757         { NULL, NULL, NULL, FALSE }
2758 };
2759
2760 static const NameItem nmc_bridge_settings [] = {
2761         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2762         { NM_SETTING_BRIDGE_SETTING_NAME,     NULL,       NULL, TRUE  },
2763         { NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
2764         { NULL, NULL, NULL, FALSE }
2765 };
2766
2767 static const NameItem nmc_bond_slave_settings [] = {
2768         { NULL, NULL, NULL, FALSE }
2769 };
2770
2771 static const NameItem nmc_team_slave_settings [] = {
2772         { NM_SETTING_TEAM_PORT_SETTING_NAME,  NULL,       NULL, TRUE  },
2773         { NULL, NULL, NULL, FALSE }
2774 };
2775
2776 static const NameItem nmc_bridge_slave_settings [] = {
2777         { NM_SETTING_BRIDGE_PORT_SETTING_NAME, NULL,       NULL, TRUE  },
2778         { NULL, NULL, NULL, FALSE }
2779 };
2780
2781 static const NameItem nmc_no_slave_settings [] = {
2782         { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
2783         { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
2784         { NULL, NULL, NULL, FALSE }
2785 };
2786
2787 static const NameItem nmc_tun_settings [] = {
2788         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2789         { NM_SETTING_TUN_SETTING_NAME,        NULL,       NULL, TRUE  },
2790         { NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
2791         { NULL, NULL, NULL, FALSE }
2792 };
2793
2794 static const NameItem nmc_ip_tunnel_settings [] = {
2795         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2796         { NM_SETTING_IP_TUNNEL_SETTING_NAME,  NULL,       NULL, TRUE  },
2797         { NULL, NULL, NULL, FALSE }
2798 };
2799
2800 static const NameItem nmc_macvlan_settings [] = {
2801         { NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
2802         { NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
2803         { NM_SETTING_MACVLAN_SETTING_NAME,    NULL,       NULL, TRUE  },
2804         { NULL, NULL, NULL, FALSE }
2805 };
2806
2807 static const NameItem nmc_vxlan_settings [] = {
2808         { NM_SETTING_CONNECTION_SETTING_NAME,  NULL,       NULL, TRUE  },
2809         { NM_SETTING_VXLAN_SETTING_NAME,       NULL,       NULL, TRUE  },
2810         { NM_SETTING_WIRED_SETTING_NAME,       "ethernet", NULL, FALSE },
2811         { NULL, NULL, NULL, FALSE }
2812 };
2813
2814 /* Available connection types */
2815 static const NameItem nmc_valid_connection_types[] = {
2816         { NM_SETTING_GENERIC_SETTING_NAME,    NULL,        nmc_generic_settings      },
2817         { NM_SETTING_WIRED_SETTING_NAME,      "ethernet",  nmc_ethernet_settings     },
2818         { NM_SETTING_PPPOE_SETTING_NAME,      NULL,        nmc_pppoe_settings        },
2819         { NM_SETTING_WIRELESS_SETTING_NAME,   "wifi",      nmc_wifi_settings         },
2820         { NM_SETTING_WIMAX_SETTING_NAME,      NULL,        nmc_wimax_settings        },
2821         { NM_SETTING_GSM_SETTING_NAME,        NULL,        nmc_gsm_settings          },
2822         { NM_SETTING_CDMA_SETTING_NAME,       NULL,        nmc_cdma_settings         },
2823         { NM_SETTING_INFINIBAND_SETTING_NAME, NULL,        nmc_infiniband_settings   },
2824         { NM_SETTING_ADSL_SETTING_NAME,       NULL,        nmc_adsl_settings         },
2825         { NM_SETTING_BLUETOOTH_SETTING_NAME,  NULL,        nmc_bluetooth_settings    },
2826         { NM_SETTING_VPN_SETTING_NAME,        NULL,        nmc_vpn_settings          },
2827         { NM_SETTING_OLPC_MESH_SETTING_NAME,  "olpc-mesh", nmc_olpc_mesh_settings    },
2828         { NM_SETTING_VLAN_SETTING_NAME,       NULL,        nmc_vlan_settings         },
2829         { NM_SETTING_BOND_SETTING_NAME,       NULL,        nmc_bond_settings         },
2830         { NM_SETTING_TEAM_SETTING_NAME,       NULL,        nmc_team_settings         },
2831         { NM_SETTING_BRIDGE_SETTING_NAME,     NULL,        nmc_bridge_settings       },
2832         { "bond-slave",                       NULL,        nmc_bond_slave_settings   },
2833         { "team-slave",                       NULL,        nmc_team_slave_settings   },
2834         { "bridge-slave",                     NULL,        nmc_bridge_slave_settings },
2835         { "no-slave",                         NULL,        nmc_no_slave_settings     },
2836         { NM_SETTING_TUN_SETTING_NAME,        NULL,        nmc_tun_settings          },
2837         { NM_SETTING_IP_TUNNEL_SETTING_NAME,  NULL,        nmc_ip_tunnel_settings    },
2838         { NM_SETTING_MACVLAN_SETTING_NAME,    NULL,        nmc_macvlan_settings      },
2839         { NM_SETTING_VXLAN_SETTING_NAME,      NULL,        nmc_vxlan_settings        },
2840         { NULL, NULL, NULL }
2841 };
2842
2843 /*
2844  * Return an alias for the 'name' if exists, else return the 'name'.
2845  * The returned string must not be freed.
2846  */
2847 static const char *
2848 get_name_alias (const char *name, const NameItem array[])
2849 {
2850         const NameItem *iter = &array[0];
2851
2852         if (!name)
2853                 return NULL;
2854
2855         while (iter && iter->name) {
2856                 if (!strcmp (name, iter->name)) {
2857                         if (iter->alias)
2858                                 return iter->alias;
2859                         else
2860                                 return iter->name;
2861                 }
2862                 iter++;
2863         }
2864         return name;
2865 }
2866
2867 /*
2868  * Construct a string with names and aliases from the arrays formatted as:
2869  * "name (alias), name, name (alias), name, name"
2870  *
2871  * Returns: string; the caller is responsible for freeing it.
2872  */
2873 static char *
2874 get_valid_options_string (const NameItem *array, const NameItem *array_slv)
2875 {
2876         const NameItem *iter = array;
2877         GString *str;
2878         int i;
2879
2880         str = g_string_sized_new (150);
2881
2882         for (i = 0; i < 2; i++, iter = array_slv) {
2883                 while (iter && iter->name) {
2884                         if (str->len)
2885                                 g_string_append (str, ", ");
2886                         if (iter->alias)
2887                                 g_string_append_printf (str, "%s (%s)", iter->name, iter->alias);
2888                         else
2889                                 g_string_append (str, iter->name);
2890                         iter++;
2891                 }
2892         }
2893         return g_string_free (str, FALSE);
2894 }
2895
2896 static const NameItem *
2897 get_valid_settings_array (const char *con_type)
2898 {
2899         guint i, num;
2900
2901         if (!con_type)
2902                 return NULL;
2903
2904         num = G_N_ELEMENTS (nmc_valid_connection_types);
2905         for (i = 0; i < num; i++) {
2906                 if (nm_streq0 (con_type, nmc_valid_connection_types[i].name))
2907                         return nmc_valid_connection_types[i].settings;
2908         }
2909         return NULL;
2910 }
2911
2912 /*
2913  * Check if 'val' is valid string in either array->name or array->alias for
2914  * both array parameters (array & array_slv).
2915  * It accepts shorter string provided they are not ambiguous.
2916  * 'val' == NULL doesn't hurt.
2917  *
2918  * Returns: pointer to array->name string or NULL on failure.
2919  * The returned string must not be freed.
2920  */
2921 static const char *
2922 check_valid_name (const char *val, const NameItem *array, const NameItem *array_slv, GError **error)
2923 {
2924         const NameItem *iter;
2925         gs_unref_ptrarray GPtrArray *tmp_arr = NULL;
2926         const char *str;
2927         GError *tmp_err = NULL;
2928         int i;
2929
2930         g_return_val_if_fail (val, NULL);
2931         g_return_val_if_fail (array, NULL);
2932
2933         /* Create a temporary array that can be used in nmc_string_is_valid() */
2934         tmp_arr = g_ptr_array_sized_new (32);
2935         iter = array;
2936         for (i = 0; i < 2; i++, iter = array_slv) {
2937                 while (iter && iter->name) {
2938                         g_ptr_array_add (tmp_arr, (gpointer) iter->name);
2939                         if (iter->alias)
2940                                 g_ptr_array_add (tmp_arr, (gpointer) iter->alias);
2941                         iter++;
2942                 }
2943         }
2944         g_ptr_array_add (tmp_arr, (gpointer) NULL);
2945
2946         /* Check string validity */
2947         str = nmc_string_is_valid (val, (const char **) tmp_arr->pdata, &tmp_err);
2948         if (!str) {
2949                 if (tmp_err->code == 1)
2950                         g_propagate_error (error, tmp_err);
2951                 else {
2952                         /* We want to handle aliases, so construct own error message */
2953                         char *err_str = get_valid_options_string (array, array_slv);
2954
2955                         g_set_error (error, 1, 0, _("'%s' not among [%s]"),
2956                                      val, err_str);
2957                         g_free (err_str);
2958                         g_clear_error (&tmp_err);
2959                 }
2960                 return NULL;
2961         }
2962
2963         /* Return a pointer to the found string in passed 'array' */
2964         iter = array;
2965         for (i = 0; i < 2; i++, iter = array_slv) {
2966                 while (iter && iter->name) {
2967                         if (   nm_streq (iter->name, str)
2968                             || nm_streq0 (iter->alias, str)) {
2969                                 return iter->name;
2970                         }
2971                         iter++;
2972                 }
2973         }
2974
2975         /* We should not really come here */
2976         g_set_error (error, 1, 0, _("Unknown error"));
2977         return NULL;
2978 }
2979
2980 static gboolean
2981 is_setting_mandatory (NMConnection *connection, NMSetting *setting)
2982 {
2983         NMSettingConnection *s_con;
2984         const char *c_type;
2985         const NameItem *item;
2986         const char *name;
2987         const char *s_type;
2988         char *slv_type;
2989
2990         s_con = nm_connection_get_setting_connection (connection);
2991         g_assert (s_con);
2992         c_type = nm_setting_connection_get_connection_type (s_con);
2993
2994         name = nm_setting_get_name (setting);
2995
2996         item = get_valid_settings_array (c_type);
2997         while (item && item->name) {
2998                 if (!strcmp (name, item->name))
2999                         return item->mandatory;
3000                 item++;
3001         }
3002
3003         /* Let's give a try to parameters related to slave type */
3004         s_type = nm_setting_connection_get_slave_type (s_con);
3005         slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
3006         item = get_valid_settings_array (slv_type);
3007         g_free (slv_type);
3008         while (item && item->name) {
3009                 if (!strcmp (name, item->name))
3010                         return item->mandatory;
3011                 item++;
3012         }
3013
3014         return FALSE;
3015 }
3016
3017 /*----------------------------------------------------------------------------*/
3018
3019 static gboolean
3020 check_mac (const char *mac,
3021            int type,
3022            const char *keyword,
3023            GError **error)
3024 {
3025         g_return_val_if_fail (type == ARPHRD_ETHER || type == ARPHRD_INFINIBAND, FALSE);
3026
3027         if (!mac)
3028                 return TRUE;
3029
3030         if (!nm_utils_hwaddr_valid (mac, nm_utils_hwaddr_len (type))) {
3031                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3032                              _("Error: '%s': '%s' is not a valid %s MAC address."),
3033                              keyword, mac, type == ARPHRD_INFINIBAND ? _("InfiniBand") : _("Ethernet"));
3034                 return FALSE;
3035         }
3036
3037         return TRUE;
3038 }
3039
3040 static gboolean
3041 check_and_convert_mtu (const char *mtu, guint32 *mtu_int, GError **error)
3042 {
3043         unsigned long local_mtu_int;
3044
3045         if (mtu_int)
3046                 *mtu_int = 0;
3047
3048         if (!mtu)
3049                 return TRUE;
3050
3051         if (!nmc_string_to_uint (mtu, TRUE, 0, G_MAXUINT32, &local_mtu_int)) {
3052                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3053                              _("Error: 'mtu': '%s' is not a valid MTU."), mtu);
3054                 return FALSE;
3055         }
3056         if (mtu_int)
3057                 *mtu_int = (guint32) local_mtu_int;
3058         return TRUE;
3059 }
3060
3061 static gboolean
3062 check_infiniband_parent (const char *parent, GError **error)
3063 {
3064         if (!parent)
3065                 return TRUE;
3066
3067         if (!nm_utils_iface_valid_name (parent)) {
3068                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3069                              _("Error: 'parent': '%s' is not a valid interface name."), parent);
3070                 return FALSE;
3071         }
3072         return TRUE;
3073 }
3074
3075
3076 static gboolean
3077 check_infiniband_p_key (const char *p_key, guint32 *p_key_int, GError **error)
3078 {
3079         unsigned long local_p_key_int;
3080         gboolean p_key_valid = FALSE;
3081         if (!p_key)
3082                 return TRUE;
3083
3084         if (!strncmp (p_key, "0x", 2))
3085                 p_key_valid = nmc_string_to_uint_base (p_key + 2, 16, TRUE, 0, G_MAXUINT16, &local_p_key_int);
3086         else
3087                 p_key_valid = nmc_string_to_uint (p_key, TRUE, 0, G_MAXUINT16, &local_p_key_int);
3088         if (!p_key_valid) {
3089                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3090                              _("Error: 'p-key': '%s' is not a valid InfiniBand P_KEY."), p_key);
3091                 return FALSE;
3092         }
3093         if (p_key_int)
3094                 *p_key_int = (guint32) local_p_key_int;
3095         return TRUE;
3096 }
3097
3098 static gboolean
3099 check_user_group_id (const char *id, GError **error)
3100 {
3101         unsigned long int value;
3102
3103         if (!nmc_string_to_uint (id, FALSE, 0, 0, &value)) {
3104                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3105                              _("Error: '%s' is not a valid UID/GID."), id);
3106                 return FALSE;
3107         }
3108
3109         return TRUE;
3110 }
3111
3112 /**
3113  * check_valid_enumeration:
3114  * @str: string to check against string array @strings
3115  * @strings: string array to check @str againt
3116  * @what: what parameter @str belongs to (used in error message)
3117  * @what_desc: longer description of @what parameter (used in error message)
3118  * @error: location to store an error, or %NULL
3119  *
3120  * Check whether @str is one of the string of @strings array. It accepts
3121  * shortcuts and normalizes them (@str argument is modified on success).
3122  *
3123  * Returns: %TRUE on success, %FALSE on failure
3124  */
3125 static gboolean
3126 check_valid_enumeration (char **str,
3127                          const char *strings[],
3128                          const char *what,
3129                          const char *what_desc,
3130                          GError **error)
3131 {
3132         char *tmp;
3133         const char *checked_str;
3134
3135         if (!str || !*str)
3136                 return TRUE;
3137
3138         tmp = g_strstrip (g_strdup (*str));
3139         checked_str = nmc_string_is_valid (tmp, strings, NULL);
3140         g_free (tmp);
3141         if (checked_str) {
3142                 g_free (*str);
3143                 *str = g_strdup (checked_str);
3144         } else {
3145                 char *options;
3146
3147                 options = nmc_util_strv_for_display (strings, TRUE);
3148                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3149                              _("Error: '%s': '%s' is not a valid %s %s."),
3150                              what, *str, what_desc, options);
3151                 g_free (options);
3152         }
3153         return !!checked_str;
3154 }
3155
3156 /* Checks Wi-Fi mode. */
3157 static gboolean
3158 check_wifi_mode (char **mode, GError **error)
3159 {
3160         const char *modes[] = { "infrastructure", "ap", "adhoc", NULL };
3161
3162         return check_valid_enumeration (mode, modes, "mode", _("Wi-Fi mode"), error);
3163 }
3164
3165 /* Checks InfiniBand mode. */
3166 static gboolean
3167 check_infiniband_mode (char **mode, GError **error)
3168 {
3169         const char *modes[] = { "datagram", "connected", NULL };
3170
3171         return check_valid_enumeration (mode, modes, "mode", _("InfiniBand transport mode"), error);
3172 }
3173
3174 /* Checks ADSL protocol */
3175 static gboolean
3176 check_adsl_protocol (char **protocol, GError **error)
3177 {
3178         const char *protos[] = { NM_SETTING_ADSL_PROTOCOL_PPPOA,
3179                                  NM_SETTING_ADSL_PROTOCOL_PPPOE,
3180                                  NM_SETTING_ADSL_PROTOCOL_IPOATM,
3181                                  NULL };
3182
3183         return check_valid_enumeration (protocol, protos, "protocol", _("ADSL protocol"), error);
3184 }
3185
3186 /* Checks ADSL encapsulation */
3187 static gboolean
3188 check_adsl_encapsulation (char **encapsulation, GError **error)
3189 {
3190         const char *modes[] = { NM_SETTING_ADSL_ENCAPSULATION_VCMUX,
3191                                 NM_SETTING_ADSL_ENCAPSULATION_LLC,
3192                                 NULL };
3193
3194         return check_valid_enumeration (encapsulation, modes, "encapsulation", _("ADSL encapsulation"), error);
3195 }
3196
3197 /* Checks TUN mode. */
3198 static gboolean
3199 check_tun_mode (char **mode, GError **error)
3200 {
3201         const char *modes[] = { "tun", "tap", NULL };
3202
3203         return check_valid_enumeration (mode, modes, "mode", _("TUN device mode"), error);
3204 }
3205
3206 static gboolean
3207 check_and_convert_vlan_flags (const char *flags, guint32 *flags_int, GError **error)
3208 {
3209         unsigned long local_flags_int;
3210
3211         if (!flags)
3212                 return TRUE;
3213
3214         if (!nmc_string_to_uint (flags, TRUE, 0, 7, &local_flags_int)) {
3215                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3216                              _("Error: 'flags': '%s' is not valid; use <0-7>."), flags);
3217                 return FALSE;
3218         }
3219         if (flags_int)
3220                 *flags_int = (guint32) local_flags_int;
3221         return TRUE;
3222 }
3223
3224 static gboolean
3225 check_and_convert_vlan_prio_maps (const char *prio_map,
3226                                   NMVlanPriorityMap type,
3227                                   char ***prio_map_arr,
3228                                   GError **error)
3229 {
3230         char **local_prio_map_arr;
3231         GError *local_err = NULL;
3232
3233         if (!prio_map)
3234                 return TRUE;
3235
3236         if (!(local_prio_map_arr = nmc_vlan_parse_priority_maps (prio_map, type, &local_err))) {
3237                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3238                              _("Error: '%s': '%s' is not valid; %s "),
3239                              type == NM_VLAN_INGRESS_MAP ? "ingress" : "egress",
3240                              prio_map, local_err->message);
3241                 return FALSE;
3242         }
3243
3244         if (prio_map_arr)
3245                 *prio_map_arr = local_prio_map_arr;
3246         return TRUE;
3247 }
3248
3249 static gboolean
3250 add_ip4_address_to_connection (NMIPAddress *ip4addr, NMConnection *connection)
3251 {
3252         NMSettingIPConfig *s_ip4;
3253         gboolean ret;
3254
3255         if (!ip4addr)
3256                 return TRUE;
3257
3258         s_ip4 = nm_connection_get_setting_ip4_config (connection);
3259         if (!s_ip4) {
3260                 s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
3261                 nm_connection_add_setting (connection, NM_SETTING (s_ip4));
3262                 g_object_set (s_ip4,
3263                               NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
3264                               NULL);
3265         }
3266         ret = nm_setting_ip_config_add_address (s_ip4, ip4addr);
3267         nm_ip_address_unref (ip4addr);
3268
3269         return ret;
3270 }
3271
3272 static gboolean
3273 add_ip6_address_to_connection (NMIPAddress *ip6addr, NMConnection *connection)
3274 {
3275         NMSettingIPConfig *s_ip6;
3276         gboolean ret;
3277
3278         if (!ip6addr)
3279                 return TRUE;
3280
3281         s_ip6 = nm_connection_get_setting_ip6_config (connection);
3282         if (!s_ip6) {
3283                 s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
3284                 nm_connection_add_setting (connection, NM_SETTING (s_ip6));
3285                 g_object_set (s_ip6,
3286                               NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
3287                               NULL);
3288         }
3289         ret = nm_setting_ip_config_add_address (s_ip6, ip6addr);
3290         nm_ip_address_unref (ip6addr);
3291
3292         return ret;
3293 }
3294
3295 static char *
3296 unique_master_iface_ifname (const GPtrArray *connections,
3297                             const char *try_name)
3298 {
3299         NMConnection *connection;
3300         char *new_name;
3301         unsigned int num = 1;
3302         int i = 0;
3303         const char *ifname = NULL;
3304
3305         new_name = g_strdup (try_name);
3306         while (i < connections->len) {
3307                 connection = NM_CONNECTION (connections->pdata[i]);
3308                 ifname = nm_connection_get_interface_name (connection);
3309                 if (g_strcmp0 (new_name, ifname) == 0) {
3310                         g_free (new_name);
3311                         new_name = g_strdup_printf ("%s%d", try_name, num++);
3312                         i = 0;
3313                 } else
3314                         i++;
3315         }
3316         return new_name;
3317 }
3318
3319 static const char *
3320 _strip_master_prefix (const char *master, const char *(**func)(NMConnection *))
3321 {
3322         if (!master)
3323                 return NULL;
3324
3325         if (g_str_has_prefix (master, "ifname/")) {
3326                 master = master + strlen ("ifname/");
3327                 if (func)
3328                         *func = nm_connection_get_interface_name;
3329         } else if (g_str_has_prefix (master, "uuid/")) {
3330                 master = master + strlen ("uuid/");
3331                 if (func)
3332                         *func = nm_connection_get_uuid;
3333         } else if (g_str_has_prefix (master, "id/")) {
3334                 master = master + strlen ("id/");
3335                 if (func)
3336                          *func = nm_connection_get_id;
3337         }
3338         return master;
3339 }
3340
3341 /* normalized_master_for_slave:
3342  * @connections: list af all connections
3343  * @master: UUID, ifname or ID of the master connection
3344  * @type: virtual connection type (bond, team, bridge, ...) or %NULL
3345  * @out_type: type of the connection that matched
3346  *
3347  * Check whether master is a valid interface name, UUID or ID of some connection,
3348  * possibly of a specified @type.
3349  * First UUID and ifname are checked. If they don't match, ID is checked
3350  * and replaced by UUID on a match.
3351  *
3352  * Returns: identifier of master connection if found, %NULL otherwise
3353  */
3354 static const char *
3355 normalized_master_for_slave (const GPtrArray *connections,
3356                              const char *master,
3357                              const char *type,
3358                              const char **out_type)
3359 {
3360         NMConnection *connection;
3361         NMSettingConnection *s_con;
3362         const char *con_type = NULL, *id, *uuid, *ifname;
3363         int i;
3364         const char *found_by_id = NULL;
3365         const char *out_type_by_id = NULL;
3366         const char *out_master = NULL;
3367         const char *(*func) (NMConnection *) = NULL;
3368
3369         if (!master)
3370                 return NULL;
3371
3372         master = _strip_master_prefix (master, &func);
3373         for (i = 0; i < connections->len; i++) {
3374                 connection = NM_CONNECTION (connections->pdata[i]);
3375                 s_con = nm_connection_get_setting_connection (connection);
3376                 g_assert (s_con);
3377                 con_type = nm_setting_connection_get_connection_type (s_con);
3378                 if (type && g_strcmp0 (con_type, type) != 0)
3379                         continue;
3380                 if (func) {
3381                         /* There was a prefix; only compare to that type. */
3382                         if (g_strcmp0 (master, func (connection)) == 0) {
3383                                 if (out_type)
3384                                         *out_type = con_type;
3385                                 if (func == nm_connection_get_id)
3386                                         out_master = nm_connection_get_uuid (connection);
3387                                 else
3388                                         out_master = master;
3389                                 break;
3390                         }
3391                 } else {
3392                         id = nm_connection_get_id (connection);
3393                         uuid = nm_connection_get_uuid (connection);
3394                         ifname = nm_connection_get_interface_name (connection);
3395                         if (   g_strcmp0 (master, uuid) == 0
3396                             || g_strcmp0 (master, ifname) == 0) {
3397                                 out_master = master;
3398                                 if (out_type)
3399                                         *out_type = con_type;
3400                                 break;
3401                         }
3402                         if (!found_by_id && g_strcmp0 (master, id) == 0) {
3403                                 out_type_by_id = con_type;
3404                                 found_by_id = uuid;
3405                         }
3406                 }
3407         }
3408
3409         if (!out_master) {
3410                 out_master = found_by_id;
3411                 if (out_type)
3412                         *out_type = out_type_by_id;
3413         }
3414
3415         if (!out_master) {
3416                 g_print (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master);
3417                 out_master = master;
3418                 if (out_type)
3419                         *out_type = type;
3420         }
3421
3422         return out_master;
3423 }
3424
3425 static gboolean
3426 bridge_prop_string_to_uint (const char *str,
3427                             const char *nmc_arg,
3428                             GType bridge_type,
3429                             const char *propname,
3430                             unsigned long *out_val,
3431                             GError **error)
3432 {
3433         GParamSpecUInt *pspec;
3434
3435         pspec = (GParamSpecUInt *) g_object_class_find_property (g_type_class_peek (bridge_type),
3436                                                                  propname);
3437         g_assert (G_IS_PARAM_SPEC_UINT (pspec));
3438
3439         if (!nmc_string_to_uint (str, TRUE, pspec->minimum, pspec->maximum, out_val)) {
3440                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3441                              _("Error: '%s': '%s' is not valid; use <%u-%u>."),
3442                              nmc_arg, str, pspec->minimum, pspec->maximum);
3443                 return FALSE;
3444         }
3445         return TRUE;
3446 }
3447
3448 #define WORD_YES "yes"
3449 #define WORD_NO  "no"
3450 #define WORD_LOC_YES _("yes")
3451 #define WORD_LOC_NO  _("no")
3452 static const char *
3453 prompt_yes_no (gboolean default_yes, char *delim)
3454 {
3455         static char prompt[128] = { 0 };
3456
3457         if (!delim)
3458                 delim = "";
3459
3460         snprintf (prompt, sizeof (prompt), "(%s/%s) [%s]%s ",
3461                   WORD_LOC_YES, WORD_LOC_NO,
3462                   default_yes ? WORD_LOC_YES : WORD_LOC_NO, delim);
3463
3464         return prompt;
3465 }
3466
3467 static gboolean
3468 normalize_yes_no (char **yes_no)
3469 {
3470         char *tmp;
3471         const char *checked_yes_no;
3472         const char *strv[] = { WORD_LOC_YES, WORD_LOC_NO, NULL };
3473
3474         if (!yes_no || !*yes_no)
3475                 return FALSE;
3476
3477         tmp = g_strstrip (g_strdup (*yes_no));
3478         checked_yes_no = nmc_string_is_valid (tmp, strv, NULL);
3479         g_free (tmp);
3480         if (g_strcmp0 (checked_yes_no, WORD_LOC_YES) == 0) {
3481                 g_free (*yes_no);
3482                 *yes_no = g_strdup (WORD_YES);
3483         } else if (g_strcmp0 (checked_yes_no, WORD_LOC_NO) == 0) {
3484                 g_free (*yes_no);
3485                 *yes_no = g_strdup (WORD_NO);
3486         }
3487         return !!checked_yes_no;
3488 }
3489
3490 static gboolean
3491 want_provide_opt_args (const char *type, int num)
3492 {
3493         char *answer;
3494         gboolean ret = TRUE;
3495
3496         /* Ask for optional arguments. */
3497         g_print (ngettext ("There is %d optional argument for '%s' connection type.\n",
3498                            "There are %d optional arguments for '%s' connection type.\n", num),
3499                  num, type);
3500         answer = nmc_readline (ngettext ("Do you want to provide it? %s",
3501                                          "Do you want to provide them? %s", num),
3502                                prompt_yes_no (TRUE, NULL));
3503         answer = answer ? g_strstrip (answer) : NULL;
3504         if (answer && matches (answer, WORD_LOC_YES) != 0)
3505                 ret = FALSE;
3506         g_free (answer);
3507         return ret;
3508 }
3509
3510 static void
3511 do_questionnaire_ethernet (gboolean ethernet, char **mtu, char **mac, char **cloned_mac)
3512 {
3513         gboolean once_more;
3514         GError *error = NULL;
3515
3516         /* Ask for optional arguments */
3517         if (ethernet && !want_provide_opt_args (_("ethernet"), 3))
3518                 return;
3519
3520         if (!*mtu) {
3521                 do {
3522                         *mtu = nmc_readline (_("MTU [auto]: "));
3523                         once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3524                         if (once_more) {
3525                                 g_print ("%s\n", error->message);
3526                                 g_clear_error (&error);
3527                                 g_free (*mtu);
3528                         }
3529                 } while (once_more);
3530         }
3531         if (!*mac) {
3532                 do {
3533                         *mac = nmc_readline (_("MAC [none]: "));
3534                         once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
3535                         if (once_more) {
3536                                 g_print ("%s\n", error->message);
3537                                 g_clear_error (&error);
3538                                 g_free (*mac);
3539                         }
3540                 } while (once_more);
3541         }
3542         if (!*cloned_mac) {
3543                 do {
3544                         *cloned_mac = nmc_readline (_("Cloned MAC [none]: "));
3545                         once_more = !check_mac (*cloned_mac, ARPHRD_ETHER, "cloned-mac", &error);
3546                         if (once_more) {
3547                                 g_print ("%s\n", error->message);
3548                                 g_clear_error (&error);
3549                                 g_free (*cloned_mac);
3550                         }
3551                 } while (once_more);
3552         }
3553 }
3554
3555 #define WORD_DATAGRAM  "datagram"
3556 #define WORD_CONNECTED "connected"
3557 #define PROMPT_IB_MODE "(" WORD_DATAGRAM "/" WORD_CONNECTED ") [" WORD_DATAGRAM "]: "
3558 static void
3559 do_questionnaire_infiniband (char **mtu, char **mac, char **mode, char **parent, char **p_key)
3560 {
3561         gboolean once_more;
3562         GError *error = NULL;
3563
3564         /* Ask for optional arguments */
3565         if (!want_provide_opt_args (_("InfiniBand"), 5))
3566                 return;
3567
3568         if (!*mtu) {
3569                 do {
3570                         *mtu = nmc_readline (_("MTU [auto]: "));
3571                         once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3572                         if (once_more) {
3573                                 g_print ("%s\n", error->message);
3574                                 g_clear_error (&error);
3575                                 g_free (*mtu);
3576                         }
3577                 } while (once_more);
3578         }
3579         if (!*mac) {
3580                 do {
3581                         *mac = nmc_readline (_("MAC [none]: "));
3582                         once_more = !check_mac (*mac, ARPHRD_INFINIBAND, "mac", &error);
3583                         if (once_more) {
3584                                 g_print ("%s\n", error->message);
3585                                 g_clear_error (&error);
3586                                 g_free (*mac);
3587                         }
3588                 } while (once_more);
3589         }
3590         if (!*mode) {
3591                 do {
3592                         *mode = nmc_readline (_("Transport mode %s"), PROMPT_IB_MODE);
3593                         if (!*mode)
3594                                 *mode = g_strdup ("datagram");
3595                         once_more = !check_infiniband_mode (mode, &error);
3596                         if (once_more) {
3597                                 g_print ("%s\n", error->message);
3598                                 g_clear_error (&error);
3599                                 g_free (*mode);
3600                         }
3601                 } while (once_more);
3602         }
3603         if (!*parent) {
3604                 do {
3605                         *parent = nmc_readline (_("Parent interface [none]: "));
3606                         once_more = !check_infiniband_parent (*parent, &error);
3607                         if (once_more) {
3608                                 g_print ("%s\n", error->message);
3609                                 g_clear_error (&error);
3610                                 g_free (*parent);
3611                         }
3612                 } while (once_more);
3613         }
3614         if (!*p_key) {
3615                 do {
3616                         *p_key = nmc_readline (_("P_KEY [none]: "));
3617                         once_more = !check_infiniband_p_key (*p_key, NULL, &error);
3618                         if (once_more) {
3619                                 g_print ("%s\n", error->message);
3620                                 g_clear_error (&error);
3621                                 g_free (*p_key);
3622                         }
3623                         /* If parent is specified, so has to be P_KEY */
3624                         if (!once_more && *parent && !*p_key) {
3625                                 once_more = TRUE;
3626                                 g_print (_("Error: 'p-key' is mandatory when 'parent' is specified.\n"));
3627                         }
3628                 } while (once_more);
3629         }
3630 }
3631
3632 #define WORD_INFRA  "infrastructure"
3633 #define WORD_AP     "ap"
3634 #define WORD_ADHOC  "adhoc"
3635 #define PROMPT_WIFI_MODE "(" WORD_INFRA "/" WORD_AP "/" WORD_ADHOC ") [" WORD_INFRA "]: "
3636 static void
3637 do_questionnaire_wifi (char **mtu, char **mac, char **cloned_mac, char **mode)
3638 {
3639         gboolean once_more;
3640         GError *error = NULL;
3641
3642         /* Ask for optional arguments */
3643         if (!want_provide_opt_args (_("Wi-Fi"), 4))
3644                 return;
3645
3646         /* Most optional Wi-Fi arguments are the same as for ethernet. */
3647         do_questionnaire_ethernet (FALSE, mtu, mac, cloned_mac);
3648
3649         if (!*mode) {
3650                 do {
3651                         *mode = nmc_readline (_("Mode %s"), PROMPT_WIFI_MODE);
3652                         if (!*mode)
3653                                 *mode = g_strdup ("infrastructure");
3654                         once_more = !check_wifi_mode (mode, &error);
3655                         if (once_more) {
3656                                 g_print ("%s\n", error->message);
3657                                 g_clear_error (&error);
3658                                 g_free (*mode);
3659                         }
3660                 } while (once_more);
3661         }
3662 }
3663
3664 static void
3665 do_questionnaire_wimax (char **mac)
3666 {
3667         gboolean once_more;
3668         GError *error = NULL;
3669
3670         /* Ask for optional 'wimax' arguments. */
3671         if (!want_provide_opt_args (_("WiMAX"), 1))
3672                 return;
3673
3674         if (!*mac) {
3675                 do {
3676                         *mac = nmc_readline (_("MAC [none]: "));
3677                         once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
3678                         if (once_more) {
3679                                 g_print ("%s\n", error->message);
3680                                 g_clear_error (&error);
3681                                 g_free (*mac);
3682                         }
3683                 } while (once_more);
3684         }
3685 }
3686
3687 static void
3688 do_questionnaire_pppoe (gboolean echo, char **password, char **service, char **mtu, char **mac)
3689 {
3690         gboolean once_more;
3691         GError *error = NULL;
3692
3693         /* Ask for optional 'pppoe' arguments. */
3694         if (!want_provide_opt_args (_("PPPoE"), 4))
3695                 return;
3696
3697         if (!*password)
3698                 *password = nmc_readline_echo (echo, _("Password [none]: "));
3699         if (!*service)
3700                 *service = nmc_readline (_("Service [none]: "));
3701
3702         if (!*mtu) {
3703                 do {
3704                         *mtu = nmc_readline (_("MTU [auto]: "));
3705                         once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3706                         if (once_more) {
3707                                 g_print ("%s\n", error->message);
3708                                 g_clear_error (&error);
3709                                 g_free (*mtu);
3710                         }
3711                 } while (once_more);
3712         }
3713         if (!*mac) {
3714                 do {
3715                         *mac = nmc_readline (_("MAC [none]: "));
3716                         once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
3717                         if (once_more) {
3718                                 g_print ("%s\n", error->message);
3719                                 g_clear_error (&error);
3720                                 g_free (*mac);
3721                         }
3722                 } while (once_more);
3723         }
3724 }
3725
3726 static void
3727 do_questionnaire_mobile (gboolean echo, char **user, char **password)
3728 {
3729         /* Ask for optional 'gsm' or 'cdma' arguments. */
3730         if (!want_provide_opt_args (_("mobile broadband"), 2))
3731                 return;
3732
3733         if (!*user)
3734                 *user = nmc_readline (_("Username [none]: "));
3735         if (!*password)
3736                 *password = nmc_readline_echo (echo, _("Password [none]: "));
3737 }
3738
3739 #define WORD_PANU      "panu"
3740 #define WORD_DUN_GSM   "dun-gsm"
3741 #define WORD_DUN_CDMA  "dun-cdma"
3742 #define PROMPT_BT_TYPE "(" WORD_PANU "/" WORD_DUN_GSM "/" WORD_DUN_CDMA ") [" WORD_PANU "]: "
3743 static void
3744 do_questionnaire_bluetooth (char **bt_type)
3745 {
3746         gboolean once_more;
3747
3748         /* Ask for optional 'bluetooth' arguments. */
3749         if (!want_provide_opt_args (_("bluetooth"), 1))
3750                 return;
3751
3752         if (!*bt_type) {
3753                 const char *types[] = { "dun", "dun-gsm", "dun-cdma", "panu", NULL };
3754                 const char *tmp;
3755                 do {
3756                         *bt_type = nmc_readline (_("Bluetooth type %s"), PROMPT_BT_TYPE);
3757                         if (!*bt_type)
3758                                 *bt_type = g_strdup ("panu");
3759                         tmp = nmc_string_is_valid (*bt_type, types, NULL);
3760                         once_more = !tmp;
3761                         if (once_more) {
3762                                 g_print (_("Error: 'bt-type': '%s' is not a valid bluetooth type.\n"), *bt_type);
3763                                 g_free (*bt_type);
3764                         }
3765                 } while (once_more);
3766                 g_free (*bt_type);
3767                 *bt_type = g_strdup (tmp);
3768         }
3769 }
3770
3771 static void
3772 do_questionnaire_vlan (char **mtu, char **flags, char **ingress, char **egress)
3773 {
3774         gboolean once_more;
3775         GError *error = NULL;
3776
3777         /* Ask for optional 'vlan' arguments. */
3778         if (!want_provide_opt_args (_("VLAN"), 4))
3779                 return;
3780
3781         if (!*mtu) {
3782                 do {
3783                         *mtu = nmc_readline (_("MTU [auto]: "));
3784                         once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3785                         if (once_more) {
3786                                 g_print ("%s\n", error->message);
3787                                 g_clear_error (&error);
3788                                 g_free (*mtu);
3789                         }
3790                 } while (once_more);
3791         }
3792         if (!*flags) {
3793                 do {
3794                         *flags = nmc_readline (_("VLAN flags (<0-7>) [none]: "));
3795                         once_more = !check_and_convert_vlan_flags (*flags, NULL, &error);
3796                         if (once_more) {
3797                                 g_print ("%s\n", error->message);
3798                                 g_clear_error (&error);
3799                                 g_free (*flags);
3800                         }
3801                 } while (once_more);
3802         }
3803         if (!*ingress) {
3804                 do {
3805                         *ingress = nmc_readline (_("Ingress priority maps [none]: "));
3806                         once_more = !check_and_convert_vlan_prio_maps (*ingress, NM_VLAN_INGRESS_MAP, NULL, &error);
3807                         if (once_more) {
3808                                 g_print ("%s\n", error->message);
3809                                 g_clear_error (&error);
3810                                 g_free (*ingress);
3811                         }
3812                 } while (once_more);
3813         }
3814         if (!*egress) {
3815                 do {
3816                         *egress = nmc_readline (_("Egress priority maps [none]: "));
3817                         once_more = !check_and_convert_vlan_prio_maps (*egress, NM_VLAN_EGRESS_MAP, NULL, &error);
3818                         if (once_more) {
3819                                 g_print ("%s\n", error->message);
3820                                 g_clear_error (&error);
3821                                 g_free (*egress);
3822                         }
3823                 } while (once_more);
3824         }
3825 }
3826
3827 #define PROMPT_BOND_MODE _("Bonding mode [balance-rr]: ")
3828 #define WORD_MIIMON "miimon"
3829 #define WORD_ARP    "arp"
3830 #define PROMPT_BOND_MON_MODE "(" WORD_MIIMON "/" WORD_ARP ") [" WORD_MIIMON "]: "
3831 static void
3832 do_questionnaire_bond (char **mode, char **primary, char **miimon,
3833                        char **downdelay, char **updelay,
3834                        char **arpinterval, char **arpiptarget,
3835                        char **lacp_rate)
3836 {
3837         char *monitor_mode;
3838         unsigned long tmp;
3839         gboolean once_more;
3840         GError *error = NULL;
3841
3842         /* Ask for optional 'bond' arguments. */
3843         if (!want_provide_opt_args (_("bond"), 5))
3844                 return;
3845
3846         if (!*mode) {
3847                 const char *mode_tmp;
3848                 do {
3849                         *mode = nmc_readline (PROMPT_BOND_MODE);
3850                         if (!*mode)
3851                                 *mode = g_strdup ("balance-rr");
3852                         mode_tmp = nmc_bond_validate_mode (*mode, &error);
3853                         g_free (*mode);
3854                         if (mode_tmp) {
3855                                 *mode = g_strdup (mode_tmp);
3856                         } else {
3857                                 g_print ("%s\n", error->message);
3858                                 g_clear_error (&error);
3859                         }
3860                 } while (!mode_tmp);
3861         }
3862
3863         if (g_strcmp0 (*mode, "active-backup") == 0 && !*primary) {
3864                 do {
3865                         *primary = nmc_readline (_("Bonding primary interface [none]: "));
3866                         once_more = *primary && !nm_utils_iface_valid_name (*primary);
3867                         if (once_more) {
3868                                 g_print (_("Error: 'primary': '%s' is not a valid interface name.\n"),
3869                                          *primary);
3870                                 g_free (*primary);
3871                         }
3872                 } while (once_more);
3873         }
3874
3875         do {
3876                 monitor_mode = nmc_readline (_("Bonding monitoring mode %s"), PROMPT_BOND_MON_MODE);
3877                 if (!monitor_mode)
3878                         monitor_mode = g_strdup (WORD_MIIMON);
3879                 g_strstrip (monitor_mode);
3880                 once_more = matches (monitor_mode, WORD_MIIMON) != 0 && matches (monitor_mode, WORD_ARP) != 0;
3881                 if (once_more) {
3882                         g_print (_("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"),
3883                                  monitor_mode, WORD_MIIMON, WORD_ARP);
3884                         g_free (monitor_mode);
3885                 }
3886         } while (once_more);
3887
3888         if (matches (monitor_mode, WORD_MIIMON) == 0) {
3889                 if (!*miimon) {
3890                         do {
3891                                 *miimon = nmc_readline (_("Bonding miimon [100]: "));
3892                                 once_more = *miimon && !nmc_string_to_uint (*miimon, TRUE, 0, G_MAXUINT32, &tmp);
3893                                 if (once_more) {
3894                                         g_print (_("Error: 'miimon': '%s' is not a valid number <0-%u>.\n"),
3895                                                  *miimon, G_MAXUINT32);
3896                                         g_free (*miimon);
3897                                 }
3898                         } while (once_more);
3899                 }
3900                 if (!*downdelay) {
3901                         do {
3902                                 *downdelay = nmc_readline (_("Bonding downdelay [0]: "));
3903                                 once_more = *downdelay && !nmc_string_to_uint (*downdelay, TRUE, 0, G_MAXUINT32, &tmp);
3904                                 if (once_more) {
3905                                         g_print (_("Error: 'downdelay': '%s' is not a valid number <0-%u>.\n"),
3906                                                  *downdelay, G_MAXUINT32);
3907                                         g_free (*downdelay);
3908                                 }
3909                         } while (once_more);
3910                 }
3911                 if (!*updelay) {
3912                         do {
3913                                 *updelay = nmc_readline (_("Bonding updelay [0]: "));
3914                                 once_more = *updelay && !nmc_string_to_uint (*updelay, TRUE, 0, G_MAXUINT32, &tmp);
3915                                 if (once_more) {
3916                                         g_print (_("Error: 'updelay': '%s' is not a valid number <0-%u>.\n"),
3917                                                  *updelay, G_MAXUINT32);
3918                                         g_free (*updelay);
3919                                 }
3920                         } while (once_more);
3921                 }
3922         } else {
3923                 if (!*arpinterval) {
3924                         do {
3925                                 *arpinterval = nmc_readline (_("Bonding arp-interval [0]: "));
3926                                 once_more = *arpinterval && !nmc_string_to_uint (*arpinterval, TRUE, 0, G_MAXUINT32, &tmp);
3927                                 if (once_more) {
3928                                         g_print (_("Error: 'arp-interval': '%s' is not a valid number <0-%u>.\n"),
3929                                                  *arpinterval, G_MAXUINT32);
3930                                         g_free (*arpinterval);
3931                                 }
3932                         } while (once_more);
3933                 }
3934                 if (!*arpiptarget) {
3935                         //FIXME: verify the string
3936                         *arpiptarget = nmc_readline (_("Bonding arp-ip-target [none]: "));
3937                 }
3938         }
3939
3940         if (   !*lacp_rate
3941             && (g_strcmp0 (*mode, "802.3ad") == 0 || g_strcmp0 (*mode, "4") == 0)) {
3942                 do {
3943                         *lacp_rate = nmc_readline (_("LACP rate ('slow' or 'fast') [slow]: "));
3944                         once_more = *lacp_rate && (strcmp (*lacp_rate, "slow") &&
3945                                                    strcmp (*lacp_rate, "0") &&
3946                                                    strcmp (*lacp_rate, "fast") &&
3947                                                    strcmp (*lacp_rate, "1"));
3948                         if (once_more) {
3949                                 printf (_("Error: 'lacp_rate': '%s' is invalid ('slow' or 'fast').\n"),
3950                                         *lacp_rate);
3951                                 g_free (*lacp_rate);
3952                         }
3953                 } while (once_more);
3954         }
3955
3956         g_free (monitor_mode);
3957 }
3958
3959 static void
3960 do_questionnaire_team_common (const char *type_name, char **config)
3961 {
3962         gboolean once_more;
3963         char *json = NULL;
3964         GError *error = NULL;
3965
3966         /* Ask for optional arguments. */
3967         if (!want_provide_opt_args (type_name, 1))
3968                 return;
3969
3970         if (!*config) {
3971                 do {
3972                         *config = nmc_readline (_("Team JSON configuration [none]: "));
3973                         once_more = !nmc_team_check_config (*config, &json, &error);
3974                         if (once_more) {
3975                                 g_print ("Error: %s\n", error->message);
3976                                 g_clear_error (&error);
3977                                 g_free (*config);
3978                         }
3979                 } while (once_more);
3980         }
3981
3982         *config = json;
3983 }
3984
3985 /* Both team and team-slave curently have just ithe same one optional argument */
3986 static void
3987 do_questionnaire_team (char **config)
3988 {
3989         do_questionnaire_team_common (_("team"), config);
3990 }
3991
3992 static void
3993 do_questionnaire_team_slave (char **config)
3994 {
3995         do_questionnaire_team_common (_("team-slave"), config);
3996 }
3997
3998 static void
3999 do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, char **hello_time,
4000                          char **max_age, char **ageing_time, char **mcast_snoop, char **mac)
4001 {
4002         unsigned long tmp;
4003         gboolean once_more;
4004         GError *error = NULL;
4005
4006         /* Ask for optional 'bridge' arguments. */
4007         if (!want_provide_opt_args (_("bridge"), 8))
4008                 return;
4009
4010         if (!*stp) {
4011                 gboolean stp_bool;
4012                 do {
4013                         *stp = nmc_readline (_("Enable STP %s"), prompt_yes_no (TRUE, ":"));
4014                         *stp = *stp ? *stp : g_strdup ("yes");
4015                         normalize_yes_no (stp);
4016                         once_more = !nmc_string_to_bool (*stp, &stp_bool, &error);
4017                         if (once_more) {
4018                                 g_print (_("Error: 'stp': %s.\n"), error->message);
4019                                 g_clear_error (&error);
4020                                 g_free (*stp);
4021                         }
4022                 } while (once_more);
4023         }
4024         if (!*priority) {
4025                 do {
4026                         *priority = nmc_readline (_("STP priority [32768]: "));
4027                         *priority = *priority ? *priority : g_strdup ("32768");
4028                         once_more = !nmc_string_to_uint (*priority, TRUE, 0, G_MAXUINT16, &tmp);
4029                         if (once_more) {
4030                                 g_print (_("Error: 'priority': '%s' is not a valid number <0-%d>.\n"),
4031                                          *priority, G_MAXUINT16);
4032                                 g_free (*priority);
4033                         }
4034                 } while (once_more);
4035         }
4036         if (!*fwd_delay) {
4037                 do {
4038                         *fwd_delay = nmc_readline (_("Forward delay [15]: "));
4039                         *fwd_delay = *fwd_delay ? *fwd_delay : g_strdup ("15");
4040                         once_more = !nmc_string_to_uint (*fwd_delay, TRUE, 2, 30, &tmp);
4041                         if (once_more) {
4042                                 g_print (_("Error: 'forward-delay': '%s' is not a valid number <2-30>.\n"),
4043                                          *fwd_delay);
4044                                 g_free (*fwd_delay);
4045                         }
4046                 } while (once_more);
4047         }
4048
4049         if (!*hello_time) {
4050                 do {
4051                         *hello_time = nmc_readline (_("Hello time [2]: "));
4052                         *hello_time = *hello_time ? *hello_time : g_strdup ("2");
4053                         once_more = !nmc_string_to_uint (*hello_time, TRUE, 1, 10, &tmp);
4054                         if (once_more) {
4055                                 g_print (_("Error: 'hello-time': '%s' is not a valid number <1-10>.\n"),
4056                                          *hello_time);
4057                                 g_free (*hello_time);
4058                         }
4059                 } while (once_more);
4060         }
4061         if (!*max_age) {
4062                 do {
4063                         *max_age = nmc_readline (_("Max age [20]: "));
4064                         *max_age = *max_age ? *max_age : g_strdup ("20");
4065                         once_more = !nmc_string_to_uint (*max_age, TRUE, 6, 40, &tmp);
4066                         if (once_more) {
4067                                 g_print (_("Error: 'max-age': '%s' is not a valid number <6-40>.\n"),
4068                                          *max_age);
4069                                 g_free (*max_age);
4070                         }
4071                 } while (once_more);
4072         }
4073         if (!*ageing_time) {
4074                 do {
4075                         *ageing_time = nmc_readline (_("MAC address ageing time [300]: "));
4076                         *ageing_time = *ageing_time ? *ageing_time : g_strdup ("300");
4077                         once_more = !nmc_string_to_uint (*ageing_time, TRUE, 0, 1000000, &tmp);
4078                         if (once_more) {
4079                                 g_print (_("Error: 'ageing-time': '%s' is not a valid number <0-1000000>.\n"),
4080                                          *ageing_time);
4081                                 g_free (*ageing_time);
4082                         }
4083                 } while (once_more);
4084         }
4085         if (!*mcast_snoop) {
4086                 gboolean mcast_snoop_bool;
4087                 do {
4088                         *mcast_snoop = nmc_readline (_("Enable IGMP snooping %s"), prompt_yes_no (TRUE, ":"));
4089                         *mcast_snoop = *mcast_snoop ? *mcast_snoop : g_strdup ("yes");
4090                         normalize_yes_no (mcast_snoop);
4091                         once_more = !nmc_string_to_bool (*mcast_snoop, &mcast_snoop_bool, &error);
4092                         if (once_more) {
4093                                 g_print (_("Error: 'multicast-snooping': %s.\n"), error->message);
4094                                 g_clear_error (&error);
4095                                 g_free (*mcast_snoop);
4096                         }
4097                 } while (once_more);
4098         }
4099         if (!*mac) {
4100                 do {
4101                         *mac = nmc_get_user_input (_("MAC [none]: "));
4102                         once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
4103                         if (once_more) {
4104                                 g_print ("%s\n", error->message);
4105                                 g_clear_error (&error);
4106                                 g_free (*mac);
4107                         }
4108                 } while (once_more);
4109         }
4110 }
4111
4112 static void
4113 do_questionnaire_bridge_slave (char **priority, char **path_cost, char **hairpin)
4114 {
4115         unsigned long tmp;
4116         gboolean once_more;
4117         GError *error = NULL;
4118
4119         /* Ask for optional 'bridge-slave' arguments. */
4120         if (!want_provide_opt_args (_("bridge-slave"), 3))
4121                 return;
4122
4123         if (!*priority) {
4124                 do {
4125                         *priority = nmc_readline (_("Bridge port priority [32]: "));
4126                         *priority = *priority ? *priority : g_strdup ("32");
4127                         once_more = !bridge_prop_string_to_uint (*priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
4128                                                                  NM_SETTING_BRIDGE_PORT_PRIORITY, &tmp, &error);
4129                         if (once_more) {
4130                                 g_print ("%s\n", error->message);
4131                                 g_clear_error (&error);
4132                                 g_free (*priority);
4133                         }
4134                 } while (once_more);
4135         }
4136         if (!*path_cost) {
4137                 do {
4138                         *path_cost = nmc_readline (_("Bridge port STP path cost [100]: "));
4139                         *path_cost = *path_cost ? *path_cost : g_strdup ("100");
4140                         once_more = !bridge_prop_string_to_uint (*path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
4141                                                                  NM_SETTING_BRIDGE_PORT_PATH_COST, &tmp, &error);
4142                         if (once_more) {
4143                                 g_print ("%s\n", error->message);
4144                                 g_clear_error (&error);
4145                                 g_free (*path_cost);
4146                         }
4147                 } while (once_more);
4148         }
4149         if (!*hairpin) {
4150                 gboolean hairpin_bool;
4151                 do {
4152                         *hairpin = nmc_readline (_("Hairpin %s"), prompt_yes_no (TRUE, ":"));
4153                         *hairpin = *hairpin ? *hairpin : g_strdup ("yes");
4154                         normalize_yes_no (hairpin);
4155                         once_more = !nmc_string_to_bool (*hairpin, &hairpin_bool, &error);
4156                         if (once_more) {
4157                                 g_print (_("Error: 'hairpin': %s.\n"), error->message);
4158                                 g_clear_error (&error);
4159                                 g_free (*hairpin);
4160                         }
4161                 } while (once_more);
4162         }
4163 }
4164
4165 static void
4166 do_questionnaire_vpn (char **user)
4167 {
4168         /* Ask for optional 'vpn' arguments. */
4169         if (!want_provide_opt_args (_("VPN"), 1))
4170                 return;
4171
4172         if (!*user)
4173                 *user = nmc_readline (_("Username [none]: "));
4174 }
4175
4176 static void
4177 do_questionnaire_olpc (char **channel, char **dhcp_anycast)
4178 {
4179         unsigned long tmp;
4180         gboolean once_more;
4181         GError *error = NULL;
4182
4183         /* Ask for optional 'olpc' arguments. */
4184         if (!want_provide_opt_args (_("OLPC Mesh"), 2))
4185                 return;
4186
4187         if (!*channel) {
4188                 do {
4189                         *channel = nmc_readline (_("OLPC Mesh channel [1]: "));
4190                         once_more = *channel && !nmc_string_to_uint (*channel, TRUE, 1, 13, &tmp);
4191                         if (once_more) {
4192                                 g_print (_("Error: 'channel': '%s' is not a valid number <1-13>.\n"),
4193                                          *channel);
4194                                 g_free (*channel);
4195                         }
4196                 } while (once_more);
4197         }
4198         if (!*dhcp_anycast) {
4199                 do {
4200                         *dhcp_anycast = nmc_readline (_("DHCP anycast MAC address [none]: "));
4201                         once_more = !check_mac (*dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", &error);
4202                         if (once_more) {
4203                                 g_print ("%s\n", error->message);
4204                                 g_clear_error (&error);
4205                                 g_free (*dhcp_anycast);
4206                         }
4207                 } while (once_more);
4208         }
4209 }
4210
4211 #define PROMPT_ADSL_ENCAP "(" NM_SETTING_ADSL_ENCAPSULATION_VCMUX "/" NM_SETTING_ADSL_ENCAPSULATION_LLC ") [none]: "
4212 static void
4213 do_questionnaire_adsl (gboolean echo, char **password, char **encapsulation)
4214 {
4215         gboolean once_more;
4216         GError *error = NULL;
4217
4218         /* Ask for optional 'adsl' arguments. */
4219         if (!want_provide_opt_args (_("ADSL"), 2))
4220                 return;
4221
4222         if (!*password)
4223                 *password = nmc_readline_echo (echo, _("Password [none]: "));
4224
4225         if (!*encapsulation) {
4226                 do {
4227                         *encapsulation = nmc_readline (_("ADSL encapsulation %s"), PROMPT_ADSL_ENCAP);
4228                         once_more = !check_adsl_encapsulation (encapsulation, &error);
4229                         if (once_more) {
4230                                 g_print ("%s\n", error->message);
4231                                 g_clear_error (&error);
4232                                 g_free (*encapsulation);
4233                         }
4234                 } while (once_more);
4235         }
4236 }
4237
4238 static void
4239 do_questionnaire_macvlan (char **tap)
4240 {
4241         gboolean once_more;
4242         GError *error = NULL;
4243
4244         /* Ask for optional 'macvlan' arguments. */
4245         if (!want_provide_opt_args (_("macvlan"), 1))
4246                 return;
4247
4248         if (!*tap) {
4249                 gboolean tap_bool;
4250                 do {
4251                         *tap = nmc_readline (_("Tap %s"), prompt_yes_no (FALSE, ":"));
4252                         *tap = *tap ? *tap : g_strdup ("yes");
4253                         normalize_yes_no (tap);
4254                         once_more = !nmc_string_to_bool (*tap, &tap_bool, &error);
4255                         if (once_more) {
4256                                 g_print (_("Error: 'tap': %s.\n"), error->message);
4257                                 g_clear_error (&error);
4258                                 g_free (*tap);
4259                         }
4260                 } while (once_more);
4261         }
4262 }
4263
4264 static void
4265 do_questionnaire_vxlan (char **parent, char **local, char **src_port_min,
4266                         char **src_port_max, char **dst_port)
4267 {
4268         unsigned long tmp;
4269         gboolean once_more;
4270
4271         /* Ask for optional 'vxlan' arguments. */
4272         if (!want_provide_opt_args (_("VXLAN"), 5))
4273                 return;
4274
4275         if (!*parent) {
4276                 do {
4277                         *parent = nmc_readline (_("Parent device [none]: "));
4278                         once_more =    *parent
4279                                     && !nm_utils_is_uuid (*parent)
4280                                     && !nm_utils_iface_valid_name (*parent);
4281                         if (once_more) {
4282                                 g_print (_("Error: 'dev': '%s' is neither UUID nor interface name.\n"),
4283                                          *parent);
4284                                 g_free (*parent);
4285                         }
4286                 } while (once_more);
4287         }
4288
4289         if (!*local) {
4290                 do {
4291                         *local = nmc_readline (_("Local address [none]: "));
4292                         once_more =    *local
4293                                     && !nm_utils_ipaddr_valid (AF_INET, *local)
4294                                     && !nm_utils_ipaddr_valid (AF_INET6, *local);
4295                         if (once_more) {
4296                                 g_print (_("Error: 'local': '%s' is not a valid IP address.\n"),
4297                                          *local);
4298                                 g_free (*local);
4299                         }
4300                 } while (once_more);
4301         }
4302
4303         if (!*src_port_min) {
4304                 do {
4305                         *src_port_min = nmc_readline (_("Minimum source port [0]: "));
4306                         *src_port_min = *src_port_min ? *src_port_min : g_strdup ("0");
4307                         once_more = !nmc_string_to_uint (*src_port_min, TRUE, 0, 65535, &tmp);
4308                         if (once_more) {
4309                                 g_print (_("Error: 'source-port-min': '%s' is not a valid number <0-65535>.\n"),
4310                                          *src_port_min);
4311                                 g_free (*src_port_min);
4312                         }
4313                 } while (once_more);
4314         }
4315
4316         if (!*src_port_max) {
4317                 do {
4318                         *src_port_max = nmc_readline (_("Maximum source port [0]: "));
4319                         *src_port_max = *src_port_max ? *src_port_max : g_strdup ("0");
4320                         once_more = !nmc_string_to_uint (*src_port_max, TRUE, 0, 65535, &tmp);
4321                         if (once_more) {
4322                                 g_print (_("Error: 'source-port-max': '%s' is not a valid number <0-65535>.\n"),
4323                                          *src_port_max);
4324                                 g_free (*src_port_max);
4325                         }
4326                 } while (once_more);
4327         }
4328
4329         if (!*dst_port) {
4330                 do {
4331                         *dst_port = nmc_readline (_("Destination port [8472]: "));
4332                         *dst_port = *dst_port ? *dst_port : g_strdup ("8472");
4333                         once_more = !nmc_string_to_uint (*dst_port, TRUE, 0, 65535, &tmp);
4334                         if (once_more) {
4335                                 g_print (_("Error: 'destination-port': '%s' is not a valid number <0-65535>.\n"),
4336                                          *dst_port);
4337                                 g_free (*dst_port);
4338                         }
4339                 } while (once_more);
4340         }
4341 }
4342
4343 static gboolean
4344 split_address (char* str, char **ip, char **rest)
4345 {
4346         size_t n1, n2, n3;
4347
4348         *ip = *rest = NULL;
4349         if (!str)
4350                 return FALSE;
4351
4352         n1 = strspn  (str,    " \t");
4353         n2 = strcspn (str+n1, " \t\0") + n1;
4354         n3 = strspn  (str+n2, " \t")   + n2;
4355
4356         str[n2] = '\0';
4357         *ip = str[n1] ? str + n1 : NULL;
4358         *rest = str[n3] ? str + n3 : NULL;
4359
4360         return TRUE;
4361 }
4362
4363 static void
4364 ask_for_ip_addresses (NMConnection *connection, int family)
4365 {
4366         gboolean ip_loop;
4367         GError *error = NULL;
4368         char *str, *ip, *rest;
4369         const char *prompt;
4370         gboolean added;
4371         NMIPAddress *ipaddr;
4372
4373         if (family == AF_INET)
4374                 prompt =_("IPv4 address (IP[/plen]) [none]: ");
4375         else
4376                 prompt =_("IPv6 address (IP[/plen]) [none]: ");
4377
4378         ip_loop = TRUE;
4379         do {
4380                 str = nmc_readline ("%s", prompt);
4381                 split_address (str, &ip, &rest);
4382                 if (ip) {
4383                         ipaddr = nmc_parse_and_build_address (family, ip, &error);
4384                         if (ipaddr) {
4385                                 if (family == AF_INET)
4386                                         added = add_ip4_address_to_connection (ipaddr, connection);
4387                                 else
4388                                         added = add_ip6_address_to_connection (ipaddr, connection);
4389                                 if (added)
4390                                         g_print (_("  Address successfully added: %s\n"), ip);
4391                                 else
4392                                         g_print (_("  Warning: address already present: %s\n"), ip);
4393                                 if (rest)
4394                                         g_print (_("  Warning: ignoring garbage at the end: '%s'\n"), rest);
4395                         } else {
4396                                 g_prefix_error (&error, _("Error: "));
4397                                 g_print ("%s\n", error->message);
4398                                 g_clear_error (&error);
4399                         }
4400                 } else
4401                         ip_loop = FALSE;
4402
4403                 g_free (str);
4404         } while (ip_loop);
4405 }
4406
4407 static void
4408 maybe_ask_for_gateway (NMConnection *connection, int family)
4409 {
4410         gboolean gw_loop;
4411         char *str, *gw, *rest;
4412         const char *prompt;
4413         NMSettingIPConfig *s_ip;
4414
4415         if (family == AF_INET) {
4416                 prompt =_("IPv4 gateway [none]: ");
4417                 s_ip = nm_connection_get_setting_ip4_config (connection);
4418         } else {
4419                 prompt =_("IPv6 gateway [none]: ");
4420                 s_ip = nm_connection_get_setting_ip6_config (connection);
4421         }
4422         if (s_ip == NULL)
4423                 return;
4424         if (   nm_setting_ip_config_get_num_addresses (s_ip) == 0
4425             || nm_setting_ip_config_get_gateway (s_ip) != NULL)
4426                 return;
4427
4428         gw_loop = TRUE;
4429         do {
4430                 str = nmc_readline ("%s", prompt);
4431                 split_address (str, &gw, &rest);
4432                 if (gw) {
4433                         if (nm_utils_ipaddr_valid (family, gw)) {
4434                                 g_object_set (s_ip,
4435                                               NM_SETTING_IP_CONFIG_GATEWAY, gw,
4436                                               NULL);
4437                                 gw_loop = FALSE;
4438                         } else
4439                                 g_print (_("Error: invalid gateway address '%s'\n"), gw);
4440                 } else
4441                         gw_loop = FALSE;
4442                 g_free (str);
4443         } while (gw_loop);
4444 }
4445
4446 static void
4447 do_questionnaire_ip (NMConnection *connection)
4448 {
4449         char *answer;
4450
4451         /* Ask for IP addresses */
4452         answer = nmc_readline (_("Do you want to add IP addresses? %s"), prompt_yes_no (TRUE, NULL));
4453         answer = answer ? g_strstrip (answer) : NULL;
4454         if (answer && matches (answer, WORD_LOC_YES) != 0) {
4455                 g_free (answer);
4456                 return;
4457         }
4458         g_free (answer);
4459
4460         g_print (_("Press <Enter> to finish adding addresses.\n"));
4461
4462         ask_for_ip_addresses (connection, AF_INET);
4463         maybe_ask_for_gateway (connection, AF_INET);
4464         ask_for_ip_addresses (connection, AF_INET6);
4465         maybe_ask_for_gateway (connection, AF_INET6);
4466 }
4467
4468 static NMSetting *
4469 is_setting_valid (NMConnection *connection, const NameItem *valid_settings_main, const NameItem *valid_settings_slave, char *setting)
4470 {
4471         const char *setting_name;
4472
4473         if (!(setting_name = check_valid_name (setting, valid_settings_main, valid_settings_slave, NULL)))
4474                 return NULL;
4475         return nm_connection_get_setting_by_name (connection, setting_name);
4476 }
4477
4478 static char *
4479 is_property_valid (NMSetting *setting, const char *property, GError **error)
4480 {
4481         char **valid_props = NULL;
4482         const char *prop_name;
4483         char *ret;
4484
4485         valid_props = nmc_setting_get_valid_properties (setting);
4486         prop_name = nmc_string_is_valid (property, (const char **) valid_props, error);
4487         ret = g_strdup (prop_name);
4488         g_strfreev (valid_props);
4489         return ret;
4490 }
4491
4492 #define WORD_TUN  "tun"
4493 #define WORD_TAP  "tap"
4494 #define PROMPT_TUN_MODE "(" WORD_TUN "/" WORD_TAP ") [" WORD_TUN "]: "
4495 static void
4496 do_questionnaire_tun (char **user, char **group,
4497                       char **pi, char **vnet_hdr, char **multi_queue)
4498 {
4499         gboolean once_more;
4500         GError *error = NULL;
4501         gboolean b;
4502
4503         /* Ask for optional 'tun' arguments. */
4504         if (!want_provide_opt_args (_("Tun"), 5))
4505                 return;
4506
4507         if (!*user) {
4508                 do {
4509                         *user = nmc_readline (_("User ID [none]: "));
4510                         if (!*user)
4511                                 break;
4512                         once_more = !check_user_group_id (*user, &error);
4513                         if (once_more) {
4514                                 g_print ("%s\n", error->message);
4515                                 g_clear_error (&error);
4516                                 g_free (*user);
4517                         }
4518                 } while (once_more);
4519         }
4520         if (!*group) {
4521                 do {
4522                         *group = nmc_readline (_("Group ID [none]: "));
4523                         if (!*group)
4524                                 break;
4525                         once_more = !check_user_group_id (*group, &error);
4526                         if (once_more) {
4527                                 g_print ("%s\n", error->message);
4528                                 g_clear_error (&error);
4529                                 g_free (*group);
4530                         }
4531                 } while (once_more);
4532         }
4533
4534         if (!*pi) {
4535                 do {
4536                         *pi = nmc_readline (_("Enable PI %s"), prompt_yes_no (FALSE, ":"));
4537                         *pi = *pi ? *pi : g_strdup ("no");
4538                         normalize_yes_no (pi);
4539                         once_more = !nmc_string_to_bool (*pi, &b, &error);
4540                         if (once_more) {
4541                                 g_print (_("Error: 'pi': %s.\n"), error->message);
4542                                 g_clear_error (&error);
4543                                 g_free (*pi);
4544                         }
4545                 } while (once_more);
4546         }
4547         if (!*vnet_hdr) {
4548                 do {
4549                         *vnet_hdr = nmc_readline (_("Enable VNET header %s"), prompt_yes_no (FALSE, ":"));
4550                         *vnet_hdr = *vnet_hdr ? *vnet_hdr : g_strdup ("no");
4551                         normalize_yes_no (vnet_hdr);
4552                         once_more = !nmc_string_to_bool (*vnet_hdr, &b, &error);
4553                         if (once_more) {
4554                                 g_print (_("Error: 'vnet-hdr': %s.\n"), error->message);
4555                                 g_clear_error (&error);
4556                                 g_free (*vnet_hdr);
4557                         }
4558                 } while (once_more);
4559         }
4560         if (!*multi_queue) {
4561                 do {
4562                         *multi_queue = nmc_readline (_("Enable multi queue %s"), prompt_yes_no (FALSE, ":"));
4563                         *multi_queue = *multi_queue ? *multi_queue : g_strdup ("no");
4564                         normalize_yes_no (multi_queue);
4565                         once_more = !nmc_string_to_bool (*multi_queue, &b, &error);
4566                         if (once_more) {
4567                                 g_print (_("Error: 'multi-queue': %s.\n"), error->message);
4568                                 g_clear_error (&error);
4569                                 g_free (*multi_queue);
4570                         }
4571                 } while (once_more);
4572         }
4573 }
4574
4575 static void
4576 do_questionnaire_ip_tunnel (char **local, char **parent)
4577 {
4578         gboolean once_more;
4579
4580         /* Ask for optional 'ip-tunnel' arguments. */
4581         if (!want_provide_opt_args (_("IP Tunnel"), 2))
4582                 return;
4583
4584         if (!*local) {
4585                 do {
4586                         *local = nmc_readline (_("Local endpoint [none]: "));
4587                         if (!*local)
4588                                 break;
4589                         once_more =    !nm_utils_ipaddr_valid (AF_INET, *local)
4590                                     && !nm_utils_ipaddr_valid (AF_INET6, *local);
4591                         if (once_more) {
4592                                 g_print (_("Error: 'local': '%s' is not valid; must be an IP address\n"),
4593                                          *local);
4594                                 g_free (*local);
4595                         }
4596                 } while (once_more);
4597         }
4598
4599         if (!*parent) {
4600                 do {
4601                         *parent = nmc_readline (_("Parent device [none]: "));
4602                         once_more =    *parent
4603                                     && !nm_utils_is_uuid (*parent)
4604                                     && !nm_utils_iface_valid_name (*parent);
4605                         if (once_more) {
4606                                 g_print (_("Error: 'dev': '%s' is neither UUID nor interface name.\n"),
4607                                          *parent);
4608                                 g_free (*parent);
4609                         }
4610                 } while (once_more);
4611         }
4612 }
4613
4614 static gboolean
4615 read_connection_properties (NMConnection *connection,
4616                             int argc,
4617                             char **argv,
4618                             GError **error)
4619 {
4620         NMSetting *setting;
4621         NMSettingConnection *s_con;
4622         const char *con_type;
4623         const char *s_dot_p;
4624         const char *value;
4625         char **strv = NULL;
4626         char *slv_type = NULL;
4627         const char *setting_name;
4628         gboolean append = FALSE;
4629         gboolean remove = FALSE;
4630         gboolean success = FALSE;
4631         GError *local = NULL;
4632
4633         s_con = nm_connection_get_setting_connection (connection);
4634         g_assert (s_con);
4635
4636         /* First check if we have a slave-type, as this would mean we will not
4637          * have ip properties but possibly others, slave-type specific.
4638          */
4639         con_type = nm_setting_connection_get_slave_type (s_con);
4640         if (!con_type)
4641                 con_type = "no";
4642
4643         slv_type = g_strdup_printf ("%s-slave", con_type);
4644
4645         con_type = nm_setting_connection_get_connection_type (s_con);
4646
4647         /* Go through arguments and set properties */
4648         while (argc) {
4649                 gs_free char *property_name = NULL;
4650
4651                 s_dot_p = *argv;
4652                 next_arg (&argc, &argv);
4653                 value = *argv;
4654                 next_arg (&argc, &argv);
4655
4656                 if (!s_dot_p) {
4657                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4658                                              _("Error: <setting>.<property> argument is missing."));
4659                         goto finish;
4660                 }
4661                 if (!value) {
4662                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4663                                      _("Error: value for '%s' is missing."), s_dot_p);
4664                         goto finish;
4665                 }
4666                 /* Empty string will reset the value to default */
4667                 if (value[0] == '\0')
4668                         value = NULL;
4669
4670                 if (s_dot_p[0] == '+') {
4671                         s_dot_p++;
4672                         append = TRUE;
4673                 } else if (s_dot_p[0] == '-') {
4674                         s_dot_p++;
4675                         remove = TRUE;
4676                 }
4677
4678                 strv = g_strsplit (s_dot_p, ".", 2);
4679                 if (g_strv_length (strv) != 2) {
4680                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4681                                      _("Error: invalid <setting>.<property> '%s'."), s_dot_p);
4682                         goto finish;
4683                 }
4684
4685                 setting_name = check_valid_name (strv[0], get_valid_settings_array (con_type),
4686                                                  get_valid_settings_array (slv_type), &local);
4687                 if (!setting_name) {
4688                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4689                                      _("Error: invalid or not allowed setting '%s': %s."),
4690                                      strv[0], local->message);
4691                         g_clear_error (&local);
4692                         goto finish;
4693                 }
4694                 setting = nm_connection_get_setting_by_name (connection, setting_name);
4695                 if (!setting) {
4696                         setting = nmc_setting_new_for_name (setting_name);
4697                         if (!setting) {
4698                                 /* This should really not happen */
4699                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_UNKNOWN,
4700                                              _("Error: don't know how to create '%s' setting."),
4701                                              setting_name);
4702                                 goto finish;
4703                         }
4704                         nm_connection_add_setting (connection, setting);
4705                 }
4706
4707                 property_name = is_property_valid (setting, strv[1], &local);
4708                 if (!property_name) {
4709                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4710                                      _("Error: invalid property '%s': %s."),
4711                                      strv[1], local->message);
4712                         g_clear_error (&local);
4713                         goto finish;
4714                 }
4715
4716                 if (!remove) {
4717                         /* Set/add value */
4718                         if (!append)
4719                                 nmc_setting_reset_property (setting, property_name, NULL);
4720                         if (!nmc_setting_set_property (setting, property_name, value, &local)) {
4721                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4722                                              _("Error: failed to modify %s.%s: %s."),
4723                                              strv[0], strv[1], local->message);
4724                                 g_clear_error (&local);
4725                                 goto finish;
4726                         }
4727                 } else {
4728                         /* Remove value
4729                          * - either empty: remove whole value
4730                          * - or specified by index <0-n>: remove item at the index
4731                          * - or option name: remove item with the option name
4732                          */
4733                         if (value) {
4734                                 unsigned long idx;
4735                                 if (nmc_string_to_uint (value, TRUE, 0, G_MAXUINT32, &idx))
4736                                         nmc_setting_remove_property_option (setting, property_name, NULL, idx, &local);
4737                                 else
4738                                         nmc_setting_remove_property_option (setting, property_name, value, 0, &local);
4739                                 if (local) {
4740                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4741                                                      _("Error: failed to remove a value from %s.%s: %s."),
4742                                                      strv[0], strv[1],  local->message);
4743                                         g_clear_error (&local);
4744                                         goto finish;
4745                                 }
4746                         } else
4747                                 nmc_setting_reset_property (setting, property_name, NULL);
4748                 }
4749
4750                 g_strfreev (strv);
4751                 strv = NULL;
4752         }
4753
4754         success = TRUE;
4755 finish:
4756         if (strv)
4757                 g_strfreev (strv);
4758         g_free (slv_type);
4759         return success;
4760 }
4761
4762 static gboolean
4763 complete_slave (NMSettingConnection *s_con,
4764                 const GPtrArray *all_connections,
4765                 const char *slave_type,
4766                 const char *master,
4767                 const char *type,
4768                 gboolean ask,
4769                 GError **error)
4770 {
4771         char *master_ask = NULL;
4772         const char *checked_master = NULL;
4773
4774         if (type)
4775                 g_print (_("Warning: 'type' is ignored. "
4776                            "Use 'nmcli connection add \"%s\" ...' instead."),
4777                            type);
4778
4779         if (nm_setting_connection_get_master (s_con)) {
4780                 /* Master already set. */
4781                 if (master) {
4782                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4783                                              _("Error: redundant 'master' option."));
4784                         return FALSE;
4785                 }
4786                 g_object_set (s_con,
4787                               NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
4788                               NULL);
4789                 return TRUE;
4790         }
4791
4792         if (!master && ask)
4793                 master = master_ask = nmc_readline (PROMPT_MASTER);
4794         if (!master) {
4795                 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4796                                      _("Error: 'master' is required."));
4797                 return FALSE;
4798         }
4799         /* Verify master argument */
4800         checked_master = normalized_master_for_slave (all_connections, master, slave_type, NULL);
4801
4802         /* Change properties in 'connection' setting */
4803         g_object_set (s_con,
4804                       NM_SETTING_CONNECTION_MASTER, checked_master,
4805                       NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
4806                       NULL);
4807
4808         g_free (master_ask);
4809
4810         return TRUE;
4811 }
4812
4813 static gboolean
4814 complete_connection_by_type (NMConnection *connection,
4815                              const char *con_type,
4816                              const GPtrArray *all_connections,
4817                              gboolean ask,
4818                              gboolean show_secrets,
4819                              int argc,
4820                              char **argv,
4821                              GError **error)
4822 {
4823         NMSettingConnection *s_con;
4824         NMSettingGeneric *s_generic;
4825         NMSettingWired *s_wired;
4826         NMSettingInfiniband *s_infiniband;
4827         NMSettingWireless *s_wifi;
4828         NMSettingWimax *s_wimax;
4829         NMSettingPppoe *s_pppoe;
4830         NMSettingGsm *s_gsm;
4831         NMSettingCdma *s_cdma;
4832         NMSettingBluetooth *s_bt;
4833         NMSettingVlan *s_vlan;
4834         NMSettingBond *s_bond;
4835         NMSettingTeam *s_team;
4836         NMSettingTeamPort *s_team_port;
4837         NMSettingBridge *s_bridge;
4838         NMSettingBridgePort *s_bridge_port;
4839         NMSettingVpn *s_vpn;
4840         NMSettingOlpcMesh *s_olpc_mesh;
4841         NMSettingAdsl *s_adsl;
4842         NMSettingTun *s_tun;
4843         NMSettingIPTunnel *s_ip_tunnel;
4844         NMSettingMacvlan *s_macvlan;
4845         NMSettingVxlan *s_vxlan;
4846
4847         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4848
4849         s_con = nm_connection_get_setting_connection (connection);
4850         g_assert (s_con);
4851
4852         if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) {
4853                 /* Build up the settings required for 'ethernet' */
4854                 gboolean success = FALSE;
4855                 const char *mtu_c = NULL;
4856                 char *mtu = NULL;
4857                 guint32 mtu_int = 0;
4858                 const char *mac_c = NULL;
4859                 char *mac = NULL;
4860                 const char *cloned_mac_c = NULL;
4861                 char *cloned_mac = NULL;
4862                 nmc_arg_t exp_args[] = { {"mtu",        TRUE, &mtu_c,        FALSE},
4863                                          {"mac",        TRUE, &mac_c,        FALSE},
4864                                          {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
4865                                          {NULL} };
4866
4867                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4868                         return FALSE;
4869
4870                 /* Also ask for all optional arguments if '--ask' is specified. */
4871                 mtu = g_strdup (mtu_c);
4872                 mac = g_strdup (mac_c);
4873                 cloned_mac = g_strdup (cloned_mac_c);
4874                 if (ask)
4875                         do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac);
4876
4877                 if (!check_and_convert_mtu (mtu, &mtu_int, error))
4878                         goto cleanup_wired;
4879                 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
4880                         goto cleanup_wired;
4881                 if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error))
4882                         goto cleanup_wired;
4883
4884                 /* Add ethernet setting */
4885                 s_wired = (NMSettingWired *) nm_setting_wired_new ();
4886                 nm_connection_add_setting (connection, NM_SETTING (s_wired));
4887
4888                 if (mtu)
4889                         g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
4890                 if (mac)
4891                         g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
4892                 if (cloned_mac)
4893                         g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_mac, NULL);
4894
4895                 success = TRUE;
4896 cleanup_wired:
4897                 g_free (mtu);
4898                 g_free (mac);
4899                 g_free (cloned_mac);
4900                 if (!success)
4901                         return FALSE;
4902
4903         } else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
4904                 /* Build up the settings required for 'infiniband' */
4905                 gboolean success = FALSE;
4906                 const char *mtu_c = NULL;
4907                 char *mtu = NULL;
4908                 guint32 mtu_int = 0;
4909                 const char *mac_c = NULL;
4910                 char *mac = NULL;
4911                 const char *mode_c = NULL;
4912                 char *mode = NULL;
4913                 const char *parent_c = NULL;
4914                 char *parent = NULL;
4915                 const char *p_key_c = NULL;
4916                 char *p_key = NULL;
4917                 guint32 p_key_int = 0;
4918                 nmc_arg_t exp_args[] = { {"mtu",            TRUE, &mtu_c,    FALSE},
4919                                          {"mac",            TRUE, &mac_c,    FALSE},
4920                                          {"transport-mode", TRUE, &mode_c,   FALSE},
4921                                          {"parent",         TRUE, &parent_c, FALSE},
4922                                          {"p-key",          TRUE, &p_key_c,  FALSE},
4923                                          {NULL} };
4924
4925                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4926                         return FALSE;
4927
4928                 /* Also ask for all optional arguments if '--ask' is specified. */
4929                 mtu = g_strdup (mtu_c);
4930                 mac = g_strdup (mac_c);
4931                 mode = g_strdup (mode_c);
4932                 parent = g_strdup (parent_c);
4933                 p_key = g_strdup (p_key_c);
4934                 if (ask)
4935                         do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key);
4936
4937                 if (!check_and_convert_mtu (mtu, &mtu_int, error))
4938                         goto cleanup_ib;
4939                 if (!check_mac (mac, ARPHRD_INFINIBAND, "mac", error))
4940                         goto cleanup_ib;
4941                 if (!check_infiniband_mode (&mode, error))
4942                         goto cleanup_ib;
4943                 if (p_key) {
4944                         if (!check_infiniband_p_key (p_key, &p_key_int, error))
4945                                 goto cleanup_ib;
4946                         if (!check_infiniband_parent (parent, error))
4947                                 goto cleanup_ib;
4948                 } else if (parent) {
4949                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4950                                      _("Error: 'parent': not valid without 'p-key'."));
4951                         goto cleanup_ib;
4952                 }
4953
4954                 /* Add 'infiniband' setting */
4955                 s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
4956                 nm_connection_add_setting (connection, NM_SETTING (s_infiniband));
4957
4958                 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL);
4959                 if (mtu)
4960                         g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL);
4961                 if (mac)
4962                         g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
4963                 if (p_key)
4964                         g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL);
4965                 if (parent)
4966                         g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL);
4967
4968
4969                 success = TRUE;
4970 cleanup_ib:
4971                 g_free (mtu);
4972                 g_free (mac);
4973                 g_free (mode);
4974                 g_free (parent);
4975                 g_free (p_key);
4976                 if (!success)
4977                         return FALSE;
4978
4979         } else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) {
4980                 /* Build up the settings required for 'wifi' */
4981                 gboolean success = FALSE;
4982                 char *ssid_ask = NULL;
4983                 const char *ssid = NULL;
4984                 GBytes *ssid_bytes;
4985                 const char *mtu_c = NULL;
4986                 char *mtu = NULL;
4987                 guint32 mtu_int = 0;
4988                 const char *mac_c = NULL;
4989                 char *mac = NULL;
4990                 const char *cloned_mac_c = NULL;
4991                 char *cloned_mac = NULL;
4992                 const char *mode_c = NULL;
4993                 char *mode = NULL;
4994                 nmc_arg_t exp_args[] = { {"ssid",       TRUE, &ssid,         !ask},
4995                                          {"mtu",        TRUE, &mtu_c,        FALSE},
4996                                          {"mac",        TRUE, &mac_c,        FALSE},
4997                                          {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
4998                                          {"mode",       TRUE, &mode_c,       FALSE},
4999                                          {NULL} };
5000
5001                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5002                         return FALSE;
5003
5004                 if (!ssid && ask)
5005                         ssid = ssid_ask = nmc_readline (_("SSID: "));
5006                 if (!ssid) {
5007                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5008                                              _("Error: 'ssid' is required."));
5009                         return FALSE;
5010                 }
5011
5012                 /* Also ask for all optional arguments if '--ask' is specified. */
5013                 mtu = g_strdup (mtu_c);
5014                 mac = g_strdup (mac_c);
5015                 cloned_mac = g_strdup (cloned_mac_c);
5016                 mode = g_strdup (mode_c);
5017                 if (ask)
5018                         do_questionnaire_wifi (&mtu, &mac, &cloned_mac, &mode);
5019
5020                 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5021                         goto cleanup_wifi;
5022                 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5023                         goto cleanup_wifi;
5024                 if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error))
5025                         goto cleanup_wifi;
5026                 if (!check_wifi_mode (&mode, error))
5027                         goto cleanup_wifi;
5028
5029                 /* Add wifi setting */
5030                 s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
5031                 nm_connection_add_setting (connection, NM_SETTING (s_wifi));
5032
5033                 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
5034                 g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_bytes, NULL);
5035
5036                 if (mtu)
5037                         g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL);
5038                 if (mac)
5039                         g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL);
5040                 if (cloned_mac)
5041                         g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac, NULL);
5042                 if (mode)
5043                         g_object_set (s_wifi, NM_SETTING_WIRELESS_MODE, mode, NULL);
5044
5045                 g_bytes_unref (ssid_bytes);
5046
5047                 success = TRUE;
5048 cleanup_wifi:
5049                 g_free (ssid_ask);
5050                 g_free (mtu);
5051                 g_free (mac);
5052                 g_free (cloned_mac);
5053                 g_free (mode);
5054                 if (!success)
5055                         return FALSE;
5056
5057         } else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) {
5058                 /* Build up the settings required for 'wimax' */
5059                 gboolean success = FALSE;
5060                 const char *nsp_name = NULL;
5061                 char *nsp_name_ask = NULL;
5062                 const char *mac_c = NULL;
5063                 char *mac = NULL;
5064                 nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask},
5065                                          {"mac", TRUE, &mac_c,    FALSE},
5066                                          {NULL} };
5067
5068                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5069                         return FALSE;
5070
5071                 if (!nsp_name && ask)
5072                         nsp_name = nsp_name_ask = nmc_readline (_("WiMAX NSP name: "));
5073                 if (!nsp_name) {
5074                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5075                                              _("Error: 'nsp' is required."));
5076                         goto cleanup_wimax;
5077                 }
5078
5079                 /* Also ask for all optional arguments if '--ask' is specified. */
5080                 mac = g_strdup (mac_c);
5081                 if (ask)
5082                         do_questionnaire_wimax (&mac);
5083
5084                 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5085                         goto cleanup_wimax;
5086
5087                 /* Add 'wimax' setting */
5088                 s_wimax = (NMSettingWimax *) nm_setting_wimax_new ();
5089                 nm_connection_add_setting (connection, NM_SETTING (s_wimax));
5090                 g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL);
5091
5092                 if (mac)
5093                         g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac, NULL);
5094
5095                 success = TRUE;
5096 cleanup_wimax:
5097                 g_free (nsp_name_ask);
5098                 g_free (mac);
5099                 if (!success)
5100                         return FALSE;
5101
5102         } else if (!strcmp (con_type, NM_SETTING_PPPOE_SETTING_NAME)) {
5103                 /* Build up the settings required for 'pppoe' */
5104                 gboolean success = FALSE;
5105                 const char *username = NULL;
5106                 char *username_ask = NULL;
5107                 const char *password_c = NULL;
5108                 char *password = NULL;
5109                 const char *service_c = NULL;
5110                 char *service = NULL;
5111                 const char *mtu_c = NULL;
5112                 char *mtu = NULL;
5113                 guint32 mtu_int = 0;
5114                 const char *mac_c = NULL;
5115                 char *mac = NULL;
5116                 nmc_arg_t exp_args[] = { {"username", TRUE, &username,   !ask},
5117                                          {"password", TRUE, &password_c, FALSE},
5118                                          {"service",  TRUE, &service_c,  FALSE},
5119                                          {"mtu",      TRUE, &mtu_c,      FALSE},
5120                                          {"mac",      TRUE, &mac_c,      FALSE},
5121                                          {NULL} };
5122
5123                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5124                         return FALSE;
5125
5126                 if (!username && ask)
5127                         username = username_ask = nmc_readline (_("PPPoE username: "));
5128                 if (!username) {
5129                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5130                                              _("Error: 'username' is required."));
5131                         goto cleanup_pppoe;
5132                 }
5133
5134                 /* Also ask for all optional arguments if '--ask' is specified. */
5135                 password = g_strdup (password_c);
5136                 service = g_strdup (service_c);
5137                 mtu = g_strdup (mtu_c);
5138                 mac = g_strdup (mac_c);
5139                 if (ask)
5140                         do_questionnaire_pppoe (show_secrets, &password, &service, &mtu, &mac);
5141
5142                 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5143                         goto cleanup_pppoe;
5144                 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5145                         goto cleanup_pppoe;
5146
5147                 /* Add 'pppoe' setting */
5148                 s_pppoe = (NMSettingPppoe *) nm_setting_pppoe_new ();
5149                 nm_connection_add_setting (connection, NM_SETTING (s_pppoe));
5150                 g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, username, NULL);
5151                 g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, password, NULL);
5152                 g_object_set (s_pppoe, NM_SETTING_PPPOE_SERVICE, service, NULL);
5153
5154                 /* Add ethernet setting */
5155                 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5156                 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5157                 if (mtu)
5158                         g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
5159                 if (mac)
5160                         g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
5161
5162                 success = TRUE;
5163 cleanup_pppoe:
5164                 g_free (username_ask);
5165                 g_free (password);
5166                 g_free (service);
5167                 g_free (mtu);
5168                 g_free (mac);
5169                 if (!success)
5170                         return FALSE;
5171
5172         } else if (   !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME)
5173                    || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) {
5174                 /* Build up the settings required for 'gsm' or 'cdma' mobile broadband */
5175                 gboolean success = FALSE;
5176                 const char *apn = NULL;
5177                 char *apn_ask = NULL;
5178                 const char *user_c = NULL;
5179                 char *user = NULL;
5180                 const char *password_c = NULL;
5181                 char *password = NULL;
5182                 gboolean is_gsm;
5183                 int i = 0;
5184                 nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */
5185                                          {NULL} };
5186
5187                 is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME);
5188
5189                 if (is_gsm)
5190                         gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask};
5191                 gsm_args[i++] = (nmc_arg_t) {"user",     TRUE, &user_c,     FALSE};
5192                 gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE};
5193                 gsm_args[i++] = (nmc_arg_t) {NULL};
5194
5195                 if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error))
5196                         return FALSE;
5197
5198                 if (!apn && ask && is_gsm)
5199                         apn = apn_ask = nmc_readline (_("APN: "));
5200                 if (!apn && is_gsm) {
5201                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5202                                              _("Error: 'apn' is required."));
5203                         goto cleanup_mobile;
5204                 }
5205
5206                 /* Also ask for all optional arguments if '--ask' is specified. */
5207                 user = g_strdup (user_c);
5208                 password = g_strdup (password_c);
5209                 if (ask)
5210                         do_questionnaire_mobile (show_secrets, &user, &password);
5211
5212                 if (is_gsm) {
5213                         g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL);
5214
5215                         /* Add 'gsm' setting */
5216                         s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
5217                         nm_connection_add_setting (connection, NM_SETTING (s_gsm));
5218                         g_object_set (s_gsm,
5219                                       NM_SETTING_GSM_NUMBER, "*99#",
5220                                       NM_SETTING_GSM_APN, apn,
5221                                       NM_SETTING_GSM_USERNAME, user,
5222                                       NM_SETTING_GSM_PASSWORD, password,
5223                                       NULL);
5224                         g_free (apn_ask);
5225                 } else {
5226                         g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL);
5227
5228                         /* Add 'cdma' setting */
5229                         s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
5230                         nm_connection_add_setting (connection, NM_SETTING (s_cdma));
5231                         g_object_set (s_cdma,
5232                                       NM_SETTING_CDMA_NUMBER, "#777",
5233                                       NM_SETTING_CDMA_USERNAME, user,
5234                                       NM_SETTING_CDMA_PASSWORD, password,
5235                                       NULL);
5236                 }
5237
5238                 success = TRUE;
5239 cleanup_mobile:
5240                 g_free (user);
5241                 g_free (password);
5242                 if (!success)
5243                         return FALSE;
5244
5245         } else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
5246                 /* Build up the settings required for 'bluetooth' */
5247                 gboolean success = FALSE;
5248                 const char *addr = NULL;
5249                 char *addr_ask = NULL;
5250                 const char *bt_type_c = NULL;
5251                 char *bt_type = NULL;
5252                 nmc_arg_t exp_args[] = { {"addr",    TRUE, &addr,      !ask},
5253                                          {"bt-type", TRUE, &bt_type_c, FALSE},
5254                                          {NULL} };
5255
5256                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5257                         return FALSE;
5258
5259                 if (!addr && ask)
5260                         addr = addr_ask = nmc_readline (_("Bluetooth device address: "));
5261                 if (!addr) {
5262                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5263                                              _("Error: 'addr' is required."));
5264                         return FALSE;
5265                 }
5266                 if (!check_mac (addr, ARPHRD_ETHER, "addr", error))
5267                         goto cleanup_bt;
5268
5269                 /* Also ask for all optional arguments if '--ask' is specified. */
5270                 bt_type = g_strdup (bt_type_c);
5271                 if (ask)
5272                         do_questionnaire_bluetooth (&bt_type);
5273
5274                 /* Default to 'panu' if bt-type is not provided. */
5275                 if (!bt_type)
5276                         bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU);
5277
5278                 /* Add 'bluetooth' setting */
5279                 s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new ();
5280                 nm_connection_add_setting (connection, NM_SETTING (s_bt));
5281
5282                 if (addr)
5283                         g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, addr, NULL);
5284
5285                 /* 'dun' type requires adding 'gsm' or 'cdma' setting */
5286                 if (   !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
5287                     || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) {
5288                         bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
5289                         s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
5290                         nm_connection_add_setting (connection, NM_SETTING (s_gsm));
5291                         g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
5292 //                      g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL;
5293
5294                 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) {
5295                         bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
5296                         s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
5297                         nm_connection_add_setting (connection, NM_SETTING (s_cdma));
5298                         g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL);
5299
5300                 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
5301                         /* no op */
5302                 } else {
5303                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5304                                      _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."),
5305                                      bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN,
5306                                      NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma");
5307                         goto cleanup_bt;
5308                 }
5309                 g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL);
5310
5311                 success = TRUE;
5312 cleanup_bt:
5313                 g_free (addr_ask);
5314                 g_free (bt_type);
5315                 if (!success)
5316                         return FALSE;
5317
5318         } else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) {
5319                 /* Build up the settings required for 'vlan' */
5320                 gboolean success = FALSE;
5321                 const char *parent = NULL;
5322                 char *parent_ask = NULL;
5323                 const char *vlan_id = NULL;
5324                 char *vlan_id_ask = NULL;
5325                 unsigned long id = 0;
5326                 const char *flags_c = NULL;
5327                 char *flags = NULL;
5328                 guint32 flags_int = 0;
5329                 const char *ingress_c = NULL, *egress_c = NULL;
5330                 char *ingress = NULL, *egress = NULL;
5331                 char **ingress_arr = NULL, **egress_arr = NULL, **p;
5332                 const char *mtu_c = NULL;
5333                 char *mtu = NULL;
5334                 guint32 mtu_int;
5335                 gboolean valid_mac = FALSE;
5336                 nmc_arg_t exp_args[] = { {"dev",     TRUE, &parent,    !ask},
5337                                          {"id",      TRUE, &vlan_id,   !ask},
5338                                          {"flags",   TRUE, &flags_c,   FALSE},
5339                                          {"ingress", TRUE, &ingress_c, FALSE},
5340                                          {"egress",  TRUE, &egress_c,  FALSE},
5341                                          {"mtu",     TRUE, &mtu_c,     FALSE},
5342                                          {NULL} };
5343
5344                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5345                         return FALSE;
5346
5347                 if (!parent && ask)
5348                         parent = parent_ask = nmc_readline (_("VLAN parent device or connection UUID: "));
5349                 if (!parent) {
5350                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5351                                              _("Error: 'dev' is required."));
5352                         return FALSE;
5353                 }
5354                 if (!vlan_id && ask)
5355                         vlan_id = vlan_id_ask = nmc_readline (_("VLAN ID <0-4094>: "));
5356                 if (!vlan_id) {
5357                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5358                                              _("Error: 'id' is required."));
5359                         goto cleanup_vlan;
5360                 }
5361                 if (vlan_id) {
5362                         if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4094, &id)) {
5363                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5364                                              _("Error: 'id': '%s' is not valid; use <0-4094>."),
5365                                              vlan_id);
5366                                 goto cleanup_vlan;
5367                         }
5368                 }
5369
5370                 if (   !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
5371                     && !nm_utils_is_uuid (parent)
5372                     && !nm_utils_iface_valid_name (parent)) {
5373                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5374                                      _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
5375                                      parent);
5376                         goto cleanup_vlan;
5377                 }
5378
5379                 /* Also ask for all optional arguments if '--ask' is specified. */
5380                 mtu = g_strdup (mtu_c);
5381                 flags = g_strdup (flags_c);
5382                 ingress = g_strdup (ingress_c);
5383                 egress = g_strdup (egress_c);
5384                 if (ask)
5385                         do_questionnaire_vlan (&mtu, &flags, &ingress, &egress);
5386
5387                 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5388                         goto cleanup_vlan;
5389                 if (!check_and_convert_vlan_flags (flags, &flags_int, error))
5390                         goto cleanup_vlan;
5391                 if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error))
5392                         goto cleanup_vlan;
5393                 if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &egress_arr, error))
5394                         goto cleanup_vlan;
5395
5396                 /* Add 'vlan' setting */
5397                 s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
5398                 nm_connection_add_setting (connection, NM_SETTING (s_vlan));
5399
5400                 /* Add 'wired' setting if necessary */
5401                 if (mtu || valid_mac) {
5402                         s_wired = (NMSettingWired *) nm_setting_wired_new ();
5403                         nm_connection_add_setting (connection, NM_SETTING (s_wired));
5404
5405                         if (mtu)
5406                                 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
5407                         if (valid_mac)
5408                                 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
5409                 }
5410
5411                 /* Set 'vlan' properties */
5412                 if (!valid_mac)
5413                         g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL);
5414
5415                 g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL);
5416
5417                 if (flags)
5418                         g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL);
5419                 for (p = ingress_arr; p && *p; p++)
5420                         nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p);
5421                 for (p = egress_arr; p && *p; p++)
5422                         nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p);
5423
5424                 success = TRUE;
5425 cleanup_vlan:
5426                 g_free (mtu);
5427                 g_free (flags);
5428                 g_free (ingress);
5429                 g_free (egress);
5430                 g_free (parent_ask);
5431                 g_free (vlan_id_ask);
5432                 g_strfreev (ingress_arr);
5433                 g_strfreev (egress_arr);
5434                 if (!success)
5435                         return FALSE;
5436
5437         } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) {
5438                 /* Build up the settings required for 'bond' */
5439                 gboolean success = FALSE;
5440                 const char *ifname = NULL;
5441                 const char *bond_mode_c = NULL;
5442                 char *bond_mode = NULL;
5443                 const char *bond_primary_c = NULL;
5444                 char *bond_primary = NULL;
5445                 const char *bond_miimon_c = NULL;
5446                 char *bond_miimon = NULL;
5447                 const char *bond_downdelay_c = NULL;
5448                 char *bond_downdelay = NULL;
5449                 const char *bond_updelay_c = NULL;
5450                 char *bond_updelay = NULL;
5451                 const char *bond_arpinterval_c = NULL;
5452                 char *bond_arpinterval = NULL;
5453                 const char *bond_arpiptarget_c = NULL;
5454                 char *bond_arpiptarget = NULL;
5455                 const char *bond_lacp_rate_c = NULL;
5456                 char *bond_lacp_rate = NULL;
5457                 nmc_arg_t exp_args[] = { {"mode",          TRUE, &bond_mode_c,        FALSE},
5458                                          {"primary",       TRUE, &bond_primary_c,     FALSE},
5459                                          {"miimon",        TRUE, &bond_miimon_c,      FALSE},
5460                                          {"downdelay",     TRUE, &bond_downdelay_c,   FALSE},
5461                                          {"updelay",       TRUE, &bond_updelay_c,     FALSE},
5462                                          {"arp-interval",  TRUE, &bond_arpinterval_c, FALSE},
5463                                          {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE},
5464                                          {"lacp-rate",     TRUE, &bond_lacp_rate_c, FALSE},
5465                                          {NULL} };
5466
5467                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5468                         return FALSE;
5469
5470                 /* Also ask for all optional arguments if '--ask' is specified. */
5471                 bond_mode = g_strdup (bond_mode_c);
5472                 bond_primary = g_strdup (bond_primary_c);
5473                 bond_miimon = g_strdup (bond_miimon_c);
5474                 bond_downdelay = g_strdup (bond_downdelay_c);
5475                 bond_updelay = g_strdup (bond_updelay_c);
5476                 bond_arpinterval = g_strdup (bond_arpinterval_c);
5477                 bond_arpiptarget = g_strdup (bond_arpiptarget_c);
5478                 bond_lacp_rate = g_strdup (bond_lacp_rate_c);
5479                 if (ask)
5480                         do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon,
5481                                                &bond_downdelay, &bond_updelay,
5482                                                &bond_arpinterval, &bond_arpiptarget,
5483                                                &bond_lacp_rate);
5484
5485                 /* Generate ifname if connection doesn't have one */
5486                 ifname = nm_setting_connection_get_interface_name (s_con);
5487                 if (!ifname) {
5488                         char *bond_ifname = unique_master_iface_ifname (all_connections, "nm-bond");
5489
5490                         g_object_set (s_con,
5491                                       NM_SETTING_CONNECTION_INTERFACE_NAME, bond_ifname,
5492                                       NULL);
5493                         g_free (bond_ifname);
5494                 }
5495
5496                 /* Add 'bond' setting */
5497                 s_bond = (NMSettingBond *) nm_setting_bond_new ();
5498                 nm_connection_add_setting (connection, NM_SETTING (s_bond));
5499
5500                 /* Set bond options */
5501                 if (bond_mode) {
5502                         GError *err = NULL;
5503                         const char *bm;
5504                         if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) {
5505                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5506                                              _("Error: 'mode': %s."), err->message);
5507                                 g_clear_error (&err);
5508                                 goto cleanup_bond;
5509                         }
5510                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm);
5511                 }
5512                 if (bond_primary) {
5513                         if (!nm_utils_iface_valid_name (bond_primary)) {
5514                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5515                                              _("Error: 'primary': '%s' is not a valid interface name."),
5516                                              bond_primary);
5517                                 goto cleanup_bond;
5518                         }
5519                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary);
5520                 }
5521                 if (bond_miimon)
5522                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon);
5523                 if (bond_downdelay && strcmp (bond_downdelay, "0") != 0)
5524                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay);
5525                 if (bond_updelay && strcmp (bond_updelay, "0") != 0)
5526                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay);
5527                 if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0)
5528                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval);
5529                 if (bond_arpiptarget)
5530                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget);
5531                 if (bond_lacp_rate)
5532                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_LACP_RATE, bond_lacp_rate);
5533
5534                 success = TRUE;
5535 cleanup_bond:
5536                 g_free (bond_mode);
5537                 g_free (bond_primary);
5538                 g_free (bond_miimon);
5539                 g_free (bond_downdelay);
5540                 g_free (bond_updelay);
5541                 g_free (bond_arpinterval);
5542                 g_free (bond_arpiptarget);
5543                 g_free (bond_lacp_rate);
5544                 if (!success)
5545                         return FALSE;
5546
5547         } else if (!strcmp (con_type, "bond-slave")) {
5548                 /* Slave types without any specific settings ('bond-slave') */
5549                 const char *master = NULL;
5550                 const char *type = NULL;
5551                 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5552                                          {"type",   TRUE, &type,   FALSE},
5553                                          {NULL} };
5554
5555                 /* Set global variables for use in TAB completion */
5556                 nmc_tab_completion.con_type = NM_SETTING_BOND_SETTING_NAME;
5557
5558                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5559                         return FALSE;
5560
5561                 if (!complete_slave (s_con, all_connections, NM_SETTING_BOND_SETTING_NAME, master, type, ask, error))
5562                         return FALSE;
5563
5564                 /* Change properties in 'connection' setting */
5565                 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL);
5566
5567                 /* Add ethernet setting */
5568                 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5569                 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5570
5571         } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) {
5572                 /* Build up the settings required for 'team' */
5573                 gboolean success = FALSE;
5574                 const char *ifname = NULL;
5575                 const char *config_c = NULL;
5576                 char *config = NULL;
5577                 char *json = NULL;
5578                 nmc_arg_t exp_args[] = { {"config", TRUE, &config_c, FALSE},
5579                                          {NULL} };
5580
5581                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5582                         return FALSE;
5583
5584                 /* Also ask for all optional arguments if '--ask' is specified. */
5585                 config = g_strdup (config_c);
5586                 if (ask)
5587                         do_questionnaire_team (&config);
5588
5589                 /* Generate ifname if conneciton doesn't have one */
5590                 ifname = nm_setting_connection_get_interface_name (s_con);
5591                 if (!ifname) {
5592                         char *team_ifname = unique_master_iface_ifname (all_connections, "nm-team");
5593
5594                         g_object_set (s_con,
5595                                       NM_SETTING_CONNECTION_INTERFACE_NAME, team_ifname,
5596                                       NULL);
5597                         g_free (team_ifname);
5598                 }
5599
5600                 /* Add 'team' setting */
5601                 s_team = (NMSettingTeam *) nm_setting_team_new ();
5602                 nm_connection_add_setting (connection, NM_SETTING (s_team));
5603
5604                 if (!nmc_team_check_config (config, &json, error)) {
5605                         g_prefix_error (error, _("Error: "));
5606                         goto cleanup_team;
5607                 }
5608
5609                 /* Set team options */
5610                 g_object_set (s_team, NM_SETTING_TEAM_CONFIG, json, NULL);
5611
5612                 success = TRUE;
5613 cleanup_team:
5614                 g_free (config);
5615                 g_free (json);
5616                 if (!success)
5617                         return FALSE;
5618
5619         } else if (!strcmp (con_type, "team-slave")) {
5620                 /* Build up the settings required for 'team-slave' */
5621                 gboolean success = FALSE;
5622                 const char *master = NULL;
5623                 char *master_ask = NULL;
5624                 const char *type = NULL;
5625                 const char *config_c = NULL;
5626                 char *config = NULL;
5627                 char *json = NULL;
5628                 nmc_arg_t exp_args[] = { {"master", TRUE, &master,   FALSE},
5629                                          {"type",   TRUE, &type,     FALSE},
5630                                          {"config", TRUE, &config_c, FALSE},
5631                                          {NULL} };
5632
5633                 /* Set global variables for use in TAB completion */
5634                 nmc_tab_completion.con_type = NM_SETTING_TEAM_SETTING_NAME;
5635
5636                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5637                         return FALSE;
5638
5639                 if (!complete_slave (s_con, all_connections, NM_SETTING_TEAM_SETTING_NAME, master, type, ask, error))
5640                         return FALSE;
5641
5642                 /* Also ask for all optional arguments if '--ask' is specified. */
5643                 config = g_strdup (config_c);
5644                 if (ask)
5645                         do_questionnaire_team_slave (&config);
5646
5647                 /* Add 'team-port' setting */
5648                 s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new ();
5649                 nm_connection_add_setting (connection, NM_SETTING (s_team_port));
5650
5651                 if (!nmc_team_check_config (config, &json, error)) {
5652                         g_prefix_error (error, _("Error: "));
5653                         goto cleanup_team_slave;
5654                 }
5655
5656                 /* Set team-port options */
5657                 g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL);
5658
5659                 success = TRUE;
5660 cleanup_team_slave:
5661                 g_free (master_ask);
5662                 g_free (config);
5663                 g_free (json);
5664                 if (!success)
5665                         return FALSE;
5666
5667                 /* Change properties in 'connection' setting */
5668                 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL);
5669
5670                 /* Add ethernet setting */
5671                 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5672                 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5673
5674         } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
5675                 /* Build up the settings required for 'bridge' */
5676                 gboolean success = FALSE;
5677                 const char *ifname = NULL;
5678                 const char *stp_c = NULL;
5679                 char *stp = NULL;
5680                 const char *priority_c = NULL;
5681                 char *priority = NULL;
5682                 const char *fwd_delay_c = NULL;
5683                 char *fwd_delay = NULL;
5684                 const char *hello_time_c = NULL;
5685                 char *hello_time = NULL;
5686                 const char *max_age_c = NULL;
5687                 char *max_age = NULL;
5688                 const char *ageing_time_c = NULL;
5689                 char *ageing_time = NULL;
5690                 const char *mcast_snoop_c = NULL;
5691                 char *mcast_snoop = NULL;
5692                 gboolean stp_bool, mcast_snoop_bool;
5693                 unsigned long stp_prio_int, fwd_delay_int, hello_time_int,
5694                               max_age_int, ageing_time_int;
5695                 const char *mac_c = NULL;
5696                 char *mac = NULL;
5697                 nmc_arg_t exp_args[] = { {"stp",           TRUE, &stp_c,         FALSE},
5698                                          {"priority",      TRUE, &priority_c,    FALSE},
5699                                          {"forward-delay", TRUE, &fwd_delay_c,   FALSE},
5700                                          {"hello-time",    TRUE, &hello_time_c,  FALSE},
5701                                          {"max-age",       TRUE, &max_age_c,     FALSE},
5702                                          {"ageing-time",   TRUE, &ageing_time_c, FALSE},
5703                                          {"multicast-snooping", TRUE, &mcast_snoop_c, FALSE},
5704                                          {"mac",           TRUE, &mac_c,         FALSE},
5705                                          {NULL} };
5706
5707                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5708                         return FALSE;
5709
5710                 /* Also ask for all optional arguments if '--ask' is specified. */
5711                 stp = g_strdup (stp_c);
5712                 priority = g_strdup (priority_c);
5713                 fwd_delay = g_strdup (fwd_delay_c);
5714                 hello_time = g_strdup (hello_time_c);
5715                 max_age = g_strdup (max_age_c);
5716                 ageing_time = g_strdup (ageing_time_c);
5717                 mcast_snoop = g_strdup (mcast_snoop_c);
5718                 mac = g_strdup (mac_c);
5719                 if (ask)
5720                         do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time,
5721                                                  &max_age, &ageing_time, &mcast_snoop, &mac);
5722
5723                 /* Generate ifname if conneciton doesn't have one */
5724                 ifname = nm_setting_connection_get_interface_name (s_con);
5725                 if (!ifname) {
5726                         char *bridge_ifname = unique_master_iface_ifname (all_connections, "nm-bridge");
5727
5728                         g_object_set (s_con,
5729                                       NM_SETTING_CONNECTION_INTERFACE_NAME, bridge_ifname,
5730                                       NULL);
5731                         g_free (bridge_ifname);
5732                 }
5733
5734                 if (stp) {
5735                         GError *tmp_err = NULL;
5736                         if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) {
5737                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5738                                              _("Error: 'stp': %s."), tmp_err->message);
5739                                 g_clear_error (&tmp_err);
5740                                 goto cleanup_bridge;
5741                         }
5742                 }
5743                 if (mcast_snoop) {
5744                         GError *tmp_err = NULL;
5745                         if (!nmc_string_to_bool (mcast_snoop, &mcast_snoop_bool, &tmp_err)) {
5746                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5747                                              _("Error: 'multicast-snooping': %s."), tmp_err->message);
5748                                 g_clear_error (&tmp_err);
5749                                 goto cleanup_bridge;
5750                         }
5751                 }
5752
5753                 /* Add 'bond' setting */
5754                 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5755                 s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
5756                 nm_connection_add_setting (connection, NM_SETTING (s_bridge));
5757
5758                 if (priority)
5759                         if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE,
5760                                                          NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error))
5761                                 goto cleanup_bridge;
5762                 if (fwd_delay)
5763                         if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE,
5764                                                          NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error))
5765                                 goto cleanup_bridge;
5766                 if (hello_time)
5767                         if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE,
5768                                                          NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error))
5769                                 goto cleanup_bridge;
5770                 if (max_age)
5771                         if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE,
5772                                                          NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error))
5773                                 goto cleanup_bridge;
5774                 if (ageing_time)
5775                         if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE,
5776                                                          NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error))
5777                                 goto cleanup_bridge;
5778                 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5779                         goto cleanup_bridge;
5780
5781                 /* Set bridge options */
5782                 if (stp)
5783                         g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL);
5784                 if (priority)
5785                         g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL);
5786                 if (fwd_delay)
5787                         g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL);
5788                 if (hello_time)
5789                         g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL);
5790                 if (max_age)
5791                         g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL);
5792                 if (ageing_time)
5793                         g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL);
5794                 if (mcast_snoop)
5795                         g_object_set (s_bridge, NM_SETTING_BRIDGE_MULTICAST_SNOOPING, mcast_snoop_bool, NULL);
5796                 if (mac)
5797                         g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NULL);
5798
5799                 success = TRUE;
5800 cleanup_bridge:
5801                 g_free (stp);
5802                 g_free (priority);
5803                 g_free (fwd_delay);
5804                 g_free (hello_time);
5805                 g_free (max_age);
5806                 g_free (ageing_time);
5807                 g_free (mcast_snoop);
5808                 g_free (mac);
5809                 if (!success)
5810                         return FALSE;
5811
5812         } else if (!strcmp (con_type, "bridge-slave")) {
5813                 /* Build up the settings required for 'bridge-slave' */
5814                 gboolean success = FALSE;
5815                 const char *master = NULL;
5816                 char *master_ask = NULL;
5817                 const char *type = NULL;
5818                 const char *priority_c = NULL;
5819                 char *priority = NULL;
5820                 const char *path_cost_c = NULL;
5821                 char *path_cost = NULL;
5822                 const char *hairpin_c = NULL;
5823                 char *hairpin = NULL;
5824                 unsigned long prio_int, path_cost_int;
5825                 gboolean hairpin_bool;
5826                 nmc_arg_t exp_args[] = { {"master",    TRUE, &master,      FALSE},
5827                                          {"type",      TRUE, &type,        FALSE},
5828                                          {"priority",  TRUE, &priority_c,  FALSE},
5829                                          {"path-cost", TRUE, &path_cost_c, FALSE},
5830                                          {"hairpin",   TRUE, &hairpin_c,   FALSE},
5831                                          {NULL} };
5832
5833                 /* Set global variables for use in TAB completion */
5834                 nmc_tab_completion.con_type = NM_SETTING_BRIDGE_SETTING_NAME;
5835
5836                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5837                         return FALSE;
5838
5839                 if (!complete_slave (s_con, all_connections, NM_SETTING_BRIDGE_SETTING_NAME, master, type, ask, error))
5840                         return FALSE;
5841
5842                 /* Add 'bridge-port' setting */
5843                 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5844                 s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new ();
5845                 nm_connection_add_setting (connection, NM_SETTING (s_bridge_port));
5846
5847                 /* Also ask for all optional arguments if '--ask' is specified. */
5848                 priority = g_strdup (priority_c);
5849                 path_cost = g_strdup (path_cost_c);
5850                 hairpin = g_strdup (hairpin_c);
5851                 if (ask)
5852                         do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin);
5853
5854                 if (priority)
5855                         if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
5856                                                          NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error))
5857                                 goto cleanup_bridge_slave;
5858                 if (path_cost)
5859                         if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
5860                                                          NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error))
5861                                 goto cleanup_bridge_slave;
5862                 if (hairpin) {
5863                         GError *tmp_err = NULL;
5864                         if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) {
5865                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5866                                              _("Error: 'hairpin': %s."), tmp_err->message);
5867                                 g_clear_error (&tmp_err);
5868                                 goto cleanup_bridge_slave;
5869                         }
5870                 }
5871
5872                 if (priority)
5873                         g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL);
5874                 if (path_cost)
5875                         g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL);
5876                 if (hairpin)
5877                         g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL);
5878
5879                 success = TRUE;
5880 cleanup_bridge_slave:
5881                 g_free (master_ask);
5882                 g_free (priority);
5883                 g_free (path_cost);
5884                 g_free (hairpin);
5885                 if (!success)
5886                         return FALSE;
5887
5888                 /* Change properties in 'connection' setting */
5889                 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL);
5890
5891                 /* Add ethernet setting */
5892                 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5893                 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5894
5895         } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) {
5896                 /* Build up the settings required for 'vpn' */
5897                 gboolean success = FALSE;
5898                 const char *vpn_type = NULL;
5899                 char *vpn_type_ask = NULL;
5900                 const char *user_c = NULL;
5901                 char *user = NULL;
5902                 const char *st;
5903                 char *service_type = NULL;
5904                 nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask},
5905                                          {"user",     TRUE, &user_c,   FALSE},
5906                                          {NULL} };
5907
5908                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5909                         return FALSE;
5910
5911                 if (!vpn_type && ask)
5912                         vpn_type = vpn_type_ask = nmc_readline (PROMPT_VPN_TYPE);
5913                 if (!vpn_type) {
5914                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5915                                              _("Error: 'vpn-type' is required."));
5916                         goto cleanup_vpn;
5917                 }
5918                 if (vpn_type_ask)
5919                         vpn_type = g_strstrip (vpn_type_ask);
5920
5921                 if (!(st = nmc_string_is_valid (vpn_type, nmc_known_vpns, NULL))) {
5922                         g_print (_("Warning: 'vpn-type': %s not known.\n"), vpn_type);
5923                         st = vpn_type;
5924                 }
5925                 service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st);
5926
5927                 /* Also ask for all optional arguments if '--ask' is specified. */
5928                 user = g_strdup (user_c);
5929                 if (ask)
5930                         do_questionnaire_vpn (&user);
5931
5932                 /* Add 'vpn' setting */
5933                 s_vpn = (NMSettingVpn *) nm_setting_vpn_new ();
5934                 nm_connection_add_setting (connection, NM_SETTING (s_vpn));
5935
5936                 g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL);
5937                 g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL);
5938
5939                 success = TRUE;
5940 cleanup_vpn:
5941                 g_free (vpn_type_ask);
5942                 g_free (service_type);
5943                 g_free (user);
5944                 if (!success)
5945                         return FALSE;
5946
5947         } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) {
5948                 /* Build up the settings required for 'olpc' */
5949                 gboolean success = FALSE;
5950                 char *ssid_ask = NULL;
5951                 const char *ssid = NULL;
5952                 GBytes *ssid_bytes;
5953                 const char *channel_c = NULL;
5954                 char *channel = NULL;
5955                 unsigned long chan;
5956                 const char *dhcp_anycast_c = NULL;
5957                 char *dhcp_anycast = NULL;
5958                 nmc_arg_t exp_args[] = { {"ssid",         TRUE, &ssid,           !ask},
5959                                          {"channel",      TRUE, &channel_c,      FALSE},
5960                                          {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE},
5961                                          {NULL} };
5962
5963                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5964                         return FALSE;
5965
5966                 if (!ssid && ask)
5967                         ssid = ssid_ask = nmc_readline (_("SSID: "));
5968                 if (!ssid) {
5969                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5970                                              _("Error: 'ssid' is required."));
5971                         goto cleanup_olpc;
5972                 }
5973
5974                 /* Also ask for all optional arguments if '--ask' is specified. */
5975                 channel = g_strdup (channel_c);
5976                 dhcp_anycast = g_strdup (dhcp_anycast_c);
5977                 if (ask)
5978                         do_questionnaire_olpc (&channel, &dhcp_anycast);
5979
5980                 if (channel) {
5981                         if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) {
5982                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5983                                              _("Error: 'channel': '%s' is not valid; use <1-13>."),
5984                                              channel);
5985                                 goto cleanup_olpc;
5986                         }
5987                 }
5988                 if (!check_mac (dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", error))
5989                         goto cleanup_olpc;
5990
5991                 /* Add OLPC mesh setting */
5992                 s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new ();
5993                 nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh));
5994
5995                 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
5996                 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_bytes, NULL);
5997                 if (channel)
5998                         g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL);
5999                 else
6000                         g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL);
6001                 if (dhcp_anycast)
6002                         g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, dhcp_anycast, NULL);
6003                 g_bytes_unref (ssid_bytes);
6004
6005                 success = TRUE;
6006 cleanup_olpc:
6007                 g_free (ssid_ask);
6008                 g_free (channel);
6009                 g_free (dhcp_anycast);
6010                 if (!success)
6011                         return FALSE;
6012
6013         } else if (!strcmp (con_type, NM_SETTING_ADSL_SETTING_NAME)) {
6014                 /* Build up the settings required for 'adsl' */
6015                 gboolean success = FALSE;
6016                 char *username_ask = NULL;
6017                 const char *username = NULL;
6018                 char *protocol_ask = NULL, *protocol = NULL;
6019                 const char *protocol_c = NULL;
6020                 const char *password_c = NULL;
6021                 char *password = NULL;
6022                 const char *encapsulation_c = NULL;
6023                 char *encapsulation = NULL;
6024                 nmc_arg_t exp_args[] = { {"username",      TRUE, &username,        !ask},
6025                                          {"protocol",      TRUE, &protocol_c,      !ask},
6026                                          {"password",      TRUE, &password_c,      FALSE},
6027                                          {"encapsulation", TRUE, &encapsulation_c, FALSE},
6028                                          {NULL} };
6029
6030                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6031                         return FALSE;
6032
6033                 if (!username && ask)
6034                         username = username_ask = nmc_readline (_("Username: "));
6035                 if (!username) {
6036                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6037                                              _("Error: 'username' is required."));
6038                         goto cleanup_adsl;
6039                 }
6040
6041 #define PROMPT_ADSL_PROTO "(" NM_SETTING_ADSL_PROTOCOL_PPPOA "/" NM_SETTING_ADSL_PROTOCOL_PPPOE "/" NM_SETTING_ADSL_PROTOCOL_IPOATM "): "
6042                 if (!protocol_c && ask)
6043                         protocol_c = protocol_ask = nmc_readline (_("Protocol %s"), PROMPT_ADSL_PROTO);
6044                 if (!protocol_c) {
6045                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6046                                              _("Error: 'protocol' is required."));
6047                         goto cleanup_adsl;
6048                 }
6049                 protocol = g_strdup (protocol_c);
6050                 if (!check_adsl_protocol (&protocol, error))
6051                         goto cleanup_adsl;
6052
6053                 /* Also ask for all optional arguments if '--ask' is specified. */
6054                 password = g_strdup (password_c);
6055                 encapsulation = g_strdup (encapsulation_c);
6056                 if (ask)
6057                         do_questionnaire_adsl (show_secrets, &password, &encapsulation);
6058
6059                 if (!check_adsl_encapsulation (&encapsulation, error))
6060                         goto cleanup_adsl;
6061
6062                 /* Add ADSL setting */
6063                 s_adsl = (NMSettingAdsl *) nm_setting_adsl_new ();
6064                 nm_connection_add_setting (connection, NM_SETTING (s_adsl));
6065
6066                 g_object_set (s_adsl,
6067                               NM_SETTING_ADSL_USERNAME, username,
6068                               NM_SETTING_ADSL_PROTOCOL, protocol,
6069                               NM_SETTING_ADSL_PASSWORD, password,
6070                               NM_SETTING_ADSL_ENCAPSULATION, encapsulation,
6071                               NULL);
6072
6073                 success = TRUE;
6074 cleanup_adsl:
6075                 g_free (username_ask);
6076                 g_free (password);
6077                 g_free (protocol);
6078                 g_free (protocol_ask);
6079                 g_free (encapsulation);
6080
6081                 if (!success)
6082                         return FALSE;
6083
6084         } else if (!strcmp (con_type, NM_SETTING_MACVLAN_SETTING_NAME)) {
6085                 /* Build up the settings required for 'macvlan' */
6086                 gboolean success = FALSE;
6087                 const char *parent = NULL;
6088                 char *parent_ask = NULL;
6089                 const char *mode = NULL;
6090                 char *mode_ask = NULL;
6091                 const char *tap_c = NULL;
6092                 char *tap = NULL;
6093                 NMSettingMacvlanMode mode_enum;
6094                 gboolean valid_mac = FALSE;
6095                 gboolean tap_bool = FALSE;
6096                 nmc_arg_t exp_args[] = { {"dev",     TRUE, &parent,    !ask},
6097                                          {"mode",    TRUE, &mode,      !ask},
6098                                          {"tap",     TRUE, &tap_c,     FALSE},
6099                                          {NULL} };
6100
6101                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6102                         return FALSE;
6103
6104                 if (!parent && ask)
6105                         parent = parent_ask = nmc_readline (_("MACVLAN parent device or connection UUID: "));
6106                 if (!parent) {
6107                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6108                                              _("Error: 'dev' is required."));
6109                         return FALSE;
6110                 }
6111
6112                 if (   !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
6113                     && !nm_utils_is_uuid (parent)
6114                     && !nm_utils_iface_valid_name (parent)) {
6115                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6116                                      _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
6117                                      parent);
6118                         goto cleanup_macvlan;
6119                 }
6120
6121                 if (!mode && ask)
6122                         mode = mode_ask = nmc_readline (PROMPT_MACVLAN_MODE);
6123                 if (!mode) {
6124                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6125                                              _("Error: 'mode' is required."));
6126                         return FALSE;
6127                 }
6128
6129                 if (!nm_utils_enum_from_str (nm_setting_macvlan_mode_get_type(), mode, (int *) &mode_enum, NULL)) {
6130                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6131                                              _("Error: 'mode' is not valid."));
6132                         return FALSE;
6133                 }
6134
6135                 /* Also ask for all optional arguments if '--ask' is specified. */
6136                 tap = g_strdup (tap_c);
6137                 if (ask)
6138                         do_questionnaire_macvlan (&tap);
6139
6140                 if (tap) {
6141                         GError *tmp_err = NULL;
6142                         if (!nmc_string_to_bool (tap, &tap_bool, &tmp_err)) {
6143                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6144                                              _("Error: 'tap': %s."), tmp_err->message);
6145                                 g_clear_error (&tmp_err);
6146                                 goto cleanup_macvlan;
6147                         }
6148                 }
6149
6150                 /* Add 'macvlan' setting */
6151                 s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new ();
6152                 nm_connection_add_setting (connection, NM_SETTING (s_macvlan));
6153
6154                 /* Add 'wired' setting if necessary */
6155                 if (valid_mac) {
6156                         s_wired = (NMSettingWired *) nm_setting_wired_new ();
6157                         nm_connection_add_setting (connection, NM_SETTING (s_wired));
6158                         g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
6159                 }
6160
6161                 /* Set 'macvlan' properties */
6162                 if (!valid_mac)
6163                         g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, parent, NULL);
6164                 g_object_set (s_macvlan, NM_SETTING_MACVLAN_MODE, mode_enum, NULL);
6165                 g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, tap_bool, NULL);
6166
6167                 success = TRUE;
6168 cleanup_macvlan:
6169                 g_free (parent_ask);
6170                 g_free (mode_ask);
6171                 g_free (tap);
6172
6173                 if (!success)
6174                         return FALSE;
6175
6176         } else if (!strcmp (con_type, NM_SETTING_TUN_SETTING_NAME)) {
6177                 /* Build up the settings required for 'tun' */
6178                 gboolean success = FALSE;
6179                 const char *mode_c = NULL;
6180                 char *mode_ask = NULL, *mode = NULL;
6181                 NMSettingTunMode mode_enum;
6182                 const char *owner_c = NULL, *group_c = NULL;
6183                 char *owner = NULL, *group = NULL;
6184                 const char *pi_c = NULL, *vnet_hdr_c = NULL, *multi_queue_c = NULL;
6185                 char *pi = NULL, *vnet_hdr = NULL, *multi_queue = NULL;
6186                 gboolean pi_bool, vnet_hdr_bool, multi_queue_bool;
6187                 nmc_arg_t exp_args[] = { {"mode",        TRUE,  &mode_c,        !ask},
6188                                          {"owner",       TRUE,  &owner_c,       FALSE},
6189                                          {"group",       TRUE,  &group_c,       FALSE},
6190                                          {"pi",          TRUE,  &pi_c,          FALSE},
6191                                          {"vnet-hdr",    TRUE,  &vnet_hdr_c,    FALSE},
6192                                          {"multi-queue", TRUE,  &multi_queue_c, FALSE},
6193                                          {NULL} };
6194
6195                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6196                         return FALSE;
6197
6198                 if (!mode_c && ask) {
6199                         mode_ask = nmc_readline (_("Mode %s"), PROMPT_TUN_MODE);
6200                         mode_ask = mode_ask ? mode_ask : g_strdup ("tun");
6201                         mode_c = mode_ask;
6202                 }
6203                 if (!mode_c) {
6204                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6205                                              _("Error: 'mode' is required."));
6206                         goto cleanup_tun;
6207                 }
6208                 mode = g_strdup (mode_c);
6209                 if (!check_tun_mode (&mode, error))
6210                         goto cleanup_tun;
6211
6212                 if (owner && !check_user_group_id (owner, error))
6213                         goto cleanup_tun;
6214                 if (group && !check_user_group_id (group, error))
6215                         goto cleanup_tun;
6216
6217                 owner = g_strdup (owner_c);
6218                 group = g_strdup (group_c);
6219                 pi = g_strdup (pi_c);
6220                 vnet_hdr = g_strdup (vnet_hdr_c);
6221                 multi_queue = g_strdup (multi_queue_c);
6222                 if (ask)
6223                         do_questionnaire_tun (&owner, &group, &pi, &vnet_hdr, &multi_queue);
6224
6225                 if (pi) {
6226                         GError *tmp_err = NULL;
6227
6228                         if (!nmc_string_to_bool (pi, &pi_bool, &tmp_err)) {
6229                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6230                                              _("Error: 'pi': %s."), tmp_err->message);
6231                                 g_clear_error (&tmp_err);
6232                                 goto cleanup_tun;
6233                         }
6234                 }
6235
6236                 if (vnet_hdr) {
6237                         GError *tmp_err = NULL;
6238
6239                         if (!nmc_string_to_bool (vnet_hdr, &vnet_hdr_bool, &tmp_err)) {
6240                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6241                                              _("Error: 'vnet-hdr': %s."), tmp_err->message);
6242                                 g_clear_error (&tmp_err);
6243                                 goto cleanup_tun;
6244                         }
6245                 }
6246
6247                 if (multi_queue) {
6248                         GError *tmp_err = NULL;
6249
6250                         if (!nmc_string_to_bool (multi_queue, &multi_queue_bool, &tmp_err)) {
6251                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6252                                              _("Error: 'multi-queue': %s."), tmp_err->message);
6253                                 g_clear_error (&tmp_err);
6254                                 goto cleanup_tun;
6255                         }
6256                 }
6257                 /* Add 'tun' setting */
6258                 s_tun = (NMSettingTun *) nm_setting_tun_new ();
6259                 nm_connection_add_setting (connection, NM_SETTING (s_tun));
6260                 mode_enum = !strcmp (mode, "tun") ? NM_SETTING_TUN_MODE_TUN : NM_SETTING_TUN_MODE_TAP;
6261
6262                 g_object_set (s_tun,
6263                               NM_SETTING_TUN_MODE,   mode_enum,
6264                               NM_SETTING_TUN_OWNER,  owner,
6265                               NM_SETTING_TUN_GROUP,  group,
6266                               NULL);
6267                 if (pi)
6268                         g_object_set (s_tun, NM_SETTING_TUN_PI, pi_bool, NULL);
6269                 if (vnet_hdr)
6270                         g_object_set (s_tun, NM_SETTING_TUN_VNET_HDR, vnet_hdr_bool, NULL);
6271                 if (multi_queue)
6272                         g_object_set (s_tun, NM_SETTING_TUN_MULTI_QUEUE, multi_queue_bool, NULL);
6273
6274                 success = TRUE;
6275 cleanup_tun:
6276                 g_free (mode_ask);
6277                 g_free (mode);
6278                 g_free (owner);
6279                 g_free (group);
6280                 g_free (pi);
6281                 g_free (vnet_hdr);
6282                 g_free (multi_queue);
6283                 if (!success)
6284                         return FALSE;
6285
6286         } else if (!strcmp (con_type, NM_SETTING_IP_TUNNEL_SETTING_NAME)) {
6287                 /* Build up the settings required for 'ip-tunnel' */
6288                 const char *mode_c = NULL, *local_c = NULL, *remote_c = NULL;
6289                 char *mode_ask = NULL, *remote_ask = NULL, *local = NULL;
6290                 const char *parent_c = NULL;
6291                 char *parent = NULL;
6292                 gboolean success = FALSE;
6293                 NMIPTunnelMode mode_enum;
6294                 nmc_arg_t exp_args[] = { {"mode",    TRUE, &mode_c,     !ask},
6295                                          {"local",   TRUE, &local_c,    FALSE},
6296                                          {"remote",  TRUE, &remote_c,   !ask},
6297                                          {"dev",     TRUE, &parent_c,   FALSE},
6298                                          {NULL} };
6299
6300                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6301                         return FALSE;
6302
6303                 if (!mode_c && ask)
6304                         mode_c = mode_ask = nmc_readline (PROMPT_IP_TUNNEL_MODE);
6305                 if (!mode_c) {
6306                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6307                                              _("Error: 'mode' is required."));
6308                         goto cleanup_tunnel;
6309                 }
6310
6311                 if (!nm_utils_enum_from_str (nm_ip_tunnel_mode_get_type (),
6312                                              mode_c, (int *) &mode_enum, NULL)) {
6313                         gs_free const char **values = NULL;
6314                         gs_free char *values_str = NULL;
6315
6316                         values = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6317                                                            NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6318                                                            G_MAXINT);
6319                         values_str = g_strjoinv (",", (char **) values);
6320                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6321                                      _("Error: 'mode': '%s' is not valid, use one of %s"),
6322                                      mode_c, values_str);
6323                         goto cleanup_tunnel;
6324                 }
6325
6326                 if (!remote_c && ask)
6327                         remote_c = remote_ask = nmc_readline (_("Remote endpoint: "));
6328                 if (!remote_c) {
6329                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6330                                              _("Error: 'remote' is required."));
6331                         goto cleanup_tunnel;
6332                 }
6333
6334                 if (   !nm_utils_ipaddr_valid (AF_INET, remote_c)
6335                     && !nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6336                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6337                                      _("Error: 'remote': '%s' is not valid; must be an IP address"),
6338                                      remote_c);
6339                         goto cleanup_tunnel;
6340                 }
6341
6342                 local = g_strdup (local_c);
6343                 parent = g_strdup (parent_c);
6344                 if (ask)
6345                         do_questionnaire_ip_tunnel (&local, &parent);
6346
6347                 if (   local
6348                     && !nm_utils_ipaddr_valid (AF_INET, local)
6349                     && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6350                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6351                                      _("Error: 'local': '%s' is not valid; must be an IP address"),
6352                                      local);
6353                         goto cleanup_tunnel;
6354                 }
6355
6356                 if (parent) {
6357                         if (   !nm_utils_is_uuid (parent)
6358                             && !nm_utils_iface_valid_name (parent)) {
6359                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6360                                              _("Error: 'dev': '%s' is neither UUID nor interface name."),
6361                                              parent);
6362                                 goto cleanup_tunnel;
6363                         }
6364                 }
6365
6366                 /* Add 'tunnel' setting */
6367                 s_ip_tunnel = (NMSettingIPTunnel *) nm_setting_ip_tunnel_new ();
6368                 nm_connection_add_setting (connection, NM_SETTING (s_ip_tunnel));
6369
6370                 /* Set 'tunnel' properties */
6371                 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_MODE, mode_enum, NULL);
6372                 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_REMOTE, remote_c, NULL);
6373                 if (local)
6374                         g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_LOCAL, local, NULL);
6375                 if (parent)
6376                         g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, parent, NULL);
6377
6378                 /* Set default values for IPv6 tunnels */
6379                 if (nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6380                         g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_TOS, 64, NULL);
6381                         g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT, 4, NULL);
6382                 }
6383
6384                 success = TRUE;
6385 cleanup_tunnel:
6386                 g_free (remote_ask);
6387                 g_free (mode_ask);
6388                 g_free (parent);
6389                 g_free (local);
6390                 if (!success)
6391                         return FALSE;
6392
6393         } else if (!strcmp (con_type, NM_SETTING_VXLAN_SETTING_NAME)) {
6394                 /* Build up the settings required for 'vxlan' */
6395                 gboolean success = FALSE;
6396                 char *id_ask = NULL;
6397                 const char *id = NULL;
6398                 char *remote_ask = NULL;
6399                 const char *remote = NULL;
6400                 const char *parent_c = NULL, *local_c = NULL;
6401                 const char *src_port_min_c = NULL, *src_port_max_c = NULL;
6402                 const char *dst_port_c = NULL;
6403                 char *parent = NULL, *local = NULL;
6404                 char *src_port_min = NULL, *src_port_max = NULL, *dst_port = NULL;
6405                 unsigned long int vni;
6406                 unsigned long sport_min = G_MAXULONG, sport_max = G_MAXULONG;
6407                 unsigned long dport = G_MAXULONG;
6408                 nmc_arg_t exp_args[] = { {"id",               TRUE, &id,             !ask},
6409                                          {"remote",           TRUE, &remote,         !ask},
6410                                          {"dev",              TRUE, &parent_c,        FALSE},
6411                                          {"local",            TRUE, &local_c,         FALSE},
6412                                          {"source-port-min",  TRUE, &src_port_min_c,  FALSE},
6413                                          {"source-port-max",  TRUE, &src_port_max_c,  FALSE},
6414                                          {"destination-port", TRUE, &dst_port_c,      FALSE},
6415                                          {NULL} };
6416
6417                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6418                         return FALSE;
6419
6420                 if (!id && ask)
6421                         id = id_ask = nmc_readline (_("VXLAN ID: "));
6422                 if (!id) {
6423                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6424                                              _("Error: 'id' is required."));
6425                         goto cleanup_vxlan;
6426                 }
6427
6428                 if (!remote && ask)
6429                         remote = remote_ask = nmc_readline (_("Remote: "));
6430                 if (!remote) {
6431                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6432                                              _("Error: 'remote' is required."));
6433                         goto cleanup_vxlan;
6434                 }
6435
6436                 if (!nmc_string_to_uint (id, TRUE, 0, (1UL << 24) - 1, &vni)) {
6437                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6438                                      _("Error: 'id': '%s' is not valid; use <0-16777215>."), id);
6439                         goto cleanup_vxlan;
6440                 }
6441
6442                 parent = g_strdup (parent_c);
6443                 local = g_strdup (local_c);
6444                 src_port_min = g_strdup (src_port_min_c);
6445                 src_port_max = g_strdup (src_port_max_c);
6446                 dst_port = g_strdup (dst_port_c);
6447
6448                 if (ask)
6449                         do_questionnaire_vxlan (&parent, &local, &src_port_min, &src_port_max, &dst_port);
6450
6451                 if (parent) {
6452                         if (   !nm_utils_is_uuid (parent)
6453                             && !nm_utils_iface_valid_name (parent)) {
6454                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6455                                              _("Error: 'dev': '%s' is neither UUID nor interface name."),
6456                                              parent);
6457                                 goto cleanup_vxlan;
6458                         }
6459                 }
6460
6461                 if (   !nm_utils_ipaddr_valid (AF_INET, remote)
6462                     && !nm_utils_ipaddr_valid (AF_INET6, remote)) {
6463                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6464                                      _("Error: 'remote': '%s' is not a valid IP address"),
6465                                      remote);
6466                         goto cleanup_vxlan;
6467                 }
6468
6469                 if (local) {
6470                         if (   !nm_utils_ipaddr_valid (AF_INET, local)
6471                             && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6472                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6473                                              _("Error: 'local': '%s' is not a valid IP address"),
6474                                              local);
6475                                 goto cleanup_vxlan;
6476                         }
6477                 }
6478
6479                 if (src_port_min) {
6480                         if (!nmc_string_to_uint (src_port_min, TRUE, 0, 65535, &sport_min)) {
6481                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6482                                              _("Error: 'source-port-min': %s is not valid; use <0-65535>."),
6483                                              src_port_min);
6484                                 goto cleanup_vxlan;
6485                         }
6486                 }
6487
6488                 if (src_port_max) {
6489                         if (!nmc_string_to_uint (src_port_max, TRUE, 0, 65535, &sport_max)) {
6490                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6491                                              _("Error: 'source-port-max': %s is not valid; use <0-65535>."),
6492                                              src_port_max);
6493                                 goto cleanup_vxlan;
6494                         }
6495                 }
6496
6497                 if (dst_port) {
6498                         if (!nmc_string_to_uint (dst_port, TRUE, 0, 65535, &dport)) {
6499                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6500                                              _("Error: 'destination-port': %s is not valid; use <0-65535>."),
6501                                              dst_port);
6502                                 goto cleanup_vxlan;
6503                         }
6504                 }
6505
6506                 /* Add 'vxlan' setting */
6507                 s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new ();
6508                 nm_connection_add_setting (connection, NM_SETTING (s_vxlan));
6509
6510                 g_object_set (s_vxlan, NM_SETTING_VXLAN_ID, (guint) vni, NULL);
6511                 g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, remote, NULL);
6512                 g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, local, NULL);
6513                 g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, parent, NULL);
6514
6515                 if (sport_min != G_MAXULONG)
6516                         g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MIN, sport_min, NULL);
6517                 if (sport_max != G_MAXULONG)
6518                         g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MAX, sport_max, NULL);
6519                 if (dport != G_MAXULONG)
6520                         g_object_set (s_vxlan, NM_SETTING_VXLAN_DESTINATION_PORT, dport, NULL);
6521
6522                 success = TRUE;
6523
6524 cleanup_vxlan:
6525                 g_free (id_ask);
6526                 g_free (remote_ask);
6527                 g_free (parent);
6528                 g_free (local);
6529                 g_free (src_port_min);
6530                 g_free (src_port_max);
6531                 if (!success)
6532                         return FALSE;
6533
6534         } else if (!strcmp (con_type, NM_SETTING_GENERIC_SETTING_NAME)) {
6535                 /* Add 'generic' setting */
6536                 s_generic = (NMSettingGeneric *) nm_setting_generic_new ();
6537                 nm_connection_add_setting (connection, NM_SETTING (s_generic));
6538         } else {
6539                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6540                              _("Error: '%s' is not a valid connection type."),
6541                              con_type);
6542                 return FALSE;
6543         }
6544
6545         if (!nm_setting_connection_get_slave_type (s_con)) {
6546                 /* Read and add IP configuration */
6547                 NMIPAddress *ip4addr = NULL, *ip6addr = NULL;
6548                 const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL;
6549                 nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE},
6550                                          {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE},
6551                                          {NULL} };
6552
6553                 while (argc) {
6554                         nmc_arg_t *p;
6555
6556                         /* reset 'found' flag */
6557                         for (p = exp_args; p->name; p++)
6558                                 p->found = FALSE;
6559
6560                         ip4 = gw4 = ip6 = gw6 = NULL;
6561
6562                         if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, NULL))
6563                                 break;
6564
6565                         /* coverity[dead_error_begin] */
6566                         if (ip4) {
6567                                 ip4addr = nmc_parse_and_build_address (AF_INET, ip4, error);
6568                                 if (!ip4addr) {
6569                                         g_prefix_error (error, _("Error: "));
6570                                         return FALSE;
6571                                 }
6572                                 add_ip4_address_to_connection (ip4addr, connection);
6573                         }
6574
6575                         /* coverity[dead_error_begin] */
6576                         if (gw4) {
6577                                 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip4_config (connection);
6578
6579                                 if (!s_ip) {
6580                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6581                                                      _("Error: IPv4 gateway specified without IPv4 addresses"));
6582                                         return FALSE;
6583                                 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6584                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6585                                                      _("Error: multiple IPv4 gateways specified"));
6586                                         return FALSE;
6587                                 } else if (!nm_utils_ipaddr_valid (AF_INET, gw4)) {
6588                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6589                                                      _("Error: Invalid IPv4 gateway '%s'"),
6590                                                      gw4);
6591                                 }
6592
6593                                 g_object_set (s_ip,
6594                                               NM_SETTING_IP_CONFIG_GATEWAY, gw4,
6595                                               NULL);
6596                         }
6597
6598                         /* coverity[dead_error_begin] */
6599                         if (ip6) {
6600                                 ip6addr = nmc_parse_and_build_address (AF_INET6, ip6, error);
6601                                 if (!ip6addr) {
6602                                         g_prefix_error (error, _("Error: "));
6603                                         return FALSE;
6604                                 }
6605                                 add_ip6_address_to_connection (ip6addr, connection);
6606                         }
6607
6608                         /* coverity[dead_error_begin] */
6609                         if (gw6) {
6610                                 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip6_config (connection);
6611
6612                                 if (!s_ip) {
6613                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6614                                                      _("Error: IPv6 gateway specified without IPv6 addresses"));
6615                                         return FALSE;
6616                                 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6617                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6618                                                      _("Error: multiple IPv6 gateways specified"));
6619                                         return FALSE;
6620                                 } else if (!nm_utils_ipaddr_valid (AF_INET, gw6)) {
6621                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6622                                                      _("Error: Invalid IPv6 gateway '%s'"),
6623                                                      gw6);
6624                                 }
6625
6626                                 g_object_set (s_ip,
6627                                               NM_SETTING_IP_CONFIG_GATEWAY, gw6,
6628                                               NULL);
6629                         }
6630                 }
6631
6632                 /* Ask for addresses if '--ask' is specified. */
6633                 if (ask)
6634                         do_questionnaire_ip (connection);
6635         }
6636
6637         if (argc) {
6638                 /* Set extra connection properties. */
6639                 nmc_arg_t exp_args[] = { {"--", FALSE, NULL, TRUE},
6640                                          {NULL} };
6641
6642                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6643                         return FALSE;
6644
6645                 if (!read_connection_properties (connection, argc, argv, error))
6646                         return FALSE;
6647         }
6648
6649         return TRUE;
6650 }
6651
6652 typedef struct {
6653         NmCli *nmc;
6654         char *con_name;
6655 } AddConnectionInfo;
6656
6657 static void
6658 add_connection_cb (GObject *client,
6659                    GAsyncResult *result,
6660                    gpointer user_data)
6661 {
6662         AddConnectionInfo *info = (AddConnectionInfo *) user_data;
6663         NmCli *nmc = info->nmc;
6664         NMRemoteConnection *connection;
6665         GError *error = NULL;
6666
6667         connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
6668         if (error) {
6669                 g_string_printf (nmc->return_text,
6670                                  _("Error: Failed to add '%s' connection: %s"),
6671                                  info->con_name, error->message);
6672                 g_error_free (error);
6673                 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
6674         } else {
6675                 g_print (_("Connection '%s' (%s) successfully added.\n"),
6676                          nm_connection_get_id (NM_CONNECTION (connection)),
6677                          nm_connection_get_uuid (NM_CONNECTION (connection)));
6678                 g_object_unref (connection);
6679         }
6680
6681         g_free (info->con_name);
6682         g_free (info);
6683         quit ();
6684 }
6685
6686 static void
6687 add_new_connection (gboolean persistent,
6688                     NMClient *client,
6689                     NMConnection *connection,
6690                     GAsyncReadyCallback callback,
6691                     gpointer user_data)
6692 {
6693         nm_client_add_connection_async (client, connection, persistent,
6694                                         NULL, callback, user_data);
6695 }
6696
6697 static void
6698 update_connection (gboolean persistent,
6699                    NMRemoteConnection *connection,
6700                    GAsyncReadyCallback callback,
6701                    gpointer user_data)
6702 {
6703         nm_remote_connection_commit_changes_async (connection, persistent,
6704                                                    NULL, callback, user_data);
6705 }
6706
6707 static char *
6708 gen_func_vpn_types (const char *text, int state)
6709 {
6710         return nmc_rl_gen_func_basic (text, state, nmc_known_vpns);
6711 }
6712
6713 static char *
6714 gen_func_bool_values_l10n (const char *text, int state)
6715 {
6716         const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL };
6717         return nmc_rl_gen_func_basic (text, state, words);
6718 }
6719
6720 static char *
6721 gen_func_wifi_mode (const char *text, int state)
6722 {
6723         const char *words[] = { "infrastructure", "ap", "adhoc", NULL };
6724         return nmc_rl_gen_func_basic (text, state, words);
6725 }
6726
6727 static char *
6728 gen_func_ib_type (const char *text, int state)
6729 {
6730         const char *words[] = { "datagram", "connected", NULL };
6731         return nmc_rl_gen_func_basic (text, state, words);
6732 }
6733
6734 static char *
6735 gen_func_bt_type (const char *text, int state)
6736 {
6737         const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL };
6738         return nmc_rl_gen_func_basic (text, state, words);
6739 }
6740
6741 static char *
6742 gen_func_bond_mode (const char *text, int state)
6743 {
6744         const char *words[] = { "balance-rr", "active-backup", "balance-xor", "broadcast",
6745                                 "802.3ad", "balance-tlb", "balance-alb", NULL };
6746         return nmc_rl_gen_func_basic (text, state, words);
6747 }
6748 static char *
6749 gen_func_bond_mon_mode (const char *text, int state)
6750 {
6751         const char *words[] = { "miimon", "arp", NULL };
6752         return nmc_rl_gen_func_basic (text, state, words);
6753 }
6754
6755 static char *
6756 gen_func_adsl_proto (const char *text, int state)
6757 {
6758         const char *words[] = { "pppoe", "pppoa", "ipoatm", NULL };
6759         return nmc_rl_gen_func_basic (text, state, words);
6760 }
6761
6762 static char *
6763 gen_func_adsl_encap (const char *text, int state)
6764 {
6765         const char *words[] = { "vcmux", "llc", NULL };
6766         return nmc_rl_gen_func_basic (text, state, words);
6767 }
6768
6769 static char *
6770 gen_func_tun_mode (const char *text, int state)
6771 {
6772         const char *words[] = { "tun", "tap", NULL };
6773         return nmc_rl_gen_func_basic (text, state, words);
6774 }
6775
6776 static char *
6777 gen_func_ip_tunnel_mode (const char *text, int state)
6778 {
6779         gs_free const char **words = NULL;
6780
6781         words = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6782                                           NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6783                                           G_MAXINT);
6784         return nmc_rl_gen_func_basic (text, state, words);
6785 }
6786
6787 static char *
6788 gen_func_macvlan_mode (const char *text, int state)
6789 {
6790         gs_free const char **words = NULL;
6791
6792         words = nm_utils_enum_get_values (nm_setting_macvlan_mode_get_type(),
6793                                           NM_SETTING_MACVLAN_MODE_UNKNOWN + 1,
6794                                           G_MAXINT);
6795         return nmc_rl_gen_func_basic (text, state, words);
6796 }
6797
6798 static char *
6799 gen_func_master_ifnames (const char *text, int state)
6800 {
6801         int i;
6802         GPtrArray *ifnames;
6803         char *ret;
6804         NMConnection *con;
6805         NMSettingConnection *s_con;
6806         const char *con_type, *ifname;
6807
6808         if (!nm_cli.connections)
6809                 return NULL;
6810
6811         /* Disable appending space after completion */
6812         rl_completion_append_character = '\0';
6813
6814         ifnames = g_ptr_array_sized_new (20);
6815         for (i = 0; i < nm_cli.connections->len; i++) {
6816                 con = NM_CONNECTION (nm_cli.connections->pdata[i]);
6817                 s_con = nm_connection_get_setting_connection (con);
6818                 g_assert (s_con);
6819                 con_type = nm_setting_connection_get_connection_type (s_con);
6820                 if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0)
6821                         continue;
6822                 ifname = nm_connection_get_interface_name (con);
6823                 g_ptr_array_add (ifnames, (gpointer) ifname);
6824         }
6825         g_ptr_array_add (ifnames, (gpointer) NULL);
6826
6827         ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata);
6828
6829         g_ptr_array_free (ifnames, TRUE);
6830         return ret;
6831 }
6832
6833 static gboolean
6834 is_single_word (const char* line)
6835 {
6836         size_t n1, n2, n3;
6837
6838         n1 = strspn  (line,    " \t");
6839         n2 = strcspn (line+n1, " \t\0") + n1;
6840         n3 = strspn  (line+n2, " \t");
6841
6842         if (n3 == 0)
6843                 return TRUE;
6844         else
6845                 return FALSE;
6846 }
6847
6848 static char **
6849 nmcli_con_add_tab_completion (const char *text, int start, int end)
6850 {
6851         char **match_array = NULL;
6852         rl_compentry_func_t *generator_func = NULL;
6853
6854         /* Disable readline's default filename completion */
6855         rl_attempted_completion_over = 1;
6856
6857         /* Restore standard append character to space */
6858         rl_completion_append_character = ' ';
6859
6860         if (!is_single_word (rl_line_buffer))
6861                 return NULL;
6862
6863         if (g_strcmp0 (rl_prompt, PROMPT_CON_TYPE) == 0)
6864                 generator_func = gen_connection_types;
6865         else if (g_strcmp0 (rl_prompt, PROMPT_VPN_TYPE) == 0)
6866                 generator_func = gen_func_vpn_types;
6867         else if (g_strcmp0 (rl_prompt, PROMPT_MASTER) == 0)
6868                 generator_func = gen_func_master_ifnames;
6869         else if (   g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
6870                  || g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, ":"))
6871                  || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL))
6872                  || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, ":")))
6873                 generator_func = gen_func_bool_values_l10n;
6874         else if (g_str_has_suffix (rl_prompt, PROMPT_WIFI_MODE))
6875                 generator_func = gen_func_wifi_mode;
6876         else if (g_str_has_suffix (rl_prompt, PROMPT_IB_MODE))
6877                 generator_func = gen_func_ib_type;
6878         else if (g_str_has_suffix (rl_prompt, PROMPT_BT_TYPE))
6879                 generator_func = gen_func_bt_type;
6880         else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MODE))
6881                 generator_func = gen_func_bond_mode;
6882         else if (g_str_has_suffix (rl_prompt, PROMPT_BOND_MON_MODE))
6883                 generator_func = gen_func_bond_mon_mode;
6884         else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_PROTO))
6885                 generator_func = gen_func_adsl_proto;
6886         else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_ENCAP))
6887                 generator_func = gen_func_adsl_encap;
6888         else if (g_str_has_suffix (rl_prompt, PROMPT_TUN_MODE))
6889                 generator_func = gen_func_tun_mode;
6890         else if (g_str_has_suffix (rl_prompt, PROMPT_IP_TUNNEL_MODE))
6891                 generator_func = gen_func_ip_tunnel_mode;
6892         else if (g_str_has_suffix (rl_prompt, PROMPT_MACVLAN_MODE))
6893                 generator_func = gen_func_macvlan_mode;
6894
6895         if (generator_func)
6896                 match_array = rl_completion_matches (text, generator_func);
6897
6898         return match_array;
6899 }
6900
6901 static NMCResultCode
6902 do_connection_add (NmCli *nmc, int argc, char **argv)
6903 {
6904         NMConnection *connection = NULL;
6905         NMSettingConnection *s_con;
6906         char *uuid;
6907         char *default_name = NULL;
6908         const char *type = NULL;
6909         char *type_ask = NULL;
6910         const char *con_name = NULL;
6911         const char *autoconnect = NULL;
6912         gboolean auto_bool = TRUE;
6913         const char *ifname = NULL;
6914         char *ifname_ask = NULL;
6915         gboolean ifname_mandatory = TRUE;
6916         const char *save = NULL;
6917         gboolean save_bool = TRUE;
6918         const char *master = NULL;
6919         const char *checked_master = NULL;
6920         const char *slave_type = NULL;
6921         AddConnectionInfo *info = NULL;
6922         const char *setting_name;
6923         GError *error = NULL;
6924         nmc_arg_t exp_args[] = { {"type",        TRUE, &type,        !nmc->ask},
6925                                  {"con-name",    TRUE, &con_name,    FALSE},
6926                                  {"autoconnect", TRUE, &autoconnect, FALSE},
6927                                  {"ifname",      TRUE, &ifname,      FALSE},
6928                                  {"save",        TRUE, &save,        FALSE},
6929                                  {"master",      TRUE, &master,      FALSE},
6930                                  {"slave-type",  TRUE, &slave_type,  FALSE},
6931                                  {NULL} };
6932
6933         rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion;
6934
6935         nmc->return_value = NMC_RESULT_SUCCESS;
6936
6937         if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) {
6938                 g_string_assign (nmc->return_text, error->message);
6939                 nmc->return_value = error->code;
6940                 g_clear_error (&error);
6941                 goto error;
6942         }
6943
6944         if (!type && nmc->ask) {
6945                 char *types_tmp = get_valid_options_string (nmc_valid_connection_types, NULL);
6946                 g_print ("Valid types: [%s]\n", types_tmp);
6947                 type = type_ask = nmc_readline (PROMPT_CON_TYPE);
6948                 g_free (types_tmp);
6949         }
6950         if (!type) {
6951                 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
6952                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6953                 goto error;
6954         }
6955         if (type_ask)
6956                 type = g_strstrip (type_ask);
6957
6958         if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, NULL, &error))) {
6959                 g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."),
6960                                  error->message);
6961                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6962                 g_clear_error (&error);
6963                 goto error;
6964         }
6965         if (autoconnect) {
6966                 GError *tmp_err = NULL;
6967                 if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) {
6968                         g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."),
6969                                          tmp_err->message);
6970                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6971                         g_clear_error (&tmp_err);
6972                         goto error;
6973                 }
6974         }
6975         if (save) {
6976                 GError *tmp_err = NULL;
6977                 if (!nmc_string_to_bool (save, &save_bool, &tmp_err)) {
6978                         g_string_printf (nmc->return_text, _("Error: 'save': %s."),
6979                                          tmp_err->message);
6980                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6981                         g_clear_error (&tmp_err);
6982                         goto error;
6983                 }
6984         }
6985
6986         /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */
6987         if (   strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0
6988             || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0
6989             || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0
6990             || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0)
6991                 ifname_mandatory = FALSE;
6992
6993         if (!ifname && ifname_mandatory) {
6994                 if (nmc->ask) {
6995                         ifname = ifname_ask = nmc_readline (_("Interface name [*]: "));
6996                         if (!ifname)
6997                                 ifname = ifname_ask = g_strdup ("*");
6998                 } else {
6999                         if (!*argv)
7000                                 g_string_printf (nmc->return_text, _("Error: 'ifname' argument is required."));
7001                         else
7002                                 g_string_printf (nmc->return_text, _("Error: mandatory 'ifname' not seen before '%s'."),
7003                                                  *argv);
7004                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7005                         goto error;
7006                 }
7007         }
7008         if (ifname) {
7009                 if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) {
7010                         g_string_printf (nmc->return_text,
7011                                          _("Error: 'ifname': '%s' is not a valid interface nor '*'."),
7012                                          ifname);
7013                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7014                         goto error;
7015                 }
7016                 /* Special value of '*' means no specific interface name */
7017                 if (strcmp (ifname, "*") == 0)
7018                         ifname = NULL;
7019         }
7020
7021         /* Create a new connection object */
7022         connection = nm_simple_connection_new ();
7023
7024         /* Build up the 'connection' setting */
7025         s_con = (NMSettingConnection *) nm_setting_connection_new ();
7026         uuid = nm_utils_uuid_generate ();
7027         if (con_name)
7028                 default_name = g_strdup (con_name);
7029         else {
7030                 char *try_name = ifname ?
7031                                      g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname)
7032                                    : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types));
7033                 default_name = nmc_unique_connection_name (nmc->connections, try_name);
7034                 g_free (try_name);
7035         }
7036
7037         if (master)
7038                 /* Verify master argument */
7039                 checked_master = normalized_master_for_slave (nmc->connections, master, slave_type, &slave_type);
7040
7041         g_object_set (s_con,
7042                       NM_SETTING_CONNECTION_ID, default_name,
7043                       NM_SETTING_CONNECTION_UUID, uuid,
7044                       NM_SETTING_CONNECTION_TYPE, setting_name,
7045                       NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool,
7046                       NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
7047                       NM_SETTING_CONNECTION_MASTER, checked_master,
7048                       NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
7049                       NULL);
7050         g_free (uuid);
7051         g_free (default_name);
7052         nm_connection_add_setting (connection, NM_SETTING (s_con));
7053
7054         if (!complete_connection_by_type (connection,
7055                                           setting_name,
7056                                           nmc->connections,
7057                                           nmc->ask,
7058                                           nmc->show_secrets,
7059                                           argc,
7060                                           argv,
7061                                           &error)) {
7062                 g_string_assign (nmc->return_text, error->message);
7063                 nmc->return_value = error->code;
7064                 g_clear_error (&error);
7065                 goto error;
7066         }
7067
7068         nmc->should_wait++;
7069
7070         info = g_malloc0 (sizeof (AddConnectionInfo));
7071         info->nmc = nmc;
7072         info->con_name = g_strdup (nm_connection_get_id (connection));
7073
7074         /* Tell the settings service to add the new connection */
7075         add_new_connection (save_bool,
7076                             nmc->client,
7077                             connection,
7078                             add_connection_cb,
7079                             info);
7080
7081         if (connection)
7082                 g_object_unref (connection);
7083
7084         return nmc->return_value;
7085
7086 error:
7087         if (connection)
7088                 g_object_unref (connection);
7089         g_free (type_ask);
7090         g_free (ifname_ask);
7091
7092         return nmc->return_value;
7093 }
7094
7095
7096 /*----------------------------------------------------------------------------*/
7097 /* Functions for readline TAB completion in editor */
7098
7099 static void
7100 uuid_display_hook (char **array, int len, int max_len)
7101 {
7102         NMConnection *con;
7103         int i, max = 0;
7104         char *tmp;
7105         const char *id;
7106         for (i = 1; i <= len; i++) {
7107                 con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL);
7108                 id = con ? nm_connection_get_id (con) : NULL;
7109                 if (id) {
7110                         tmp = g_strdup_printf ("%s (%s)", array[i], id);
7111                         g_free (array[i]);
7112                         array[i] = tmp;
7113                         if (max < strlen (id))
7114                                 max = strlen (id);
7115                 }
7116         }
7117         rl_display_match_list (array, len, max_len + max + 3);
7118         rl_forced_update_display ();
7119 }
7120
7121 static char *
7122 gen_nmcli_cmds_menu (const char *text, int state)
7123 {
7124         const char *words[] = { "goto", "set", "remove", "describe", "print", "verify",
7125                                 "save", "activate", "back", "help", "quit", "nmcli",
7126                                 NULL };
7127         return nmc_rl_gen_func_basic (text, state, words);
7128 }
7129
7130 static char *
7131 gen_nmcli_cmds_submenu (const char *text, int state)
7132 {
7133         const char *words[] = { "set", "add", "change", "remove", "describe",
7134                                 "print", "back", "help", "quit",
7135                                 NULL };
7136         return nmc_rl_gen_func_basic (text, state, words);
7137 }
7138
7139 static char *
7140 gen_cmd_nmcli (const char *text, int state)
7141 {
7142         const char *words[] = { "status-line", "save-confirmation", "show-secrets", "prompt-color", NULL };
7143         return nmc_rl_gen_func_basic (text, state, words);
7144 }
7145
7146 static char *
7147 gen_cmd_nmcli_prompt_color (const char *text, int state)
7148 {
7149         const char *words[] = { "normal", "black", "red", "green", "yellow",
7150                                 "blue", "magenta", "cyan", "white", NULL };
7151         return nmc_rl_gen_func_basic (text, state, words);
7152 }
7153
7154 static char *
7155 gen_func_bool_values (const char *text, int state)
7156 {
7157         const char *words[] = { "yes", "no", NULL };
7158         return nmc_rl_gen_func_basic (text, state, words);
7159 }
7160
7161 static char *
7162 gen_cmd_verify0 (const char *text, int state)
7163 {
7164         const char *words[] = { "all", "fix", NULL };
7165         return nmc_rl_gen_func_basic (text, state, words);
7166 }
7167
7168 static char *
7169 gen_cmd_print0 (const char *text, int state)
7170 {
7171         static char **words = NULL;
7172         char *ret = NULL;
7173
7174         if (!state) {
7175                 GVariant *settings;
7176                 GVariantIter iter;
7177                 const char *setting_name;
7178                 int i = 0;
7179
7180                 settings = nm_connection_to_dbus (nmc_tab_completion.connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
7181                 words = g_new (char *, g_variant_n_children (settings) + 2);
7182                 g_variant_iter_init (&iter, settings);
7183                 while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL))
7184                         words [i++] = g_strdup (setting_name);
7185                 words[i++] = g_strdup ("all");
7186                 words[i] = NULL;
7187                 g_variant_unref (settings);
7188         }
7189
7190         if (words) {
7191                 ret = nmc_rl_gen_func_basic (text, state, (const char **) words);
7192                 if (ret == NULL) {
7193                         g_strfreev (words);
7194                         words = NULL;
7195                 }
7196         }
7197         return ret;
7198 }
7199
7200 static char *
7201 gen_cmd_print2 (const char *text, int state)
7202 {
7203         const char *words[] = { "setting", "connection", "all", NULL };
7204         return nmc_rl_gen_func_basic (text, state, words);
7205 }
7206
7207 static char *
7208 gen_cmd_save (const char *text, int state)
7209 {
7210         const char *words[] = { "persistent", "temporary", NULL };
7211         return nmc_rl_gen_func_basic (text, state, words);
7212 }
7213
7214 static char *
7215 gen_connection_types (const char *text, int state)
7216 {
7217         static int list_idx, len;
7218         const char *c_type, *a_type;
7219
7220         if (!state) {
7221                 list_idx = 0;
7222                 len = strlen (text);
7223         }
7224
7225         while (nmc_valid_connection_types[list_idx].name) {
7226                 a_type = nmc_valid_connection_types[list_idx].alias;
7227                 c_type = nmc_valid_connection_types[list_idx].name;
7228                 list_idx++;
7229                 if (a_type && !strncmp (text, a_type, len))
7230                         return g_strdup (a_type);
7231                 if (c_type && !strncmp (text, c_type, len))
7232                         return g_strdup (c_type);
7233         }
7234
7235         return NULL;
7236 }
7237
7238 static char *
7239 gen_setting_names (const char *text, int state)
7240 {
7241         static int list_idx, len, is_slv;
7242         const char *s_name, *a_name;
7243         const NameItem *valid_settings_arr;
7244         NMSettingConnection *s_con;
7245         const char *s_type = NULL;
7246         char *slv_type;
7247
7248         if (!state) {
7249                 list_idx = 0;
7250                 len = strlen (text);
7251                 is_slv = 0;
7252         }
7253
7254         if (!is_slv) {
7255                 valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type);
7256                 if (!valid_settings_arr)
7257                         return NULL;
7258                 while (valid_settings_arr[list_idx].name) {
7259                         a_name = valid_settings_arr[list_idx].alias;
7260                         s_name = valid_settings_arr[list_idx].name;
7261                         list_idx++;
7262                         if (len == 0 && a_name)
7263                                 return g_strdup_printf ("%s (%s)", s_name, a_name);
7264                         if (a_name && !strncmp (text, a_name, len))
7265                                 return g_strdup (a_name);
7266                         if (s_name && !strncmp (text, s_name, len))
7267                                 return g_strdup (s_name);
7268                 }
7269
7270                 /* Let's give a try to parameters related to slave type */
7271                 list_idx = 0;
7272                 is_slv = 1;
7273         }
7274
7275         /* is_slv */
7276         s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7277         if (s_con)
7278                 s_type = nm_setting_connection_get_slave_type (s_con);
7279         slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7280         valid_settings_arr = get_valid_settings_array (slv_type);
7281         g_free (slv_type);
7282
7283         while (valid_settings_arr[list_idx].name) {
7284                 a_name = valid_settings_arr[list_idx].alias;
7285                 s_name = valid_settings_arr[list_idx].name;
7286                 list_idx++;
7287                 if (len == 0 && a_name)
7288                         return g_strdup_printf ("%s (%s)", s_name, a_name);
7289                 if (a_name && !strncmp (text, a_name, len))
7290                         return g_strdup (a_name);
7291                 if (s_name && !strncmp (text, s_name, len))
7292                         return g_strdup (s_name);
7293         }
7294
7295         return NULL;
7296 }
7297
7298 static char *
7299 gen_property_names (const char *text, int state)
7300 {
7301         NMSetting *setting = NULL;
7302         char **valid_props = NULL;
7303         char *ret = NULL;
7304         const char *line = rl_line_buffer;
7305         const char *setting_name;
7306         char **strv = NULL;
7307         const NameItem *valid_settings_main;
7308         const NameItem *valid_settings_slave;
7309         const char *p1;
7310         const char *slv_type;
7311
7312         /* Try to get the setting from 'line' - setting_name.property */
7313         p1 = strchr (line, '.');
7314         if (p1) {
7315                 while (p1 > line && !g_ascii_isspace (*p1))
7316                         p1--;
7317
7318                 strv = g_strsplit (p1+1, ".", 2);
7319
7320                 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7321
7322                 /* Support autocompletion of slave-connection parameters
7323                  * guessing the slave type from the setting name already
7324                  * typed (or autocompleted) */
7325                 if (nm_streq0 (strv[0], NM_SETTING_TEAM_PORT_SETTING_NAME))
7326                         slv_type = "team-slave";
7327                 else if (nm_streq0 (strv[0], NM_SETTING_BRIDGE_PORT_SETTING_NAME))
7328                         slv_type = "bridge-slave";
7329                 else
7330                         slv_type = "no-slave";
7331                 valid_settings_slave = get_valid_settings_array (slv_type);
7332
7333                 setting_name = check_valid_name (strv[0],
7334                                                  valid_settings_main,
7335                                                  valid_settings_slave,
7336                                                  NULL);
7337                 setting = nmc_setting_new_for_name (setting_name);
7338         } else {
7339                 /* Else take the current setting, if any */
7340                 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7341         }
7342
7343         if (setting) {
7344                 valid_props = nmc_setting_get_valid_properties (setting);
7345                 ret = nmc_rl_gen_func_basic (text, state, (const char **) valid_props);
7346         }
7347
7348         g_strfreev (strv);
7349         g_strfreev (valid_props);
7350         if (setting)
7351                 g_object_unref (setting);
7352         return ret;
7353 }
7354
7355 static char *
7356 gen_compat_devices (const char *text, int state)
7357 {
7358         int i, j = 0;
7359         const GPtrArray *devices;
7360         const char **compatible_devices;
7361         char *ret;
7362
7363         devices = nm_client_get_devices (nmc_tab_completion.nmc->client);
7364         if (devices->len == 0)
7365                 return NULL;
7366
7367         compatible_devices = g_new (const char *, devices->len + 1);
7368         for (i = 0; i < devices->len; i++) {
7369                 NMDevice *dev = g_ptr_array_index (devices, i);
7370                 const char *ifname = nm_device_get_iface (dev);
7371                 NMDevice *device = NULL;
7372                 const char *spec_object = NULL;
7373
7374                 if (find_device_for_connection (nmc_tab_completion.nmc, nmc_tab_completion.connection,
7375                                                 ifname, NULL, NULL, &device, &spec_object, NULL)) {
7376                         compatible_devices[j++] = ifname;
7377                 }
7378         }
7379         compatible_devices[j] = NULL;
7380
7381         ret = nmc_rl_gen_func_basic (text, state, compatible_devices);
7382
7383         g_free (compatible_devices);
7384         return ret;
7385 }
7386
7387 static const char **
7388 _create_vpn_array (const GPtrArray *connections, gboolean uuid)
7389 {
7390         int c, idx = 0;
7391         const char **array;
7392
7393         if (connections->len < 1)
7394                 return NULL;
7395
7396         array = g_new (const char *, connections->len + 1);
7397         for (c = 0; c < connections->len; c++) {
7398                 NMConnection *connection = NM_CONNECTION (connections->pdata[c]);
7399                 const char *type = nm_connection_get_connection_type (connection);
7400
7401                 if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) == 0)
7402                         array[idx++] = uuid ? nm_connection_get_uuid (connection) : nm_connection_get_id (connection);
7403         }
7404         array[idx] = NULL;
7405         return array;
7406 }
7407
7408 static char *
7409 gen_vpn_uuids (const char *text, int state)
7410 {
7411         const GPtrArray *connections = nm_cli.connections;
7412         const char **uuids;
7413         char *ret;
7414
7415         if (connections->len < 1)
7416                 return NULL;
7417
7418         uuids = _create_vpn_array (connections, TRUE);
7419         ret = nmc_rl_gen_func_basic (text, state, uuids);
7420         g_free (uuids);
7421         return ret;
7422 }
7423
7424 static char *
7425 gen_vpn_ids (const char *text, int state)
7426 {
7427         const GPtrArray *connections = nm_cli.connections;
7428         const char **ids;
7429         char *ret;
7430
7431         if (connections->len < 1)
7432                 return NULL;
7433
7434         ids = _create_vpn_array (connections, FALSE);
7435         ret = nmc_rl_gen_func_basic (text, state, ids);
7436         g_free (ids);
7437         return ret;
7438 }
7439
7440 static rl_compentry_func_t *
7441 get_gen_func_cmd_nmcli (const char *str)
7442 {
7443         if (!str)
7444                 return NULL;
7445         if (matches (str, "status-line") == 0)
7446                 return gen_func_bool_values;
7447         if (matches (str, "save-confirmation") == 0)
7448                 return gen_func_bool_values;
7449         if (matches (str, "show-secrets") == 0)
7450                 return gen_func_bool_values;
7451         if (matches (str, "prompt-color") == 0)
7452                 return gen_cmd_nmcli_prompt_color;
7453         return NULL;
7454 }
7455
7456 /*
7457  * Helper function parsing line for completion.
7458  * IN:
7459  *   line : the whole line to be parsed
7460  *   end  : the position of cursor in the line
7461  *   cmd  : command to match
7462  * OUT:
7463  *   cw_num    : is set to the word number being completed (1, 2, 3, 4).
7464  *   prev_word : returns the previous word (so that we have some context).
7465  *
7466  * Returns TRUE when the first word of the 'line' matches 'cmd'.
7467  *
7468  * Examples:
7469  * line="rem"              cmd="remove"   -> TRUE  cw_num=1
7470  * line="set con"          cmd="set"      -> TRUE  cw_num=2
7471  * line="go ipv4.method"   cmd="goto"     -> TRUE  cw_num=2
7472  * line="  des eth.mtu "   cmd="describe" -> TRUE  cw_num=3
7473  * line=" bla ipv4.method" cmd="goto"     -> FALSE
7474  */
7475 static gboolean
7476 should_complete_cmd (const char *line, int end, const char *cmd,
7477                      int *cw_num, char **prev_word)
7478 {
7479         char *tmp;
7480         const char *word1, *word2, *word3;
7481         size_t n1, n2, n3, n4, n5, n6;
7482         gboolean word1_done, word2_done, word3_done;
7483         gboolean ret = FALSE;
7484
7485         if (!line)
7486                 return FALSE;
7487
7488         tmp = g_strdup (line);
7489
7490         n1 = strspn  (tmp,    " \t");
7491         n2 = strcspn (tmp+n1, " \t\0") + n1;
7492         n3 = strspn  (tmp+n2, " \t")   + n2;
7493         n4 = strcspn (tmp+n3, " \t\0") + n3;
7494         n5 = strspn  (tmp+n4, " \t")   + n4;
7495         n6 = strcspn (tmp+n5, " \t\0") + n5;
7496
7497         word1_done = end > n2;
7498         word2_done = end > n4;
7499         word3_done = end > n6;
7500         tmp[n2] = tmp[n4] = tmp[n6] = '\0';
7501
7502         word1 = tmp[n1] ? tmp + n1 : NULL;
7503         word2 = tmp[n3] ? tmp + n3 : NULL;
7504         word3 = tmp[n5] ? tmp + n5 : NULL;
7505
7506         if (!word1_done) {
7507                 if (cw_num)
7508                         *cw_num = 1;
7509                 if (prev_word)
7510                         *prev_word = NULL;
7511         } else if (!word2_done) {
7512                 if (cw_num)
7513                         *cw_num = 2;
7514                 if (prev_word)
7515                         *prev_word = g_strdup (word1);
7516         } else if (!word3_done) {
7517                 if (cw_num)
7518                         *cw_num = 3;
7519                 if (prev_word)
7520                         *prev_word = g_strdup (word2);
7521         } else {
7522                 if (cw_num)
7523                         *cw_num = 4;
7524                 if (prev_word)
7525                         *prev_word = g_strdup (word3);
7526         }
7527
7528         if (word1 && matches (word1, cmd) == 0)
7529                 ret = TRUE;
7530
7531         g_free (tmp);
7532         return ret;
7533 }
7534
7535 /*
7536  * extract_setting_and_property:
7537  * prompt: (in) (allow-none): prompt string, or NULL
7538  * line: (in) (allow-none): line, or NULL
7539  * setting: (out) (transfer full) (array zero-terminated=1):
7540  *   return location for setting name
7541  * property: (out) (transfer full) (array zero-terminated=1):
7542  *   return location for property name
7543  *
7544  * Extract setting and property names from prompt and/or line.
7545  */
7546 static void
7547 extract_setting_and_property (const char *prompt, const char *line,
7548                               char **setting, char **property)
7549 {
7550         char *prop = NULL;
7551         char *sett = NULL;
7552
7553         if (prompt) {
7554                 /* prompt looks like this:
7555                   "nmcli 802-1x>" or "nmcli 802-1x.pac-file>" */
7556                 const char *p1, *p2, *dot;
7557                 size_t num1, num2;
7558                 p1 = strchr (prompt, ' ');
7559                 if (p1) {
7560                         dot = strchr (++p1, '.');
7561                         if (dot) {
7562                                 p2 = dot + 1;
7563                                 num1 = strcspn (p1, ".");
7564                                 num2 = strcspn (p2, ">");
7565                                 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7566                                 prop = num2 > 0 ? g_strndup (p2, num2) : NULL;
7567                         } else {
7568                                 num1 = strcspn (p1, ">");
7569                                 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7570                         }
7571                 }
7572         }
7573
7574         if (line) {
7575                 /* line looks like this:
7576                   " set 802-1x.pac-file ..." or " set pac-file ..." */
7577                 const char *p1, *p2, *dot;
7578                 size_t n1, n2, n3, n4;
7579                 size_t num1, num2, len;
7580                 n1 = strspn  (line,    " \t");         /* white-space */
7581                 n2 = strcspn (line+n1, " \t\0") + n1;  /* command */
7582                 n3 = strspn  (line+n2, " \t")   + n2;  /* white-space */
7583                 n4 = strcspn (line+n3, " \t\0") + n3;  /* setting/property */
7584                 p1 = line + n3;
7585                 len = n4 - n3;
7586
7587                 dot = strchr (p1, '.');
7588                 if (dot && dot < p1 + len) {
7589                         p2 = dot + 1;
7590                         num1 = strcspn (p1, ".");
7591                         num2 = len > num1 + 1 ? len - num1 - 1 : 0;
7592                         sett = num1 > 0 ? g_strndup (p1, num1) : sett;
7593                         prop = num2 > 0 ? g_strndup (p2, num2) : prop;
7594                 } else {
7595                         if (!prop)
7596                                 prop = len > 0 ? g_strndup (p1, len) : NULL;
7597                 }
7598         }
7599
7600         if (setting)
7601                 *setting = sett;
7602         else
7603                 g_free (sett);
7604         if (property)
7605                 *property = prop;
7606         else
7607                 g_free (prop);
7608 }
7609
7610 static void
7611 get_setting_and_property (const char *prompt, const char *line,
7612                           NMSetting **setting_out, char**property_out)
7613 {
7614         const NameItem *valid_settings_main;
7615         const NameItem *valid_settings_slave;
7616         const char *setting_name;
7617         NMSetting *setting = NULL;
7618         char *property = NULL;
7619         char *sett = NULL, *prop = NULL;
7620         NMSettingConnection *s_con;
7621         const char *s_type = NULL;
7622         char *slv_type;
7623
7624         extract_setting_and_property (prompt, line, &sett, &prop);
7625         if (sett) {
7626                 /* Is this too much (and useless?) effort for an unlikely case? */
7627                 s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7628                 if (s_con)
7629                         s_type = nm_setting_connection_get_slave_type (s_con);
7630                 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7631
7632                 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7633                 valid_settings_slave = get_valid_settings_array (slv_type);
7634                 g_free (slv_type);
7635
7636                 setting_name = check_valid_name (sett, valid_settings_main,
7637                                                  valid_settings_slave,  NULL);
7638                 setting = nmc_setting_new_for_name (setting_name);
7639         } else
7640                 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7641
7642         if (setting && prop)
7643                 property = is_property_valid (setting, prop, NULL);
7644         else
7645                 property = g_strdup (nmc_tab_completion.property);
7646
7647         *setting_out = setting;
7648         *property_out = property;
7649
7650         g_free (sett);
7651         g_free (prop);
7652 }
7653
7654 static gboolean
7655 _get_and_check_property (const char *prompt,
7656                          const char *line,
7657                          const char **array,
7658                          const char **array_multi,
7659                          gboolean *multi)
7660 {
7661         char *prop;
7662         gboolean found = FALSE;
7663
7664         extract_setting_and_property (prompt, line, NULL, &prop);
7665         if (prop) {
7666                 if (array)
7667                         found = !!nmc_string_is_valid (prop, array, NULL);
7668                 if (array_multi && multi)
7669                         *multi = !!nmc_string_is_valid (prop, array_multi, NULL);
7670                 g_free (prop);
7671         }
7672         return found;
7673 }
7674
7675 static gboolean
7676 should_complete_files (const char *prompt, const char *line)
7677 {
7678         const char *file_properties[] = {
7679                 /* '802-1x' properties */
7680                 "ca-cert",
7681                 "ca-path",
7682                 "client-cert",
7683                 "pac-file",
7684                 "phase2-ca-cert",
7685                 "phase2-ca-path",
7686                 "phase2-client-cert",
7687                 "private-key",
7688                 "phase2-private-key",
7689                 /* 'team' and 'team-port' properties */
7690                 "config",
7691                 NULL
7692         };
7693         return _get_and_check_property (prompt, line, file_properties, NULL, NULL);
7694 }
7695
7696 static gboolean
7697 should_complete_vpn_uuids (const char *prompt, const char *line)
7698 {
7699         const char *uuid_properties[] = {
7700                 /* 'connection' properties */
7701                 "secondaries",
7702                 NULL
7703         };
7704         return _get_and_check_property (prompt, line, uuid_properties, NULL, NULL);
7705 }
7706
7707 static const char **
7708 get_allowed_property_values (void)
7709 {
7710         NMSetting *setting;
7711         char *property;
7712         const char **avals = NULL;
7713
7714         get_setting_and_property (rl_prompt, rl_line_buffer, &setting, &property);
7715         if (setting && property)
7716                 avals = nmc_setting_get_property_allowed_values (setting, property);
7717
7718         if (setting)
7719                 g_object_unref (setting);
7720         g_free (property);
7721
7722         return avals;
7723 }
7724
7725 static gboolean
7726 should_complete_property_values (const char *prompt, const char *line, gboolean *multi)
7727 {
7728         /* properties allowing multiple values */
7729         const char *multi_props[] = {
7730                 /* '802-1x' properties */
7731                 NM_SETTING_802_1X_EAP,
7732                 /* '802-11-wireless-security' properties */
7733                 NM_SETTING_WIRELESS_SECURITY_PROTO,
7734                 NM_SETTING_WIRELESS_SECURITY_PAIRWISE,
7735                 NM_SETTING_WIRELESS_SECURITY_GROUP,
7736                 /* 'bond' properties */
7737                 NM_SETTING_BOND_OPTIONS,
7738                 /* 'ethernet' properties */
7739                 NM_SETTING_WIRED_S390_OPTIONS,
7740                 NULL
7741         };
7742         _get_and_check_property (prompt, line, NULL, multi_props, multi);
7743         return get_allowed_property_values () != NULL;
7744 }
7745
7746 //FIXME: this helper should go to libnm later
7747 static gboolean
7748 _setting_property_is_boolean (NMSetting *setting, const char *property_name)
7749 {
7750         GParamSpec *pspec;
7751
7752         g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
7753         g_return_val_if_fail (property_name, FALSE);
7754
7755         pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
7756         if (pspec && pspec->value_type == G_TYPE_BOOLEAN)
7757                 return TRUE;
7758         return FALSE;
7759 }
7760
7761 static gboolean
7762 should_complete_boolean (const char *prompt, const char *line)
7763 {
7764         NMSetting *setting;
7765         char *property;
7766         gboolean is_boolean = FALSE;
7767
7768         get_setting_and_property (prompt, line, &setting, &property);
7769         if (setting && property)
7770                 is_boolean = _setting_property_is_boolean (setting, property);
7771
7772         if (setting)
7773                 g_object_unref (setting);
7774         g_free (property);
7775
7776         return is_boolean;
7777 }
7778
7779 static char *
7780 gen_property_values (const char *text, int state)
7781 {
7782         char *ret = NULL;
7783         const char **avals;
7784
7785         avals = get_allowed_property_values ();
7786         if (avals)
7787                 ret = nmc_rl_gen_func_basic (text, state, avals);
7788         return ret;
7789 }
7790
7791 /* from readline */
7792 extern int rl_complete_with_tilde_expansion;
7793
7794 /*
7795  * Attempt to complete on the contents of TEXT.  START and END show the
7796  * region of TEXT that contains the word to complete.  We can use the
7797  * entire line in case we want to do some simple parsing.  Return the
7798  * array of matches, or NULL if there aren't any.
7799  */
7800 static char **
7801 nmcli_editor_tab_completion (const char *text, int start, int end)
7802 {
7803         char **match_array = NULL;
7804         const char *line = rl_line_buffer;
7805         rl_compentry_func_t *generator_func = NULL;
7806         char *prompt_tmp;
7807         char *word = NULL;
7808         size_t n1;
7809         int num;
7810
7811         /* Restore standard append character to space */
7812         rl_completion_append_character = ' ';
7813
7814         /* Restore standard function for displaying matches */
7815         rl_completion_display_matches_hook = NULL;
7816
7817         /* Disable default filename completion */
7818         rl_attempted_completion_over = 1;
7819
7820         /* Enable tilde expansion when filenames are completed */
7821         rl_complete_with_tilde_expansion = 1;
7822
7823         /* Filter out possible ANSI color escape sequences */
7824         prompt_tmp = nmc_filter_out_colors ((const char *) rl_prompt);
7825
7826         /* Find the first non-space character */
7827         n1 = strspn (line, " \t");
7828
7829         /* Choose the right generator function */
7830         if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0)
7831                 generator_func = gen_connection_types;
7832         else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0)
7833                 generator_func = gen_setting_names;
7834         else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0)
7835                 generator_func = gen_property_names;
7836         else if (   g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
7837                  || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL)))
7838                 generator_func = gen_func_bool_values_l10n;
7839         else if (g_str_has_prefix (prompt_tmp, "nmcli")) {
7840                 if (!strchr (prompt_tmp, '.')) {
7841                         int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1;
7842                         const char *dot = strchr (line, '.');
7843                         gboolean multi;
7844
7845                         /* Main menu  - level 0,1 */
7846                         if (start == n1)
7847                                 generator_func = gen_nmcli_cmds_menu;
7848                         else {
7849                                 if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) {
7850                                         if (level == 0 && (!dot || dot >= line + end))
7851                                                 generator_func = gen_setting_names;
7852                                         else
7853                                                 generator_func = gen_property_names;
7854                                 } else if (should_complete_cmd (line, end, "set", &num, NULL)) {
7855                                         if (num < 3) {
7856                                                 if (level == 0 && (!dot || dot >= line + end)) {
7857                                                         generator_func = gen_setting_names;
7858                                                         rl_completion_append_character = '.';
7859                                                 } else
7860                                                         generator_func = gen_property_names;
7861                                         } else if (num >= 3) {
7862                                                 if (num == 3 && should_complete_files (NULL, line))
7863                                                         rl_attempted_completion_over = 0;
7864                                                 else if (should_complete_vpn_uuids (NULL, line)) {
7865                                                         rl_completion_display_matches_hook = uuid_display_hook;
7866                                                         generator_func = gen_vpn_uuids;
7867                                                 } else if (   should_complete_property_values (NULL, line, &multi)
7868                                                            && (num == 3 || multi)) {
7869                                                         generator_func = gen_property_values;
7870                                                 } else if (should_complete_boolean (NULL, line) && num == 3)
7871                                                         generator_func = gen_func_bool_values;
7872                                         }
7873                                 } else if (  (   should_complete_cmd (line, end, "remove", &num, NULL)
7874                                               || should_complete_cmd (line, end, "describe", &num, NULL))
7875                                            && num <= 2) {
7876                                         if (level == 0 && (!dot || dot >= line + end)) {
7877                                                 generator_func = gen_setting_names;
7878                                                 rl_completion_append_character = '.';
7879                                         } else
7880                                                 generator_func = gen_property_names;
7881                                 } else if (should_complete_cmd (line, end, "nmcli", &num, &word)) {
7882                                         if (num < 3)
7883                                                 generator_func = gen_cmd_nmcli;
7884                                         else if (num == 3)
7885                                                 generator_func = get_gen_func_cmd_nmcli (word);
7886                                 } else if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) {
7887                                         if (level == 0 && (!dot || dot >= line + end))
7888                                                 generator_func = gen_cmd_print0;
7889                                         else
7890                                                 generator_func = gen_property_names;
7891                                 } else if (should_complete_cmd (line, end, "verify", &num, NULL) && num <= 2) {
7892                                         generator_func = gen_cmd_verify0;
7893                                 } else if (should_complete_cmd (line, end, "activate", &num, NULL) && num <= 2) {
7894                                         generator_func = gen_compat_devices;
7895                                 } else if (should_complete_cmd (line, end, "save", &num, NULL) && num <= 2) {
7896                                         generator_func = gen_cmd_save;
7897                                 } else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7898                                         generator_func = gen_nmcli_cmds_menu;
7899                         }
7900                 } else {
7901                         /* Submenu - level 2 */
7902                         if (start == n1)
7903                                 generator_func = gen_nmcli_cmds_submenu;
7904                         else {
7905                                 gboolean multi;
7906
7907                                 if (   should_complete_cmd (line, end, "add", &num, NULL)
7908                                     || should_complete_cmd (line, end, "set", &num, NULL)) {
7909                                         if (num <= 2 && should_complete_files (prompt_tmp, line))
7910                                                 rl_attempted_completion_over = 0;
7911                                         else if (should_complete_vpn_uuids (prompt_tmp, line)) {
7912                                                 rl_completion_display_matches_hook = uuid_display_hook;
7913                                                 generator_func = gen_vpn_uuids;
7914                                         } else if (   should_complete_property_values (prompt_tmp, NULL, &multi)
7915                                                    && (num <= 2 || multi)) {
7916                                                 generator_func = gen_property_values;
7917                                         } else if (should_complete_boolean (prompt_tmp, NULL) && num <= 2)
7918                                                 generator_func = gen_func_bool_values;
7919                                 }
7920                                 if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2)
7921                                         generator_func = gen_cmd_print2;
7922                                 else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7923                                         generator_func = gen_nmcli_cmds_submenu;
7924                         }
7925                 }
7926         }
7927
7928         if (generator_func)
7929                 match_array = rl_completion_matches (text, generator_func);
7930
7931         g_free (prompt_tmp);
7932         g_free (word);
7933         return match_array;
7934 }
7935
7936 #define NMCLI_EDITOR_HISTORY ".nmcli-history"
7937
7938 static void
7939 load_history_cmds (const char *uuid)
7940 {
7941         GKeyFile *kf;
7942         char *filename;
7943         char **keys;
7944         char *line;
7945         size_t i;
7946         GError *err = NULL;
7947
7948         filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7949         kf = g_key_file_new ();
7950         if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7951                 if (g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE))
7952                         g_print ("Warning: %s parse error: %s\n", filename, err->message);
7953                 g_key_file_free (kf);
7954                 g_free (filename);
7955                 return;
7956         }
7957         keys = g_key_file_get_keys (kf, uuid, NULL, NULL);
7958         for (i = 0; keys && keys[i]; i++) {
7959                 line = g_key_file_get_string (kf, uuid, keys[i], NULL);
7960                 if (line && *line)
7961                         add_history (line);
7962                 g_free (line);
7963         }
7964         g_strfreev (keys);
7965         g_key_file_free (kf);
7966         g_free (filename);
7967 }
7968
7969 static void
7970 save_history_cmds (const char *uuid)
7971 {
7972         HIST_ENTRY **hist = NULL;
7973         GKeyFile *kf;
7974         char *filename;
7975         size_t i;
7976         char *key;
7977         char *data;
7978         gsize len = 0;
7979         GError *err = NULL;
7980
7981         hist = history_list ();
7982         if (hist) {
7983                 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7984                 kf = g_key_file_new ();
7985                 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7986                         if (   !g_error_matches (err, G_FILE_ERROR, G_FILE_ERROR_NOENT)
7987                             && !g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
7988                                 g_print ("Warning: %s parse error: %s\n", filename, err->message);
7989                                 g_key_file_free (kf);
7990                                 g_free (filename);
7991                                 g_clear_error (&err);
7992                                 return;
7993                         }
7994                         g_clear_error (&err);
7995                 }
7996
7997                 /* Remove previous history group and save new history entries */
7998                 g_key_file_remove_group (kf, uuid, NULL);
7999                 for (i = 0; hist[i]; i++)
8000                 {
8001                         key = g_strdup_printf ("%zd", i);
8002                         g_key_file_set_string (kf, uuid, key, hist[i]->line);
8003                         g_free (key);
8004                 }
8005
8006                 /* Write history to file */
8007                 data = g_key_file_to_data (kf, &len, NULL);
8008                 if (data) {
8009                         g_file_set_contents (filename, data, len, NULL);
8010                         g_free (data);
8011                 }
8012                 g_key_file_free (kf);
8013                 g_free (filename);
8014         }
8015 }
8016
8017 /*----------------------------------------------------------------------------*/
8018
8019 static void
8020 editor_show_connection (NMConnection *connection, NmCli *nmc)
8021 {
8022         nmc->print_output = NMC_PRINT_PRETTY;
8023         nmc->multiline_output = TRUE;
8024         nmc->escape_values = 0;
8025
8026         /* Remove any previous data */
8027         nmc_empty_output_fields (nmc);
8028
8029         nmc_connection_profile_details (connection, nmc, nmc->editor_show_secrets);
8030 }
8031
8032 static void
8033 editor_show_setting (NMSetting *setting, NmCli *nmc)
8034 {
8035         g_print (_("['%s' setting values]\n"),
8036                  nm_setting_get_name (setting));
8037
8038         nmc->print_output = NMC_PRINT_NORMAL;
8039         nmc->multiline_output = TRUE;
8040         nmc->escape_values = 0;
8041
8042         /* Remove any previous data */
8043         nmc_empty_output_fields (nmc);
8044
8045         setting_details (setting, nmc, NULL, nmc->editor_show_secrets);
8046 }
8047
8048 typedef enum {
8049         NMC_EDITOR_MAIN_CMD_UNKNOWN = 0,
8050         NMC_EDITOR_MAIN_CMD_GOTO,
8051         NMC_EDITOR_MAIN_CMD_REMOVE,
8052         NMC_EDITOR_MAIN_CMD_SET,
8053         NMC_EDITOR_MAIN_CMD_DESCRIBE,
8054         NMC_EDITOR_MAIN_CMD_PRINT,
8055         NMC_EDITOR_MAIN_CMD_VERIFY,
8056         NMC_EDITOR_MAIN_CMD_SAVE,
8057         NMC_EDITOR_MAIN_CMD_ACTIVATE,
8058         NMC_EDITOR_MAIN_CMD_BACK,
8059         NMC_EDITOR_MAIN_CMD_HELP,
8060         NMC_EDITOR_MAIN_CMD_NMCLI,
8061         NMC_EDITOR_MAIN_CMD_QUIT,
8062 } NmcEditorMainCmd;
8063
8064 static NmcEditorMainCmd
8065 parse_editor_main_cmd (const char *cmd, char **cmd_arg)
8066 {
8067         NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN;
8068         char **vec;
8069
8070         vec = nmc_strsplit_set (cmd, " \t", 2);
8071         if (g_strv_length (vec) < 1) {
8072                 if (cmd_arg)
8073                         *cmd_arg = NULL;
8074                 return NMC_EDITOR_MAIN_CMD_UNKNOWN;
8075         }
8076
8077         if (matches (vec[0], "goto") == 0)
8078                 editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO;
8079         else if (matches (vec[0], "remove") == 0)
8080                 editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE;
8081         else if (matches (vec[0], "set") == 0)
8082                 editor_cmd = NMC_EDITOR_MAIN_CMD_SET;
8083         else if (matches (vec[0], "describe") == 0)
8084                 editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE;
8085         else if (matches (vec[0], "print") == 0)
8086                 editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT;
8087         else if (matches (vec[0], "verify") == 0)
8088                 editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY;
8089         else if (matches (vec[0], "save") == 0)
8090                 editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE;
8091         else if (matches (vec[0], "activate") == 0)
8092                 editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE;
8093         else if (matches (vec[0], "back") == 0)
8094                 editor_cmd = NMC_EDITOR_MAIN_CMD_BACK;
8095         else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8096                 editor_cmd = NMC_EDITOR_MAIN_CMD_HELP;
8097         else if (matches (vec[0], "quit") == 0)
8098                 editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT;
8099         else if (matches (vec[0], "nmcli") == 0)
8100                 editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI;
8101
8102         /* set pointer to command argument */
8103         if (cmd_arg)
8104                 *cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL;
8105
8106         g_strfreev (vec);
8107         return editor_cmd;
8108 }
8109
8110 static void
8111 editor_main_usage (void)
8112 {
8113         g_print ("------------------------------------------------------------------------------\n");
8114         /* TRANSLATORS: do not translate command names and keywords before ::
8115          *              However, you should translate terms enclosed in <>.
8116          */
8117         g_print (_("---[ Main menu ]---\n"
8118                    "goto     [<setting> | <prop>]        :: go to a setting or property\n"
8119                    "remove   <setting>[.<prop>] | <prop> :: remove setting or reset property value\n"
8120                    "set      [<setting>.<prop> <value>]  :: set property value\n"
8121                    "describe [<setting>.<prop>]          :: describe property\n"
8122                    "print    [all | <setting>[.<prop>]]  :: print the connection\n"
8123                    "verify   [all | fix]                 :: verify the connection\n"
8124                    "save     [persistent|temporary]      :: save the connection\n"
8125                    "activate [<ifname>] [/<ap>|<nsp>]    :: activate the connection\n"
8126                    "back                                 :: go one level up (back)\n"
8127                    "help/?   [<command>]                 :: print this help\n"
8128                    "nmcli    <conf-option> <value>       :: nmcli configuration\n"
8129                    "quit                                 :: exit nmcli\n"));
8130         g_print ("------------------------------------------------------------------------------\n");
8131 }
8132
8133 static void
8134 editor_main_help (const char *command)
8135 {
8136         if (!command)
8137                 editor_main_usage ();
8138         else {
8139                 /* detailed command descriptions */
8140                 NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL);
8141
8142                 switch (cmd) {
8143                 case NMC_EDITOR_MAIN_CMD_GOTO:
8144                         g_print (_("goto <setting>[.<prop>] | <prop>  :: enter setting/property for editing\n\n"
8145                                    "This command enters into a setting or property for editing it.\n\n"
8146                                    "Examples: nmcli> goto connection\n"
8147                                    "          nmcli connection> goto secondaries\n"
8148                                    "          nmcli> goto ipv4.addresses\n"));
8149                         break;
8150                 case NMC_EDITOR_MAIN_CMD_REMOVE:
8151                         g_print (_("remove <setting>[.<prop>]  :: remove setting or reset property value\n\n"
8152                                    "This command removes an entire setting from the connection, or if a property\n"
8153                                    "is given, resets that property to the default value.\n\n"
8154                                    "Examples: nmcli> remove wifi-sec\n"
8155                                    "          nmcli> remove eth.mtu\n"));
8156                         break;
8157                 case NMC_EDITOR_MAIN_CMD_SET:
8158                         g_print (_("set [<setting>.<prop> <value>]  :: set property value\n\n"
8159                                    "This command sets property value.\n\n"
8160                                    "Example: nmcli> set con.id My connection\n"));
8161                         break;
8162                 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
8163                         g_print (_("describe [<setting>.<prop>]  :: describe property\n\n"
8164                                    "Shows property description. You can consult nm-settings(5) "
8165                                    "manual page to see all NM settings and properties.\n"));
8166                         break;
8167                 case NMC_EDITOR_MAIN_CMD_PRINT:
8168                         g_print (_("print [all]  :: print setting or connection values\n\n"
8169                                    "Shows current property or the whole connection.\n\n"
8170                                    "Example: nmcli ipv4> print all\n"));
8171                         break;
8172                 case NMC_EDITOR_MAIN_CMD_VERIFY:
8173                         g_print (_("verify [all | fix]  :: verify setting or connection validity\n\n"
8174                                    "Verifies whether the setting or connection is valid and can be saved later.\n"
8175                                    "It indicates invalid values on error. Some errors may be fixed automatically\n"
8176                                    "by 'fix' option.\n\n"
8177                                    "Examples: nmcli> verify\n"
8178                                    "          nmcli> verify fix\n"
8179                                    "          nmcli bond> verify\n"));
8180                         break;
8181                 case NMC_EDITOR_MAIN_CMD_SAVE:
8182                         g_print (_("save [persistent|temporary]  :: save the connection\n\n"
8183                                    "Sends the connection profile to NetworkManager that either will save it\n"
8184                                    "persistently, or will only keep it in memory. 'save' without an argument\n"
8185                                    "means 'save persistent'.\n"
8186                                    "Note that once you save the profile persistently those settings are saved\n"
8187                                    "across reboot or restart. Subsequent changes can also be temporary or\n"
8188                                    "persistent, but any temporary changes will not persist across reboot or\n"
8189                                    "restart. If you want to fully remove the persistent connection, the connection\n"
8190                                    "profile must be deleted.\n"));
8191                         break;
8192                 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
8193                         g_print (_("activate [<ifname>] [/<ap>|<nsp>]  :: activate the connection\n\n"
8194                                    "Activates the connection.\n\n"
8195                                    "Available options:\n"
8196                                    "<ifname>    - device the connection will be activated on\n"
8197                                    "/<ap>|<nsp> - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when <ifname> is not specified)\n"));
8198                         break;
8199                 case NMC_EDITOR_MAIN_CMD_BACK:
8200                         g_print (_("back  :: go to upper menu level\n\n"));
8201                         break;
8202                 case NMC_EDITOR_MAIN_CMD_HELP:
8203                         g_print (_("help/? [<command>]  :: help for the nmcli commands\n\n"));
8204                         break;
8205                 case NMC_EDITOR_MAIN_CMD_NMCLI:
8206                         g_print (_("nmcli [<conf-option> <value>]  :: nmcli configuration\n\n"
8207                                    "Configures nmcli. The following options are available:\n"
8208                                    "status-line yes | no          [default: no]\n"
8209                                    "save-confirmation yes | no    [default: yes]\n"
8210                                    "show-secrets yes | no         [default: no]\n"
8211                                    "prompt-color <color> | <0-8>  [default: 0]\n"
8212                                    "%s"  /* color table description */
8213                                    "\n"
8214                                    "Examples: nmcli> nmcli status-line yes\n"
8215                                    "          nmcli> nmcli save-confirmation no\n"
8216                                    "          nmcli> nmcli prompt-color 3\n"),
8217                                    "  0 = normal\n"
8218                                    "  1 = \33[30mblack\33[0m\n"
8219                                    "  2 = \33[31mred\33[0m\n"
8220                                    "  3 = \33[32mgreen\33[0m\n"
8221                                    "  4 = \33[33myellow\33[0m\n"
8222                                    "  5 = \33[34mblue\33[0m\n"
8223                                    "  6 = \33[35mmagenta\33[0m\n"
8224                                    "  7 = \33[36mcyan\33[0m\n"
8225                                    "  8 = \33[37mwhite\33[0m\n");
8226                         break;
8227                 case NMC_EDITOR_MAIN_CMD_QUIT:
8228                         g_print (_("quit  :: exit nmcli\n\n"
8229                                    "This command exits nmcli. When the connection being edited "
8230                                    "is not saved, the user is asked to confirm the action.\n"));
8231                         break;
8232                 default:
8233                         g_print (_("Unknown command: '%s'\n"), command);
8234                         break;
8235                 }
8236         }
8237 }
8238
8239 typedef enum {
8240         NMC_EDITOR_SUB_CMD_UNKNOWN = 0,
8241         NMC_EDITOR_SUB_CMD_SET,
8242         NMC_EDITOR_SUB_CMD_ADD,
8243         NMC_EDITOR_SUB_CMD_CHANGE,
8244         NMC_EDITOR_SUB_CMD_REMOVE,
8245         NMC_EDITOR_SUB_CMD_DESCRIBE,
8246         NMC_EDITOR_SUB_CMD_PRINT,
8247         NMC_EDITOR_SUB_CMD_BACK,
8248         NMC_EDITOR_SUB_CMD_HELP,
8249         NMC_EDITOR_SUB_CMD_QUIT
8250 } NmcEditorSubCmd;
8251
8252 static NmcEditorSubCmd
8253 parse_editor_sub_cmd (const char *cmd, char **cmd_arg)
8254 {
8255         NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN;
8256         char **vec;
8257
8258         vec = nmc_strsplit_set (cmd, " \t", 2);
8259         if (g_strv_length (vec) < 1) {
8260                 if (cmd_arg)
8261                         *cmd_arg = NULL;
8262                 return NMC_EDITOR_SUB_CMD_UNKNOWN;
8263         }
8264
8265         if (matches (vec[0], "set") == 0)
8266                 editor_cmd = NMC_EDITOR_SUB_CMD_SET;
8267         else if (matches (vec[0], "add") == 0)
8268                 editor_cmd = NMC_EDITOR_SUB_CMD_ADD;
8269         else if (matches (vec[0], "change") == 0)
8270                 editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE;
8271         else if (matches (vec[0], "remove") == 0)
8272                 editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE;
8273         else if (matches (vec[0], "describe") == 0)
8274                 editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE;
8275         else if (matches (vec[0], "print") == 0)
8276                 editor_cmd = NMC_EDITOR_SUB_CMD_PRINT;
8277         else if (matches (vec[0], "back") == 0)
8278                 editor_cmd = NMC_EDITOR_SUB_CMD_BACK;
8279         else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8280                 editor_cmd = NMC_EDITOR_SUB_CMD_HELP;
8281         else if (matches (vec[0], "quit") == 0)
8282                 editor_cmd = NMC_EDITOR_SUB_CMD_QUIT;
8283
8284         /* set pointer to command argument */
8285         if (cmd_arg)
8286                 *cmd_arg = g_strdup (vec[1]);
8287
8288         g_strfreev (vec);
8289         return editor_cmd;
8290 }
8291
8292 static void
8293 editor_sub_help (void)
8294 {
8295         g_print ("------------------------------------------------------------------------------\n");
8296         /* TRANSLATORS: do not translate command names and keywords before ::
8297          *              However, you should translate terms enclosed in <>.
8298          */
8299         g_print (_("---[ Property menu ]---\n"
8300                    "set      [<value>]               :: set new value\n"
8301                    "add      [<value>]               :: add new option to the property\n"
8302                    "change                           :: change current value\n"
8303                    "remove   [<index> | <option>]    :: delete the value\n"
8304                    "describe                         :: describe property\n"
8305                    "print    [setting | connection]  :: print property (setting/connection) value(s)\n"
8306                    "back                             :: go to upper level\n"
8307                    "help/?   [<command>]             :: print this help or command description\n"
8308                    "quit                             :: exit nmcli\n"));
8309         g_print ("------------------------------------------------------------------------------\n");
8310 }
8311
8312 static void
8313 editor_sub_usage (const char *command)
8314 {
8315
8316         if (!command)
8317                 editor_sub_help ();
8318         else {
8319                 /* detailed command descriptions */
8320                 NmcEditorSubCmd cmdsub = parse_editor_sub_cmd (command, NULL);
8321
8322                 switch (cmdsub) {
8323                 case NMC_EDITOR_SUB_CMD_SET:
8324                         g_print (_("set [<value>]  :: set new value\n\n"
8325                                    "This command sets provided <value> to this property\n"));
8326                         break;
8327                 case NMC_EDITOR_SUB_CMD_ADD:
8328                         g_print (_("add [<value>]  :: append new value to the property\n\n"
8329                                    "This command adds provided <value> to this property, if "
8330                                    "the property is of a container type. For single-valued "
8331                                    "properties the property value is replaced (same as 'set').\n"));
8332                         break;
8333                 case NMC_EDITOR_SUB_CMD_CHANGE:
8334                         g_print (_("change  :: change current value\n\n"
8335                                    "Displays current value and allows editing it.\n"));
8336                         break;
8337                 case NMC_EDITOR_SUB_CMD_REMOVE:
8338                         g_print (_("remove [<value>|<index>|<option name>]  :: delete the value\n\n"
8339                                    "Removes the property value. For single-valued properties, this sets the\n"
8340                                    "property back to its default value. For container-type properties, this removes\n"
8341                                    "all the values of that property, or you can specify an argument to remove just\n"
8342                                    "a single item or option. The argument is either a value or index of the item to\n"
8343                                    "remove, or an option name (for properties with named options).\n\n"
8344                                    "Examples: nmcli ipv4.dns> remove 8.8.8.8\n"
8345                                    "          nmcli ipv4.dns> remove 2\n"
8346                                    "          nmcli bond.options> remove downdelay\n\n"));
8347                         break;
8348                 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8349                         g_print (_("describe  :: describe property\n\n"
8350                                    "Shows property description. You can consult nm-settings(5) "
8351                                    "manual page to see all NM settings and properties.\n"));
8352                         break;
8353                 case NMC_EDITOR_SUB_CMD_PRINT:
8354                         g_print (_("print [property|setting|connection]  :: print property (setting, connection) value(s)\n\n"
8355                                    "Shows property value. Providing an argument you can also display "
8356                                    "values for the whole setting or connection.\n"));
8357                         break;
8358                 case NMC_EDITOR_SUB_CMD_BACK:
8359                         g_print (_("back  :: go to upper menu level\n\n"));
8360                         break;
8361                 case NMC_EDITOR_SUB_CMD_HELP:
8362                         g_print (_("help/? [<command>]  :: help for nmcli commands\n\n"));
8363                         break;
8364                 case NMC_EDITOR_SUB_CMD_QUIT:
8365                         g_print (_("quit  :: exit nmcli\n\n"
8366                                    "This command exits nmcli. When the connection being edited "
8367                                    "is not saved, the user is asked to confirm the action.\n"));
8368                         break;
8369                 default:
8370                         g_print (_("Unknown command: '%s'\n"), command);
8371                         break;
8372                 }
8373         }
8374 }
8375
8376 /*----------------------------------------------------------------------------*/
8377
8378 typedef struct {
8379         NMDevice *device;
8380         NMActiveConnection *ac;
8381         guint monitor_id;
8382 } MonitorACInfo;
8383
8384 static gboolean nmc_editor_cb_called;
8385 static GError *nmc_editor_error;
8386 static MonitorACInfo *nmc_editor_monitor_ac;
8387 static GMutex nmc_editor_mutex;
8388 static GCond nmc_editor_cond;
8389
8390 /*
8391  * Store 'error' to shared 'nmc_editor_error' and monitoring info to
8392  * 'nmc_editor_monitor_ac' and signal the condition so that
8393  * the 'editor-thread' thread could process that.
8394  */
8395 static void
8396 set_info_and_signal_editor_thread (GError *error, MonitorACInfo *monitor_ac_info)
8397 {
8398         g_mutex_lock (&nmc_editor_mutex);
8399         nmc_editor_cb_called = TRUE;
8400         nmc_editor_error = error ? g_error_copy (error) : NULL;
8401         nmc_editor_monitor_ac = monitor_ac_info;
8402         g_cond_signal (&nmc_editor_cond);
8403         g_mutex_unlock (&nmc_editor_mutex);
8404 }
8405
8406 static void
8407 add_connection_editor_cb (GObject *client,
8408                           GAsyncResult *result,
8409                           gpointer user_data)
8410 {
8411         NMRemoteConnection *connection;
8412         GError *error = NULL;
8413
8414         connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
8415         set_info_and_signal_editor_thread (error, NULL);
8416
8417         g_clear_object (&connection);
8418         g_clear_error (&error);
8419 }
8420
8421 static void
8422 update_connection_editor_cb (GObject *connection,
8423                              GAsyncResult *result,
8424                              gpointer user_data)
8425 {
8426         GError *error = NULL;
8427
8428         nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
8429                                                     result, &error);
8430         set_info_and_signal_editor_thread (error, NULL);
8431         g_clear_error (&error);
8432 }
8433
8434 static gboolean
8435 progress_activation_editor_cb (gpointer user_data)
8436 {
8437         MonitorACInfo *info = (MonitorACInfo *) user_data;
8438         NMDevice *device = info->device;
8439         NMActiveConnection *ac = info->ac;
8440         NMActiveConnectionState ac_state;
8441         NMDeviceState dev_state;
8442
8443         if (!device || !ac)
8444                 goto finish;
8445
8446         ac_state = nm_active_connection_get_state (ac);
8447         dev_state = nm_device_get_state (device);
8448
8449         nmc_terminal_show_progress (nmc_device_state_to_string (dev_state));
8450
8451         if (   ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
8452             || dev_state == NM_DEVICE_STATE_ACTIVATED) {
8453                 nmc_terminal_erase_line ();
8454                 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
8455                          nm_object_get_path (NM_OBJECT (ac)));
8456                 goto finish; /* we are done */
8457         } else if (   ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
8458                    || dev_state == NM_DEVICE_STATE_FAILED) {
8459                 nmc_terminal_erase_line ();
8460                 g_print (_("Error: Connection activation failed.\n"));
8461                 goto finish; /* we are done */
8462         }
8463
8464         return TRUE;
8465
8466 finish:
8467         if (device)
8468                 g_object_unref (device);
8469         if (ac)
8470                 g_object_unref (ac);
8471         return FALSE;
8472 }
8473
8474 static void
8475 activate_connection_editor_cb (GObject *client,
8476                                GAsyncResult *result,
8477                                gpointer user_data)
8478 {
8479         ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
8480         NMDevice *device = info->device;
8481         const GPtrArray *ac_devs;
8482         MonitorACInfo *monitor_ac_info = NULL;
8483         NMActiveConnection *active;
8484         GError *error = NULL;
8485
8486         active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
8487
8488         if (!error) {
8489                 if (!device) {
8490                         ac_devs = nm_active_connection_get_devices (active);
8491                         device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
8492                 }
8493                 if (device) {
8494                         monitor_ac_info = g_malloc0 (sizeof (AddConnectionInfo));
8495                         monitor_ac_info->device = g_object_ref (device);
8496                         monitor_ac_info->ac = active;
8497                         monitor_ac_info->monitor_id = g_timeout_add (120, progress_activation_editor_cb, monitor_ac_info);
8498                 } else
8499                         g_object_unref (active);
8500         }
8501         set_info_and_signal_editor_thread (error, monitor_ac_info);
8502         g_clear_error (&error);
8503 }
8504
8505 /*----------------------------------------------------------------------------*/
8506
8507 static void
8508 print_property_description (NMSetting *setting, const char *prop_name)
8509 {
8510         char *desc;
8511
8512         desc = nmc_setting_get_property_desc (setting, prop_name);
8513         g_print ("\n=== [%s] ===\n%s\n", prop_name, desc);
8514         g_free (desc);
8515 }
8516
8517 static void
8518 print_setting_description (NMSetting *setting)
8519 {
8520         /* Show description of all properties */
8521         char **all_props;
8522         int i;
8523
8524         all_props = nmc_setting_get_valid_properties (setting);
8525         g_print (("<<< %s >>>\n"), nm_setting_get_name (setting));
8526         for (i = 0; all_props && all_props[i]; i++)
8527                 print_property_description (setting, all_props[i]);
8528         g_strfreev (all_props);
8529 }
8530
8531 static gboolean
8532 connection_remove_setting (NMConnection *connection, NMSetting *setting)
8533 {
8534         gboolean mandatory;
8535
8536         g_return_val_if_fail (setting, FALSE);
8537
8538         mandatory = is_setting_mandatory (connection, setting);
8539         if (!mandatory) {
8540                 nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting));
8541                 return TRUE;
8542         }
8543         g_print (_("Error: setting '%s' is mandatory and cannot be removed.\n"),
8544                  nm_setting_get_name (setting));
8545         return FALSE;
8546 }
8547
8548 static void
8549 editor_show_status_line (NMConnection *connection, gboolean dirty, gboolean temp)
8550 {
8551         NMSettingConnection *s_con;
8552         const char *con_type, *con_id, *con_uuid;
8553
8554         s_con = nm_connection_get_setting_connection (connection);
8555         g_assert (s_con);
8556         con_type = nm_setting_connection_get_connection_type (s_con);
8557         con_id = nm_connection_get_id (connection);
8558         con_uuid = nm_connection_get_uuid (connection);
8559
8560         /* TRANSLATORS: status line in nmcli connection editor */
8561         g_print (_("[ Type: %s | Name: %s | UUID: %s | Dirty: %s | Temp: %s ]\n"),
8562                  con_type, con_id, con_uuid,
8563                  dirty ? _("yes") : _("no"),
8564                  temp ? _("yes") : _("no"));
8565 }
8566
8567 static gboolean
8568 refresh_remote_connection (GWeakRef *weak, NMRemoteConnection **remote)
8569 {
8570         gboolean previous;
8571
8572         g_return_val_if_fail (remote != NULL, FALSE);
8573
8574         previous = (*remote != NULL);
8575         if (*remote)
8576                 g_object_unref (*remote);
8577         *remote = g_weak_ref_get (weak);
8578
8579         return (previous && !*remote);
8580 }
8581
8582 static gboolean
8583 is_connection_dirty (NMConnection *connection, NMRemoteConnection *remote)
8584 {
8585         return !nm_connection_compare (connection,
8586                                        remote ? NM_CONNECTION (remote) : NULL,
8587                                        NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS |
8588                                        NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP);
8589 }
8590
8591 static gboolean
8592 confirm_quit (void)
8593 {
8594         char *answer;
8595         gboolean want_quit = FALSE;
8596
8597         answer = nmc_readline (_("The connection is not saved. "
8598                                  "Do you really want to quit? %s"),
8599                                prompt_yes_no (FALSE, NULL));
8600         answer = answer ? g_strstrip (answer) : NULL;
8601         if (answer && matches (answer, WORD_LOC_YES) == 0)
8602                 want_quit = TRUE;
8603
8604         g_free (answer);
8605         return want_quit;
8606 }
8607
8608 /*
8609  * Submenu for detailed property editing
8610  * Return: TRUE - continue;  FALSE - should quit
8611  */
8612 static gboolean
8613 property_edit_submenu (NmCli *nmc,
8614                        NMConnection *connection,
8615                        NMRemoteConnection **rem_con,
8616                        GWeakRef *rem_con_weak,
8617                        NMSetting *curr_setting,
8618                        const char *prop_name)
8619 {
8620         NmcEditorSubCmd cmdsub;
8621         gboolean cmd_property_loop = TRUE;
8622         gboolean should_quit = FALSE;
8623         char *prop_val_user;
8624         gboolean set_result;
8625         GError *tmp_err = NULL;
8626         char *prompt;
8627         gboolean dirty;
8628         GValue prop_g_value = G_VALUE_INIT;
8629         gboolean temp_changes;
8630         gboolean removed;
8631
8632         /* Set global variable for use in TAB completion */
8633         nmc_tab_completion.property = prop_name;
8634
8635         prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
8636                                "nmcli %s.%s> ",
8637                                nm_setting_get_name (curr_setting), prop_name);
8638
8639         while (cmd_property_loop) {
8640                 char *cmd_property_user;
8641                 char *cmd_property_arg;
8642
8643                 /* Get the remote connection again, it may have disapeared */
8644                 removed = refresh_remote_connection (rem_con_weak, rem_con);
8645                 if (removed)
8646                         g_print (_("The connection profile has been removed from another client. "
8647                                    "You may type 'save' in the main menu to restore it.\n"));
8648
8649                 /* Connection is dirty? (not saved or differs from the saved) */
8650                 dirty = is_connection_dirty (connection, *rem_con);
8651                 temp_changes = *rem_con ? nm_remote_connection_get_unsaved (*rem_con) : TRUE;
8652                 if (nmc->editor_status_line)
8653                         editor_show_status_line (connection, dirty, temp_changes);
8654
8655                 cmd_property_user = nmc_readline ("%s", prompt);
8656                 if (!cmd_property_user || *cmd_property_user == '\0')
8657                         continue;
8658                 cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg);
8659
8660                 switch (cmdsub) {
8661                 case NMC_EDITOR_SUB_CMD_SET:
8662                 case NMC_EDITOR_SUB_CMD_ADD:
8663                         /* list, arrays,...: SET replaces the whole property value
8664                          *                   ADD adds the new value(s)
8665                          * single values:  : both SET and ADD sets the new value
8666                          */
8667                         if (!cmd_property_arg) {
8668                                 const char **avals = nmc_setting_get_property_allowed_values (curr_setting, prop_name);
8669                                 if (avals) {
8670                                         char *avals_str = nmc_util_strv_for_display (avals, FALSE);
8671                                         g_print (_("Allowed values for '%s' property: %s\n"),
8672                                                  prop_name, avals_str);
8673                                         g_free (avals_str);
8674                                 }
8675                                 prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
8676                         } else
8677                                 prop_val_user = g_strdup (cmd_property_arg);
8678
8679                         /* nmc_setting_set_property() only adds new value, thus we have to
8680                          * remove the original value and save it for error cases.
8681                          */
8682                         if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8683                                 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8684                                 nmc_property_set_default_value (curr_setting, prop_name);
8685                         }
8686
8687                         set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err);
8688                         g_free (prop_val_user);
8689                         if (!set_result) {
8690                                 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8691                                 g_clear_error (&tmp_err);
8692                                 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8693                                         /* Block change signals and restore original value */
8694                                         g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8695                                         nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8696                                         g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8697                                 }
8698                         }
8699                         if (G_IS_VALUE (&prop_g_value))
8700                                 g_value_unset (&prop_g_value);
8701                         break;
8702
8703                 case NMC_EDITOR_SUB_CMD_CHANGE:
8704                         rl_startup_hook = nmc_rl_set_deftext;
8705                         nmc_rl_pre_input_deftext = nmc_setting_get_property_parsable (curr_setting, prop_name, NULL);
8706                         prop_val_user = nmc_readline (_("Edit '%s' value: "), prop_name);
8707
8708                         nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8709                         nmc_property_set_default_value (curr_setting, prop_name);
8710
8711                         if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) {
8712                                 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8713                                 g_clear_error (&tmp_err);
8714                                 g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8715                                 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8716                                 g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8717                         }
8718                         g_free (prop_val_user);
8719                         if (G_IS_VALUE (&prop_g_value))
8720                                 g_value_unset (&prop_g_value);
8721                         break;
8722
8723                 case NMC_EDITOR_SUB_CMD_REMOVE:
8724                         if (cmd_property_arg) {
8725                                 unsigned long val_int = G_MAXUINT32;
8726                                 char *option = NULL;
8727
8728                                 if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int))
8729                                         option = g_strdup (cmd_property_arg);
8730
8731                                 if (!nmc_setting_remove_property_option (curr_setting, prop_name,
8732                                                                          option ? g_strstrip (option) : NULL,
8733                                                                          (guint32) val_int,
8734                                                                          &tmp_err)) {
8735                                         g_print (_("Error: %s\n"), tmp_err->message);
8736                                         g_clear_error (&tmp_err);
8737                                 }
8738                                 g_free (option);
8739                         } else {
8740                                 if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) {
8741                                         g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
8742                                                  tmp_err->message);
8743                                         g_clear_error (&tmp_err);
8744                                 }
8745                         }
8746                         break;
8747
8748                 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8749                         /* Show property description */
8750                         print_property_description (curr_setting, prop_name);
8751                         break;
8752
8753                 case NMC_EDITOR_SUB_CMD_PRINT:
8754                         /* Print current connection settings/properties */
8755                         if (cmd_property_arg) {
8756                                 if (matches (cmd_property_arg, "setting") == 0)
8757                                         editor_show_setting (curr_setting, nmc);
8758                                 else if (   matches (cmd_property_arg, "connection") == 0
8759                                          || matches (cmd_property_arg, "all") == 0)
8760                                         editor_show_connection (connection, nmc);
8761                                 else
8762                                         g_print (_("Unknown command argument: '%s'\n"), cmd_property_arg);
8763                         } else {
8764                                 char *prop_val =  nmc_setting_get_property (curr_setting, prop_name, NULL);
8765                                 g_print ("%s: %s\n", prop_name, prop_val);
8766                                 g_free (prop_val);
8767                         }
8768                         break;
8769
8770                 case NMC_EDITOR_SUB_CMD_BACK:
8771                         /* Set global variable for use in TAB completion */
8772                         nmc_tab_completion.property = NULL;
8773                         cmd_property_loop = FALSE;
8774                         break;
8775
8776                 case NMC_EDITOR_SUB_CMD_HELP:
8777                         editor_sub_usage (cmd_property_arg);
8778                         break;
8779
8780                 case NMC_EDITOR_SUB_CMD_QUIT:
8781                         if (is_connection_dirty (connection, *rem_con)) {
8782                                 if (confirm_quit ()) {
8783                                         cmd_property_loop = FALSE;
8784                                         should_quit = TRUE;  /* we will quit nmcli */
8785                                 }
8786                         } else {
8787                                 cmd_property_loop = FALSE;
8788                                 should_quit = TRUE;  /* we will quit nmcli */
8789                         }
8790                         break;
8791
8792                 case NMC_EDITOR_SUB_CMD_UNKNOWN:
8793                 default:
8794                         g_print (_("Unknown command: '%s'\n"), cmd_property_user);
8795                         break;
8796                 }
8797                 g_free (cmd_property_user);
8798                 g_free (cmd_property_arg);
8799         }
8800         g_free (prompt);
8801
8802         return !should_quit;
8803 }
8804
8805 /*
8806  * Split 'str' in the following format:  [[[setting.]property] [value]]
8807  * and return the components in 'setting', 'property' and 'value'
8808  * Use g_free() to deallocate the returned strings.
8809  */
8810 static void
8811 split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value)
8812 {
8813         char **args, **items;
8814
8815         if (!str)
8816                 return;
8817
8818         args = nmc_strsplit_set (str, " \t", 2);
8819         if (args[0]) {
8820                 items = nmc_strsplit_set (args[0], ".", 2);
8821                 if (g_strv_length (items) == 2) {
8822                         if (setting)
8823                                 *setting = g_strdup (items[0]);
8824                         if (property)
8825                                 *property = g_strdup (items[1]);
8826                 } else {
8827                         if (property)
8828                                 *property = g_strdup (items[0]);
8829                 }
8830                 g_strfreev (items);
8831
8832                 if (value && args[1])
8833                         *value = g_strstrip (g_strdup (args[1]));
8834         }
8835         g_strfreev (args);
8836 }
8837
8838 static NMSetting *
8839 create_setting_by_name (const char *name, const NameItem *valid_settings_main, const NameItem *valid_settings_slave)
8840 {
8841         const char *setting_name;
8842         NMSetting *setting = NULL;
8843
8844         /* Get a valid setting name */
8845         setting_name = check_valid_name (name, valid_settings_main, valid_settings_slave, NULL);
8846
8847         if (setting_name) {
8848                 setting = nmc_setting_new_for_name (setting_name);
8849                 if (!setting)
8850                         return NULL; /* This should really not happen */
8851                 nmc_setting_custom_init (setting);
8852         }
8853         return setting;
8854 }
8855
8856 static const char *
8857 ask_check_setting (const char *arg,
8858                    const NameItem *valid_settings_main,
8859                    const NameItem *valid_settings_slave,
8860                    const char *valid_settings_str)
8861 {
8862         char *setting_name_user;
8863         const char *setting_name;
8864         GError *err = NULL;
8865
8866         if (!arg) {
8867                 g_print (_("Available settings: %s\n"), valid_settings_str);
8868                 setting_name_user = nmc_readline (EDITOR_PROMPT_SETTING);
8869         } else
8870                 setting_name_user = g_strdup (arg);
8871
8872         if (setting_name_user)
8873                 g_strstrip (setting_name_user);
8874
8875         if (!(setting_name = check_valid_name (setting_name_user,
8876                                                valid_settings_main,
8877                                                valid_settings_slave,
8878                                                &err))) {
8879                 g_print (_("Error: invalid setting name; %s\n"), err->message);
8880                 g_clear_error (&err);
8881         }
8882         g_free (setting_name_user);
8883         return setting_name;
8884 }
8885
8886 static const char *
8887 ask_check_property (const char *arg,
8888                     const char **valid_props,
8889                     const char *valid_props_str)
8890 {
8891         char *prop_name_user;
8892         const char *prop_name;
8893         GError *tmp_err = NULL;
8894
8895         if (!arg) {
8896                 g_print (_("Available properties: %s\n"), valid_props_str);
8897                 prop_name_user = nmc_readline (EDITOR_PROMPT_PROPERTY);
8898                 if (prop_name_user)
8899                         g_strstrip (prop_name_user);
8900         } else
8901                 prop_name_user = g_strdup (arg);
8902
8903         if (!(prop_name = nmc_string_is_valid (prop_name_user, valid_props, &tmp_err))) {
8904                 g_print (_("Error: property %s\n"), tmp_err->message);
8905                 g_clear_error (&tmp_err);
8906         }
8907         g_free (prop_name_user);
8908         return prop_name;
8909 }
8910
8911 /* Copy timestamp from src do dst */
8912 static void
8913 update_connection_timestamp (NMConnection *src, NMConnection *dst)
8914 {
8915         NMSettingConnection *s_con_src, *s_con_dst;
8916
8917         s_con_src = nm_connection_get_setting_connection (src);
8918         s_con_dst = nm_connection_get_setting_connection (dst);
8919         if (s_con_src && s_con_dst) {
8920                 guint64 timestamp = nm_setting_connection_get_timestamp (s_con_src);
8921                 g_object_set (s_con_dst, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
8922         }
8923 }
8924
8925 static gboolean
8926 confirm_connection_saving (NMConnection *local, NMConnection *remote)
8927 {
8928         NMSettingConnection *s_con_loc, *s_con_rem;
8929         gboolean ac_local, ac_remote;
8930         gboolean confirmed = TRUE;
8931
8932         s_con_loc = nm_connection_get_setting_connection (local);
8933         g_assert (s_con_loc);
8934         ac_local = nm_setting_connection_get_autoconnect (s_con_loc);
8935
8936         if (remote) {
8937                 s_con_rem = nm_connection_get_setting_connection (remote);
8938                 g_assert (s_con_rem);
8939                 ac_remote = nm_setting_connection_get_autoconnect (s_con_rem);
8940         } else
8941                 ac_remote = FALSE;
8942
8943         if (ac_local && !ac_remote) {
8944                 char *answer;
8945                 answer = nmc_readline (_("Saving the connection with 'autoconnect=yes'. "
8946                                          "That might result in an immediate activation of the connection.\n"
8947                                          "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL));
8948                 answer = answer ? g_strstrip (answer) : NULL;
8949                 if (!answer || matches (answer, WORD_LOC_YES) == 0)
8950                         confirmed = TRUE;
8951                 else
8952                         confirmed = FALSE;
8953                 g_free (answer);
8954         }
8955         return confirmed;
8956 }
8957
8958 typedef struct {
8959         guint level;
8960         char *main_prompt;
8961         NMSetting *curr_setting;
8962         char **valid_props;
8963         char *valid_props_str;
8964 } NmcEditorMenuContext;
8965
8966 static void
8967 menu_switch_to_level0 (NmCli *nmc,
8968                        NmcEditorMenuContext *menu_ctx,
8969                        const char *prompt,
8970                        NmcTermColor prompt_color)
8971 {
8972         menu_ctx->level = 0;
8973         g_free (menu_ctx->main_prompt);
8974         menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL, "%s", prompt);
8975         menu_ctx->curr_setting = NULL;
8976         g_strfreev (menu_ctx->valid_props);
8977         menu_ctx->valid_props = NULL;
8978         g_free (menu_ctx->valid_props_str);
8979         menu_ctx->valid_props_str = NULL;
8980 }
8981
8982 static void
8983 menu_switch_to_level1 (NmCli *nmc,
8984                        NmcEditorMenuContext *menu_ctx,
8985                        NMSetting *setting,
8986                        const char *setting_name,
8987                        NmcTermColor prompt_color)
8988 {
8989         menu_ctx->level = 1;
8990         g_free (menu_ctx->main_prompt);
8991         menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL,
8992                                               "nmcli %s> ", setting_name);
8993         menu_ctx->curr_setting = setting;
8994         g_strfreev (menu_ctx->valid_props);
8995         menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting);
8996         g_free (menu_ctx->valid_props_str);
8997         menu_ctx->valid_props_str = g_strjoinv (", ", menu_ctx->valid_props);
8998 }
8999
9000 static gboolean
9001 editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
9002 {
9003         NMSettingConnection *s_con;
9004         NMRemoteConnection *rem_con;
9005         NMRemoteConnection *con_tmp;
9006         GWeakRef weak = { { NULL } };
9007         gboolean removed;
9008         NmcEditorMainCmd cmd;
9009         char *cmd_user;
9010         gboolean cmd_loop = TRUE;
9011         char *cmd_arg = NULL;
9012         char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v;
9013         const char *BASE_PROMPT = "nmcli> ";
9014         const NameItem *valid_settings_main = NULL;
9015         const NameItem *valid_settings_slave = NULL;
9016         char *valid_settings_str = NULL;
9017         const char *s_type = NULL;
9018         char *slv_type;
9019         AddConnectionInfo *info = NULL;
9020         gboolean dirty;
9021         gboolean temp_changes;
9022         GError *err1 = NULL;
9023         NmcEditorMenuContext menu_ctx;
9024
9025         s_con = nm_connection_get_setting_connection (connection);
9026         if (s_con)
9027                 s_type = nm_setting_connection_get_slave_type (s_con);
9028         slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
9029
9030         valid_settings_main = get_valid_settings_array (connection_type);
9031         valid_settings_slave = get_valid_settings_array (slv_type);
9032         g_free (slv_type);
9033
9034         valid_settings_str = get_valid_options_string (valid_settings_main, valid_settings_slave);
9035         g_print (_("You may edit the following settings: %s\n"), valid_settings_str);
9036
9037         menu_ctx.level = 0;
9038         menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9039                                              BASE_PROMPT);
9040         menu_ctx.curr_setting = NULL;
9041         menu_ctx.valid_props = NULL;
9042         menu_ctx.valid_props_str = NULL;
9043
9044         /* Get remote connection */
9045         con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9046                                                     nm_connection_get_uuid (connection));
9047         g_weak_ref_init (&weak, con_tmp);
9048         rem_con = g_weak_ref_get (&weak);
9049
9050         while (cmd_loop) {
9051                 /* Connection is dirty? (not saved or differs from the saved) */
9052                 dirty = is_connection_dirty (connection, rem_con);
9053                 temp_changes = rem_con ? nm_remote_connection_get_unsaved (rem_con) : TRUE;
9054                 if (nmc->editor_status_line)
9055                         editor_show_status_line (connection, dirty, temp_changes);
9056
9057                 /* Read user input */
9058                 cmd_user = nmc_readline ("%s", menu_ctx.main_prompt);
9059
9060                 /* Get the remote connection again, it may have disapeared */
9061                 removed = refresh_remote_connection (&weak, &rem_con);
9062                 if (removed)
9063                         g_print (_("The connection profile has been removed from another client. "
9064                                    "You may type 'save' to restore it.\n"));
9065
9066                 if (!cmd_user || *cmd_user == '\0')
9067                         continue;
9068                 cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
9069
9070                 cmd_arg_s = NULL;
9071                 cmd_arg_p = NULL;
9072                 cmd_arg_v = NULL;
9073                 split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
9074                 switch (cmd) {
9075                 case NMC_EDITOR_MAIN_CMD_SET:
9076                         /* Set property value */
9077                         if (!cmd_arg) {
9078                                 if (menu_ctx.level == 1) {
9079                                         const char *prop_name;
9080                                         char *prop_val_user = NULL;
9081                                         const char **avals;
9082                                         GError *tmp_err = NULL;
9083
9084                                         prop_name = ask_check_property (cmd_arg,
9085                                                                         (const char **) menu_ctx.valid_props,
9086                                                                         menu_ctx.valid_props_str);
9087                                         if (!prop_name)
9088                                                 break;
9089
9090                                         avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name);
9091                                         if (avals) {
9092                                                 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9093                                                 g_print (_("Allowed values for '%s' property: %s\n"),
9094                                                          prop_name, avals_str);
9095                                                 g_free (avals_str);
9096                                         }
9097                                         prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
9098
9099                                         /* Set property value */
9100                                         if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) {
9101                                                 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
9102                                                 g_clear_error (&tmp_err);
9103                                         }
9104                                 } else {
9105                                         g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9106                                         g_print (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n"));
9107                                 }
9108                         } else {
9109                                 NMSetting *ss = NULL;
9110                                 gboolean created_ss = FALSE;
9111                                 char *prop_name;
9112                                 GError *tmp_err = NULL;
9113
9114                                 if (cmd_arg_s) {
9115                                         /* setting provided as "setting.property" */
9116                                         ss = is_setting_valid (connection, valid_settings_main, valid_settings_slave, cmd_arg_s);
9117                                         if (!ss) {
9118                                                 ss = create_setting_by_name (cmd_arg_s, valid_settings_main, valid_settings_slave);
9119                                                 if (!ss) {
9120                                                         g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9121                                                                  cmd_arg_s, valid_settings_str);
9122                                                         break;
9123                                                 }
9124                                                 created_ss = TRUE;
9125                                         }
9126                                 } else {
9127                                         if (menu_ctx.curr_setting)
9128                                                 ss = menu_ctx.curr_setting;
9129                                         else {
9130                                                 g_print (_("Error: missing setting for '%s' property\n"), cmd_arg_p);
9131                                                 break;
9132                                         }
9133                                 }
9134
9135                                 prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9136                                 if (!prop_name) {
9137                                         g_print (_("Error: invalid property: %s\n"), tmp_err->message);
9138                                         g_clear_error (&tmp_err);
9139                                         if (created_ss)
9140                                                 g_object_unref (ss);
9141                                         break;
9142                                 }
9143
9144
9145
9146                                 /* Ask for value */
9147                                 if (!cmd_arg_v) {
9148                                         const char **avals = nmc_setting_get_property_allowed_values (ss, prop_name);
9149                                         if (avals) {
9150                                                 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9151                                                 g_print (_("Allowed values for '%s' property: %s\n"),
9152                                                          prop_name, avals_str);
9153                                                 g_free (avals_str);
9154                                         }
9155                                         cmd_arg_v = nmc_readline (_("Enter '%s' value: "), prop_name);
9156                                 }
9157
9158                                 /* Set property value */
9159                                 if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) {
9160                                         g_print (_("Error: failed to set '%s' property: %s\n"),
9161                                                  prop_name, tmp_err->message);
9162                                         g_clear_error (&tmp_err);
9163                                 }
9164
9165                                 if (created_ss)
9166                                         nm_connection_add_setting (connection, ss);
9167                                 g_free (prop_name);
9168                         }
9169                         break;
9170
9171                 case NMC_EDITOR_MAIN_CMD_GOTO:
9172                         /* cmd_arg_s != NULL means 'setting.property' argument */
9173                         if (menu_ctx.level == 0 || cmd_arg_s) {
9174                                 /* in top level - no setting selected yet */
9175                                 const char *setting_name;
9176                                 NMSetting *setting;
9177                                 const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p;
9178
9179                                 setting_name = ask_check_setting (user_arg,
9180                                                                   valid_settings_main,
9181                                                                   valid_settings_slave,
9182                                                                   valid_settings_str);
9183                                 if (!setting_name)
9184                                         break;
9185
9186                                 setting = nm_connection_get_setting_by_name (connection, setting_name);
9187                                 if (!setting) {
9188                                         setting = nmc_setting_new_for_name (setting_name);
9189                                         if (!setting) {
9190                                                 g_print (_("Error: unknown setting '%s'\n"), setting_name);
9191                                                 break;
9192                                         }
9193                                         nmc_setting_custom_init (setting);
9194                                         nm_connection_add_setting (connection, setting);
9195                                 }
9196                                 /* Set global variable for use in TAB completion */
9197                                 nmc_tab_completion.setting = setting;
9198
9199                                 /* Switch to level 1 */
9200                                 menu_switch_to_level1 (nmc, &menu_ctx, setting, setting_name, nmc->editor_prompt_color);
9201
9202                                 if (!cmd_arg_s) {
9203                                         g_print (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str);
9204                                         break;
9205                                 }
9206                         }
9207                         if (menu_ctx.level == 1 || cmd_arg_s) {
9208                                 /* level 1 - setting selected */
9209                                 const char *prop_name;
9210
9211                                 prop_name = ask_check_property (cmd_arg_p,
9212                                                                 (const char **) menu_ctx.valid_props,
9213                                                                 menu_ctx.valid_props_str);
9214                                 if (!prop_name)
9215                                         break;
9216
9217                                 /* submenu - level 2 - editing properties */
9218                                 cmd_loop = property_edit_submenu (nmc,
9219                                                                   connection,
9220                                                                   &rem_con,
9221                                                                   &weak,
9222                                                                   menu_ctx.curr_setting,
9223                                                                   prop_name);
9224                         }
9225                         break;
9226
9227                 case NMC_EDITOR_MAIN_CMD_REMOVE:
9228                         /* Remove setting from connection, or delete value of a property */
9229                         if (!cmd_arg) {
9230                                 if (menu_ctx.level == 1) {
9231                                         GError *tmp_err = NULL;
9232                                         const char *prop_name;
9233
9234                                         prop_name = ask_check_property (cmd_arg,
9235                                                                         (const char **) menu_ctx.valid_props,
9236                                                                         menu_ctx.valid_props_str);
9237                                         if (!prop_name)
9238                                                 break;
9239
9240                                         /* Delete property value */
9241                                         if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) {
9242                                                 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9243                                                          tmp_err->message);
9244                                                 g_clear_error (&tmp_err);
9245                                         }
9246                                 } else
9247                                         g_print (_("Error: no argument given; valid are [%s]\n"), valid_settings_str);
9248                         } else {
9249                                 NMSetting *ss = NULL;
9250                                 gboolean descr_all;
9251                                 char *user_s;
9252
9253                                 /* cmd_arg_s != NULL means argument is "setting.property" */
9254                                 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9255                                 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9256                                 if (user_s) {
9257                                         ss = is_setting_valid (connection,
9258                                                                valid_settings_main,
9259                                                                valid_settings_slave,
9260                                                                user_s);
9261                                         if (!ss) {
9262                                                 if (check_valid_name (user_s,
9263                                                                       valid_settings_main,
9264                                                                       valid_settings_slave,
9265                                                                       NULL))
9266                                                         g_print (_("Setting '%s' is not present in the connection.\n"),
9267                                                                  user_s);
9268                                                 else
9269                                                         g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9270                                                                  user_s, valid_settings_str);
9271                                                 break;
9272                                         }
9273                                 } else
9274                                         ss = menu_ctx.curr_setting;
9275
9276                                 if (descr_all) {
9277                                         /* Remove setting from the connection */
9278                                         connection_remove_setting (connection, ss);
9279                                         if (ss == menu_ctx.curr_setting) {
9280                                                 /* If we removed the setting we are in, go up */
9281                                                 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9282                                                 nmc_tab_completion.setting = NULL;  /* for TAB completion */
9283                                         }
9284                                 } else {
9285                                         GError *tmp_err = NULL;
9286                                         char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9287                                         if (prop_name) {
9288                                                 /* Delete property value */
9289                                                 if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) {
9290                                                         g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9291                                                                  tmp_err->message);
9292                                                         g_clear_error (&tmp_err);
9293                                                 }
9294                                         } else {
9295                                                 /* If the string is not a property, try it as a setting */
9296                                                 NMSetting *s_tmp;
9297                                                 s_tmp = is_setting_valid (connection,
9298                                                                           valid_settings_main,
9299                                                                           valid_settings_slave,
9300                                                                           cmd_arg_p);
9301                                                 if (s_tmp) {
9302                                                         /* Remove setting from the connection */
9303                                                         connection_remove_setting (connection, s_tmp);
9304                                                         /* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */
9305                                                         if (ss == menu_ctx.curr_setting) {
9306                                                                 /* If we removed the setting we are in, go up */
9307                                                                 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9308                                                                 nmc_tab_completion.setting = NULL;  /* for TAB completion */
9309                                                         }
9310                                                 } else
9311                                                         g_print (_("Error: %s properties, nor it is a setting name.\n"),
9312                                                                  tmp_err->message);
9313                                                 g_clear_error (&tmp_err);
9314                                         }
9315                                         g_free (prop_name);
9316                                 }
9317                         }
9318                         break;
9319
9320                 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
9321                         /* Print property description */
9322                         if (!cmd_arg) {
9323                                 if (menu_ctx.level == 1) {
9324                                         const char *prop_name;
9325
9326                                         prop_name = ask_check_property (cmd_arg,
9327                                                                         (const char **) menu_ctx.valid_props,
9328                                                                         menu_ctx.valid_props_str);
9329                                         if (!prop_name)
9330                                                 break;
9331
9332                                         /* Show property description */
9333                                         print_property_description (menu_ctx.curr_setting, prop_name);
9334                                 } else {
9335                                         g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9336                                         g_print (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n"));
9337                                 }
9338                         } else {
9339                                 NMSetting *ss = NULL;
9340                                 gboolean unref_ss = FALSE;
9341                                 gboolean descr_all;
9342                                 char *user_s;
9343
9344                                 /* cmd_arg_s != NULL means argument is "setting.property" */
9345                                 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9346                                 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9347                                 if (user_s) {
9348                                         ss = is_setting_valid (connection,
9349                                                                valid_settings_main,
9350                                                                valid_settings_slave,
9351                                                                user_s);
9352                                         if (!ss) {
9353                                                 ss = create_setting_by_name (user_s,
9354                                                                              valid_settings_main,
9355                                                                              valid_settings_slave);
9356                                                 if (!ss) {
9357                                                         g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9358                                                                  user_s, valid_settings_str);
9359                                                         break;
9360                                                 }
9361                                                 unref_ss = TRUE;
9362                                         }
9363                                 } else
9364                                         ss = menu_ctx.curr_setting;
9365
9366                                 if (descr_all) {
9367                                         /* Show description for all properties */
9368                                         print_setting_description (ss);
9369                                 } else {
9370                                         GError *tmp_err = NULL;
9371                                         char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9372                                         if (prop_name) {
9373                                                 /* Show property description */
9374                                                 print_property_description (ss, prop_name);
9375                                         } else {
9376                                                 /* If the string is not a property, try it as a setting */
9377                                                 NMSetting *s_tmp;
9378                                                 s_tmp = is_setting_valid (connection,
9379                                                                           valid_settings_main,
9380                                                                           valid_settings_slave,
9381                                                                           cmd_arg_p);
9382                                                 if (s_tmp)
9383                                                         print_setting_description (s_tmp);
9384                                                 else
9385                                                         g_print (_("Error: invalid property: %s, "
9386                                                                    "neither a valid setting name.\n"),
9387                                                                  tmp_err->message);
9388                                                 g_clear_error (&tmp_err);
9389                                         }
9390                                         g_free (prop_name);
9391                                 }
9392                                 if (unref_ss)
9393                                         g_object_unref (ss);
9394                         }
9395                         break;
9396
9397                 case NMC_EDITOR_MAIN_CMD_PRINT:
9398                         /* Print current connection settings/properties */
9399                         if (cmd_arg) {
9400                                 if (strcmp (cmd_arg, "all") == 0)
9401                                         editor_show_connection (connection, nmc);
9402                                 else {
9403                                         NMSetting *ss = NULL;
9404                                         gboolean whole_setting;
9405                                         char *user_s;
9406
9407                                         /* cmd_arg_s != NULL means argument is "setting.property" */
9408                                         whole_setting = !cmd_arg_s && !menu_ctx.curr_setting;
9409                                         user_s = whole_setting ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9410                                         if (user_s) {
9411                                                 const char *s_name;
9412                                                 s_name = check_valid_name (user_s,
9413                                                                            valid_settings_main,
9414                                                                            valid_settings_slave,
9415                                                                            NULL);
9416                                                 if (!s_name) {
9417                                                         g_print (_("Error: unknown setting: '%s'\n"), user_s);
9418                                                         break;
9419                                                 }
9420                                                 ss = nm_connection_get_setting_by_name (connection, s_name);
9421                                                 if (!ss) {
9422                                                         g_print (_("Error: '%s' setting not present in the connection\n"), s_name);
9423                                                         break;
9424                                                 }
9425                                         } else
9426                                                 ss = menu_ctx.curr_setting;
9427
9428                                         if (whole_setting) {
9429                                                 /* Print the whole setting */
9430                                                 editor_show_setting (ss, nmc);
9431                                         } else {
9432                                                 GError *err = NULL;
9433                                                 char *prop_name = is_property_valid (ss, cmd_arg_p, &err);
9434                                                 if (prop_name) {
9435                                                         /* Print one property */
9436                                                         char *prop_val = nmc_setting_get_property (ss, prop_name, NULL);
9437                                                         g_print ("%s.%s: %s\n", nm_setting_get_name (ss),prop_name , prop_val);
9438                                                         g_free (prop_val);
9439                                                 } else {
9440                                                         /* If the string is not a property, try it as a setting */
9441                                                         NMSetting *s_tmp;
9442                                                         s_tmp = is_setting_valid (connection,
9443                                                                                   valid_settings_main,
9444                                                                                   valid_settings_slave,
9445                                                                                   cmd_arg_p);
9446                                                         if (s_tmp) {
9447                                                                 /* Print the whole setting */
9448                                                                 editor_show_setting (s_tmp, nmc);
9449                                                         } else
9450                                                                 g_print (_("Error: invalid property: %s%s\n"),
9451                                                                          err->message,
9452                                                                          cmd_arg_s ? "" : _(", neither a valid setting name"));
9453                                                         g_clear_error (&err);
9454                                                 }
9455                                                 g_free (prop_name);
9456                                         }
9457                                 }
9458                         } else {
9459                                 if (menu_ctx.curr_setting)
9460                                         editor_show_setting (menu_ctx.curr_setting, nmc);
9461                                 else
9462                                         editor_show_connection (connection, nmc);
9463                         }
9464                         break;
9465
9466                 case NMC_EDITOR_MAIN_CMD_VERIFY:
9467                         /* Verify current setting or the whole connection */
9468                         if (cmd_arg && strcmp (cmd_arg, "all") && strcmp (cmd_arg, "fix")) {
9469                                 g_print (_("Invalid verify option: %s\n"), cmd_arg);
9470                                 break;
9471                         }
9472
9473                         if (   menu_ctx.curr_setting
9474                             && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) {
9475                                 GError *tmp_err = NULL;
9476                                 (void) nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err);
9477                                 g_print (_("Verify setting '%s': %s\n"),
9478                                          nm_setting_get_name (menu_ctx.curr_setting),
9479                                          tmp_err ? tmp_err->message : "OK");
9480                                 g_clear_error (&tmp_err);
9481                         } else {
9482                                 GError *tmp_err = NULL;
9483                                 gboolean valid, modified;
9484                                 gboolean fixed = TRUE;
9485
9486                                 valid = nm_connection_verify (connection, &tmp_err);
9487                                 if (!valid && (g_strcmp0 (cmd_arg, "fix") == 0)) {
9488                                         /* Try to fix normalizable errors */
9489                                         g_clear_error (&tmp_err);
9490                                         fixed = nm_connection_normalize (connection, NULL, &modified, &tmp_err);
9491                                 }
9492                                 g_print (_("Verify connection: %s\n"),
9493                                          tmp_err ? tmp_err->message : "OK");
9494                                 if (!fixed)
9495                                         g_print (_("The error cannot be fixed automatically.\n"));
9496                                 g_clear_error (&tmp_err);
9497                         }
9498                         break;
9499
9500                 case NMC_EDITOR_MAIN_CMD_SAVE:
9501                         /* Save the connection */
9502                         if (nm_connection_verify (connection, &err1)) {
9503                                 gboolean persistent = TRUE;
9504
9505                                 /* parse argument */
9506                                 if (cmd_arg) {
9507                                         if (matches (cmd_arg, "temporary") == 0)
9508                                                 persistent = FALSE;
9509                                         else if (matches (cmd_arg, "persistent") == 0)
9510                                                 persistent = TRUE;
9511                                         else {
9512                                                 g_print (_("Error: invalid argument '%s'\n"), cmd_arg);
9513                                                 break;
9514                                         }
9515                                 }
9516
9517                                 /* Ask for save confirmation if the connection changes to autoconnect=yes */
9518                                 if (nmc->editor_save_confirmation)
9519                                         if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con)))
9520                                                 break;
9521
9522                                 if (!rem_con) {
9523                                         /* Tell the settings service to add the new connection */
9524                                         info = g_malloc0 (sizeof (AddConnectionInfo));
9525                                         info->nmc = nmc;
9526                                         info->con_name = g_strdup (nm_connection_get_id (connection));
9527                                         add_new_connection (persistent,
9528                                                             nmc->client,
9529                                                             connection,
9530                                                             add_connection_editor_cb,
9531                                                             info);
9532                                 } else {
9533                                         /* Save/update already saved (existing) connection */
9534                                         nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con),
9535                                                                                         connection);
9536                                         update_connection (persistent, rem_con, update_connection_editor_cb, NULL);
9537                                 }
9538
9539                                 g_mutex_lock (&nmc_editor_mutex);
9540                                 //FIXME: add also a timeout for cases the callback is not called
9541                                 while (!nmc_editor_cb_called)
9542                                         g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9543
9544                                 if (nmc_editor_error) {
9545                                         g_print (_("Error: Failed to save '%s' (%s) connection: %s\n"),
9546                                                  nm_connection_get_id (connection),
9547                                                  nm_connection_get_uuid (connection),
9548                                                  nmc_editor_error->message);
9549                                         g_error_free (nmc_editor_error);
9550                                 } else {
9551                                         g_print (!rem_con ?
9552                                                  _("Connection '%s' (%s) successfully saved.\n") :
9553                                                  _("Connection '%s' (%s) successfully updated.\n"),
9554                                                  nm_connection_get_id (connection),
9555                                                  nm_connection_get_uuid (connection));
9556
9557                                         con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9558                                                                                     nm_connection_get_uuid (connection));
9559                                         g_weak_ref_set (&weak, con_tmp);
9560                                         refresh_remote_connection (&weak, &rem_con);
9561
9562                                         /* Replace local connection with the remote one to be sure they are equal.
9563                                          * This mitigates problems with plugins not preserving some properties or
9564                                          * adding ipv{4,6} settings when not present.
9565                                          */
9566                                         if (con_tmp) {
9567                                                 char *s_name = NULL;
9568                                                 if (menu_ctx.curr_setting)
9569                                                         s_name = g_strdup (nm_setting_get_name (menu_ctx.curr_setting));
9570
9571                                                 /* Update settings in the local connection */
9572                                                 nm_connection_replace_settings_from_connection (connection,
9573                                                                                                 NM_CONNECTION (con_tmp));
9574
9575                                                 /* Also update setting for menu context and TAB-completion */
9576                                                 menu_ctx.curr_setting = s_name ? nm_connection_get_setting_by_name (connection, s_name) : NULL;
9577                                                 nmc_tab_completion.setting = menu_ctx.curr_setting;
9578                                                 g_free (s_name);
9579                                         }
9580                                 }
9581
9582                                 nmc_editor_cb_called = FALSE;
9583                                 nmc_editor_error = NULL;
9584                                 g_mutex_unlock (&nmc_editor_mutex);
9585                         } else {
9586                                 g_print (_("Error: connection verification failed: %s\n"),
9587                                          err1 ? err1->message : _("(unknown error)"));
9588                                 g_print (_("You may try running 'verify fix' to fix errors.\n"));
9589                         }
9590
9591                         g_clear_error (&err1);
9592                         break;
9593
9594                 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
9595                         {
9596                         GError *tmp_err = NULL;
9597                         const char *ifname = cmd_arg_p;
9598                         const char *ap_nsp = cmd_arg_v;
9599
9600                         /* When only AP/NSP is specified it is prepended with '/' */
9601                         if (!cmd_arg_v) {
9602                                 if (ifname && ifname[0] == '/') {
9603                                         ap_nsp = ifname + 1;
9604                                         ifname = NULL;
9605                                 }
9606                         } else
9607                                 ap_nsp = ap_nsp && ap_nsp[0] == '/' ? ap_nsp + 1 : ap_nsp;
9608
9609                         if (is_connection_dirty (connection, rem_con)) {
9610                                 g_print (_("Error: connection is not saved. Type 'save' first.\n"));
9611                                 break;
9612                         }
9613                         if (!nm_connection_verify (NM_CONNECTION (rem_con), &tmp_err)) {
9614                                 g_print (_("Error: connection is not valid: %s\n"), tmp_err->message);
9615                                 g_clear_error (&tmp_err);
9616                                 break;
9617                         }
9618
9619                         nmc->nowait_flag = FALSE;
9620                         nmc->should_wait++;
9621                         nmc->print_output = NMC_PRINT_PRETTY;
9622                         if (!nmc_activate_connection (nmc, NM_CONNECTION (rem_con), ifname, ap_nsp, ap_nsp, NULL,
9623                                                       activate_connection_editor_cb, &tmp_err)) {
9624                                 g_print (_("Error: Cannot activate connection: %s.\n"), tmp_err->message);
9625                                 g_clear_error (&tmp_err);
9626                                 break;
9627                         }
9628
9629                         g_mutex_lock (&nmc_editor_mutex);
9630                         while (!nmc_editor_cb_called)
9631                                 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9632
9633                         if (nmc_editor_error) {
9634                                 g_print (_("Error: Failed to activate '%s' (%s) connection: %s\n"),
9635                                          nm_connection_get_id (connection),
9636                                          nm_connection_get_uuid (connection),
9637                                          nmc_editor_error->message);
9638                                 g_error_free (nmc_editor_error);
9639                         } else {
9640                                 g_print (_("Monitoring connection activation (press any key to continue)\n"));
9641                                 nmc_get_user_input ("");
9642                         }
9643
9644                         if (nmc_editor_monitor_ac) {
9645                                 if (nmc_editor_monitor_ac->monitor_id)
9646                                         g_source_remove (nmc_editor_monitor_ac->monitor_id);
9647                                 g_free (nmc_editor_monitor_ac);
9648                         }
9649                         nmc_editor_cb_called = FALSE;
9650                         nmc_editor_error = NULL;
9651                         nmc_editor_monitor_ac = NULL;
9652                         g_mutex_unlock (&nmc_editor_mutex);
9653
9654                         /* Update timestamp in local connection */
9655                         update_connection_timestamp (NM_CONNECTION (rem_con), connection);
9656
9657                         }
9658                         break;
9659
9660                 case NMC_EDITOR_MAIN_CMD_BACK:
9661                         /* Go back (up) an the menu */
9662                         if (menu_ctx.level == 1) {
9663                                 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9664                                 nmc_tab_completion.setting = NULL;  /* for TAB completion */
9665                         }
9666                         break;
9667
9668                 case NMC_EDITOR_MAIN_CMD_HELP:
9669                         /* Print command help */
9670                         editor_main_help (cmd_arg);
9671                         break;
9672
9673                 case NMC_EDITOR_MAIN_CMD_NMCLI:
9674                         if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) {
9675                                 GError *tmp_err = NULL;
9676                                 gboolean bb;
9677                                 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9678                                         g_print (_("Error: status-line: %s\n"), tmp_err->message);
9679                                         g_clear_error (&tmp_err);
9680                                 } else
9681                                         nmc->editor_status_line = bb;
9682                         } else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) {
9683                                 GError *tmp_err = NULL;
9684                                 gboolean bb;
9685                                 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9686                                         g_print (_("Error: save-confirmation: %s\n"), tmp_err->message);
9687                                         g_clear_error (&tmp_err);
9688                                 } else
9689                                         nmc->editor_save_confirmation = bb;
9690                         } else if (cmd_arg_p && matches (cmd_arg_p, "show-secrets") == 0) {
9691                                 GError *tmp_err = NULL;
9692                                 gboolean bb;
9693                                 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9694                                         g_print (_("Error: show-secrets: %s\n"), tmp_err->message);
9695                                         g_clear_error (&tmp_err);
9696                                 } else
9697                                         nmc->editor_show_secrets = bb;
9698                         } else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) {
9699                                 GError *tmp_err = NULL;
9700                                 NmcTermColor color;
9701                                 color = nmc_term_color_parse_string (cmd_arg_v ? g_strstrip (cmd_arg_v) : " ", &tmp_err);
9702                                 if (tmp_err) {
9703                                         g_print (_("Error: bad color: %s\n"), tmp_err->message);
9704                                         g_clear_error (&tmp_err);
9705                                 } else {
9706                                         nmc->editor_prompt_color = color;
9707                                         g_free (menu_ctx.main_prompt);
9708                                         if (menu_ctx.level == 0)
9709                                                 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9710                                                                                      BASE_PROMPT);
9711                                         else
9712                                                 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9713                                                                                      "nmcli %s> ",
9714                                                                                      nm_setting_get_name (menu_ctx.curr_setting));
9715                                 }
9716                         } else if (!cmd_arg_p) {
9717                                 g_print (_("Current nmcli configuration:\n"));
9718                                 g_print ("status-line: %s\n"
9719                                          "save-confirmation: %s\n"
9720                                          "show-secrets: %s\n"
9721                                          "prompt-color: %d\n",
9722                                          nmc->editor_status_line ? "yes" : "no",
9723                                          nmc->editor_save_confirmation ? "yes" : "no",
9724                                          nmc->editor_show_secrets ? "yes" : "no",
9725                                          nmc->editor_prompt_color);
9726                         } else
9727                                 g_print (_("Invalid configuration option '%s'; allowed [%s]\n"),
9728                                          cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, show-secrets, prompt-color");
9729
9730                         break;
9731
9732                 case NMC_EDITOR_MAIN_CMD_QUIT:
9733                         if (is_connection_dirty (connection, rem_con)) {
9734                                 if (confirm_quit ())
9735                                         cmd_loop = FALSE;  /* quit command loop */
9736                         } else
9737                                 cmd_loop = FALSE;  /* quit command loop */
9738                         break;
9739
9740                 case NMC_EDITOR_MAIN_CMD_UNKNOWN:
9741                 default:
9742                         g_print (_("Unknown command: '%s'\n"), cmd_user);
9743                         break;
9744                 }
9745
9746                 g_free (cmd_user);
9747                 g_free (cmd_arg);
9748                 g_free (cmd_arg_s);
9749                 g_free (cmd_arg_p);
9750                 g_free (cmd_arg_v);
9751         }
9752         g_free (valid_settings_str);
9753         g_free (menu_ctx.main_prompt);
9754         g_strfreev (menu_ctx.valid_props);
9755         g_free (menu_ctx.valid_props_str);
9756         if (rem_con)
9757                 g_object_unref (rem_con);
9758         g_weak_ref_clear (&weak);
9759
9760         /* Save history file */
9761         save_history_cmds (nm_connection_get_uuid (connection));
9762
9763         return TRUE;
9764 }
9765
9766 static const char *
9767 get_ethernet_device_name (NmCli *nmc)
9768 {
9769         const GPtrArray *devices;
9770         int i;
9771
9772         devices = nm_client_get_devices (nmc->client);
9773         for (i = 0; i < devices->len; i++) {
9774                 NMDevice *dev = g_ptr_array_index (devices, i);
9775                 if (NM_IS_DEVICE_ETHERNET (dev))
9776                         return nm_device_get_iface (dev);
9777         }
9778         return NULL;
9779 }
9780
9781 static void
9782 editor_init_new_connection (NmCli *nmc, NMConnection *connection)
9783 {
9784         NMSetting *setting, *base_setting;
9785         NMSettingConnection *s_con;
9786         const char *con_type;
9787         const char *slave_type = NULL;
9788
9789         s_con = nm_connection_get_setting_connection (connection);
9790         g_assert (s_con);
9791         con_type = nm_setting_connection_get_connection_type (s_con);
9792
9793         /* Initialize new connection according to its type using sensible defaults. */
9794
9795         nmc_setting_connection_connect_handlers (s_con, connection);
9796
9797         if (g_strcmp0 (con_type, "bond-slave") == 0)
9798                 slave_type = NM_SETTING_BOND_SETTING_NAME;
9799         if (g_strcmp0 (con_type, "team-slave") == 0)
9800                 slave_type = NM_SETTING_TEAM_SETTING_NAME;
9801         if (g_strcmp0 (con_type, "bridge-slave") == 0)
9802                 slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
9803
9804         if (slave_type) {
9805                 const char *dev_ifname = get_ethernet_device_name (nmc);
9806
9807                 /* For bond/team/bridge slaves add 'wired' setting */
9808                 setting = nm_setting_wired_new ();
9809                 nm_connection_add_setting (connection, setting);
9810
9811                 g_object_set (s_con,
9812                               NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
9813                               NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9814                               NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
9815                               NULL);
9816         } else {
9817                 /* Add a "base" setting to the connection by default */
9818                 base_setting = nmc_setting_new_for_name (con_type);
9819                 if (!base_setting)
9820                         return;
9821                 nm_connection_add_setting (connection, base_setting);
9822
9823                 /* Set a sensible bond/team/bridge interface name by default */
9824                 if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0)
9825                         g_object_set (s_con,
9826                                       NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bond",
9827                                       NULL);
9828                 if (g_strcmp0 (con_type, NM_SETTING_TEAM_SETTING_NAME) == 0)
9829                         g_object_set (s_con,
9830                                       NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-team",
9831                                       NULL);
9832                 if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0)
9833                         g_object_set (s_con,
9834                                       NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bridge",
9835                                       NULL);
9836
9837                 /* Set sensible initial VLAN values */
9838                 if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) {
9839                         const char *dev_ifname = get_ethernet_device_name (nmc);
9840
9841                         g_object_set (NM_SETTING_VLAN (base_setting),
9842                                       NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0",
9843                                       NM_SETTING_VLAN_ID, 1,
9844                                       NULL);
9845                         g_object_set (s_con,
9846                                       NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9847                                       NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME,
9848                                       NULL);
9849                 }
9850
9851                 /* Initialize 'transport-mode' so that 'infiniband' is valid */
9852                 if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0)
9853                         g_object_set (NM_SETTING_INFINIBAND (base_setting),
9854                                       NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
9855                                       NULL);
9856
9857                 /* Initialize 'number' so that 'cdma' is valid */
9858                 if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0)
9859                         g_object_set (NM_SETTING_CDMA (base_setting),
9860                                       NM_SETTING_CDMA_NUMBER, "#777",
9861                                       NULL);
9862
9863                 /* Initialize 'number' so that 'gsm' is valid */
9864                 if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0)
9865                         g_object_set (NM_SETTING_GSM (base_setting),
9866                                       NM_SETTING_GSM_NUMBER, "*99#",
9867                                       NULL);
9868
9869                 /* Wi-Fi */
9870                 if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0) {
9871                         /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL
9872                          * is regarded as "infrastructure", explicit value makes no doubts.
9873                          */
9874                         g_object_set (NM_SETTING_WIRELESS (base_setting),
9875                                       NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
9876                                       NULL);
9877
9878                         /* Do custom initialization for wifi setting */
9879                         nmc_setting_custom_init (base_setting);
9880                 }
9881
9882                 /* ADSL */
9883                 if (g_strcmp0 (con_type, NM_SETTING_ADSL_SETTING_NAME) == 0) {
9884                         /* Initialize a protocol */
9885                         g_object_set (NM_SETTING_ADSL (base_setting),
9886                                       NM_SETTING_ADSL_PROTOCOL, NM_SETTING_ADSL_PROTOCOL_PPPOE,
9887                                       NULL);
9888                 }
9889
9890                 /* Always add IPv4 and IPv6 settings for non-slave connections */
9891                 setting = nm_setting_ip4_config_new ();
9892                 nmc_setting_custom_init (setting);
9893                 nm_connection_add_setting (connection, setting);
9894
9895                 setting = nm_setting_ip6_config_new ();
9896                 nmc_setting_custom_init (setting);
9897                 nm_connection_add_setting (connection, setting);
9898         }
9899 }
9900
9901 static void
9902 editor_init_existing_connection (NMConnection *connection)
9903 {
9904         NMSettingIPConfig *s_ip4, *s_ip6;
9905         NMSettingWireless *s_wireless;
9906         NMSettingConnection *s_con;
9907
9908         s_ip4 = nm_connection_get_setting_ip4_config (connection);
9909         s_ip6 = nm_connection_get_setting_ip6_config (connection);
9910         s_wireless = nm_connection_get_setting_wireless (connection);
9911         s_con = nm_connection_get_setting_connection (connection);
9912
9913         if (s_ip4)
9914                 nmc_setting_ip4_connect_handlers (s_ip4);
9915         if (s_ip6)
9916                 nmc_setting_ip6_connect_handlers (s_ip6);
9917         if (s_wireless)
9918                 nmc_setting_wireless_connect_handlers (s_wireless);
9919         if (s_con)
9920                 nmc_setting_connection_connect_handlers (s_con, connection);
9921 }
9922
9923 static NMCResultCode
9924 do_connection_edit (NmCli *nmc, int argc, char **argv)
9925 {
9926         NMConnection *connection = NULL;
9927         NMSettingConnection *s_con;
9928         const char *connection_type;
9929         char *uuid;
9930         char *default_name = NULL;
9931         const char *type = NULL;
9932         char *type_ask = NULL;
9933         const char *con_name = NULL;
9934         const char *con = NULL;
9935         const char *con_id = NULL;
9936         const char *con_uuid = NULL;
9937         const char *con_path = NULL;
9938         const char *selector = NULL;
9939         char *tmp_str;
9940         GError *error = NULL;
9941         GError *err1 = NULL;
9942         nmc_arg_t exp_args[] = { {"type",     TRUE, &type,     FALSE},
9943                                  {"con-name", TRUE, &con_name, FALSE},
9944                                  {"id",       TRUE, &con_id,   FALSE},
9945                                  {"uuid",     TRUE, &con_uuid, FALSE},
9946                                  {"path",     TRUE, &con_path, FALSE},
9947                                  {NULL} };
9948
9949         nmc->return_value = NMC_RESULT_SUCCESS;
9950
9951         if (argc == 1)
9952                 con = *argv;
9953         else {
9954                 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) {
9955                         g_string_assign (nmc->return_text, error->message);
9956                         nmc->return_value = error->code;
9957                         g_clear_error (&error);
9958                         goto error;
9959                 }
9960         }
9961
9962         /* Setup some readline completion stuff */
9963         /* Set a pointer to an alternative function to create matches */
9964         rl_attempted_completion_function = (rl_completion_func_t *) nmcli_editor_tab_completion;
9965         /* Use ' ' and '.' as word break characters */
9966         rl_completer_word_break_characters = ". ";
9967
9968         if (!con) {
9969                 if (con_id && !con_uuid && !con_path) {
9970                         con = con_id;
9971                         selector = "id";
9972                 } else if (con_uuid && !con_id && !con_path) {
9973                         con = con_uuid;
9974                         selector = "uuid";
9975                 } else if (con_path && !con_id && !con_uuid) {
9976                         con = con_path;
9977                         selector = "path";
9978                 } else if (!con_path && !con_id && !con_uuid) {
9979                         /* no-op */
9980                 } else {
9981                         g_string_printf (nmc->return_text,
9982                                          _("Error: only one of 'id', uuid, or 'path' can be provided."));
9983                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
9984                         goto error;
9985                 }
9986         }
9987
9988         if (con) {
9989                 /* Existing connection */
9990                 NMConnection *found_con;
9991
9992                 found_con = nmc_find_connection (nmc->connections, selector, con, NULL);
9993                 if (!found_con) {
9994                         g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
9995                         nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
9996                         goto error;
9997                 }
9998
9999                 /* Duplicate the connection and use that so that we need not
10000                  * differentiate existing vs. new later
10001                  */
10002                 connection = nm_simple_connection_new_clone (found_con);
10003
10004                 /* Merge secrets into the connection */
10005                 update_secrets_in_connection (NM_REMOTE_CONNECTION (found_con), connection);
10006
10007                 s_con = nm_connection_get_setting_connection (connection);
10008                 g_assert (s_con);
10009                 connection_type = nm_setting_connection_get_connection_type (s_con);
10010
10011                 if (type)
10012                         g_print (_("Warning: editing existing connection '%s'; 'type' argument is ignored\n"),
10013                                  nm_connection_get_id (connection));
10014                 if (con_name)
10015                         g_print (_("Warning: editing existing connection '%s'; 'con-name' argument is ignored\n"),
10016                                  nm_connection_get_id (connection));
10017
10018                 /* Load previously saved history commands for the connection */
10019                 load_history_cmds (nm_connection_get_uuid (connection));
10020
10021                 editor_init_existing_connection (connection);
10022         } else {
10023                 /* New connection */
10024                 connection_type = check_valid_name (type, nmc_valid_connection_types, NULL, &err1);
10025                 tmp_str = get_valid_options_string (nmc_valid_connection_types, NULL);
10026
10027                 while (!connection_type) {
10028                         if (!type)
10029                                 g_print (_("Valid connection types: %s\n"), tmp_str);
10030                         else
10031                                 g_print (_("Error: invalid connection type; %s\n"), err1->message);
10032                         g_clear_error (&err1);
10033
10034                         type_ask = nmc_readline (EDITOR_PROMPT_CON_TYPE);
10035                         type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10036                         connection_type = check_valid_name (type_ask, nmc_valid_connection_types, NULL, &err1);
10037                         g_free (type_ask);
10038                 }
10039                 g_free (tmp_str);
10040
10041                 /* Create a new connection object */
10042                 connection = nm_simple_connection_new ();
10043
10044                 /* Build up the 'connection' setting */
10045                 s_con = (NMSettingConnection *) nm_setting_connection_new ();
10046                 uuid = nm_utils_uuid_generate ();
10047                 if (con_name)
10048                         default_name = g_strdup (con_name);
10049                 else
10050                         default_name = nmc_unique_connection_name (nmc->connections,
10051                                                                    get_name_alias (connection_type, nmc_valid_connection_types));
10052
10053                 g_object_set (s_con,
10054                               NM_SETTING_CONNECTION_ID, default_name,
10055                               NM_SETTING_CONNECTION_UUID, uuid,
10056                               NM_SETTING_CONNECTION_TYPE, connection_type,
10057                               NULL);
10058                 g_free (uuid);
10059                 g_free (default_name);
10060                 nm_connection_add_setting (connection, NM_SETTING (s_con));
10061
10062                 /* Initialize the new connection so that it is valid from the start */
10063                 editor_init_new_connection (nmc, connection);
10064         }
10065
10066         /* nmcli runs the editor */
10067         nmc->in_editor = TRUE;
10068
10069         g_print ("\n");
10070         g_print (_("===| nmcli interactive connection editor |==="));
10071         g_print ("\n\n");
10072         if (con)
10073                 g_print (_("Editing existing '%s' connection: '%s'"), connection_type, con);
10074         else
10075                 g_print (_("Adding a new '%s' connection"), connection_type);
10076         g_print ("\n\n");
10077         g_print (_("Type 'help' or '?' for available commands."));
10078         g_print ("\n");
10079         g_print (_("Type 'describe [<setting>.<prop>]' for detailed property description."));
10080         g_print ("\n\n");
10081
10082         /* Set global variables for use in TAB completion */
10083         nmc_tab_completion.nmc = nmc;
10084         nmc_tab_completion.con_type = g_strdup (connection_type);
10085         nmc_tab_completion.connection = connection;
10086
10087         /* Run menu loop */
10088         editor_menu_main (nmc, connection, connection_type);
10089
10090         if (connection)
10091                 g_object_unref (connection);
10092         g_free (nmc_tab_completion.con_type);
10093
10094         nmc->should_wait++;
10095         return nmc->return_value;
10096
10097 error:
10098         g_assert (!connection);
10099         g_free (type_ask);
10100
10101         nmc->should_wait++;
10102         return nmc->return_value;
10103 }
10104
10105
10106 static void
10107 modify_connection_cb (GObject *connection,
10108                       GAsyncResult *result,
10109                       gpointer user_data)
10110 {
10111         NmCli *nmc = (NmCli *) user_data;
10112         GError *error = NULL;
10113
10114         if (!nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
10115                                                          result, &error)) {
10116                 g_string_printf (nmc->return_text,
10117                                  _("Error: Failed to modify connection '%s': %s"),
10118                                  nm_connection_get_id (NM_CONNECTION (connection)),
10119                                  error->message);
10120                 g_error_free (error);
10121                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10122         } else {
10123                 if (nmc->print_output == NMC_PRINT_PRETTY)
10124                         g_print (_("Connection '%s' (%s) successfully modified.\n"),
10125                                  nm_connection_get_id (NM_CONNECTION (connection)),
10126                                  nm_connection_get_uuid (NM_CONNECTION (connection)));
10127         }
10128         quit ();
10129 }
10130
10131 static NMCResultCode
10132 do_connection_modify (NmCli *nmc,
10133                       gboolean temporary,
10134                       int argc,
10135                       char **argv)
10136 {
10137         NMConnection *connection = NULL;
10138         NMRemoteConnection *rc = NULL;
10139         const char *name;
10140         const char *selector = NULL;
10141         GError *error = NULL;
10142
10143         nmc->return_value = NMC_RESULT_SUCCESS;
10144
10145         if (argc == 0) {
10146                 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10147                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10148                 goto finish;
10149         }
10150         if (   strcmp (*argv, "id") == 0
10151             || strcmp (*argv, "uuid") == 0
10152             || strcmp (*argv, "path") == 0) {
10153
10154                 selector = *argv;
10155                 if (next_arg (&argc, &argv) != 0) {
10156                         g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10157                                          selector);
10158                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10159                         goto finish;
10160                 }
10161                 name = *argv;
10162         }
10163         name = *argv;
10164         if (!name) {
10165                 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10166                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10167                 goto finish;
10168         }
10169         connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10170         if (!connection) {
10171                 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10172                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10173                 goto finish;
10174         }
10175         rc = nm_client_get_connection_by_uuid (nmc->client,
10176                                                nm_connection_get_uuid (connection));
10177         if (!rc) {
10178                 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10179                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10180                 goto finish;
10181         }
10182
10183         if (next_arg (&argc, &argv) != 0) {
10184                 g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing."));
10185                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10186                 goto finish;
10187         }
10188
10189         if (!read_connection_properties (NM_CONNECTION (rc), argc, argv, &error)) {
10190                 g_string_assign (nmc->return_text, error->message);
10191                 nmc->return_value = error->code;
10192                 g_clear_error (&error);
10193                 goto finish;
10194         }
10195
10196         update_connection (!temporary, rc, modify_connection_cb, nmc);
10197
10198         nmc->should_wait++;
10199 finish:
10200         return nmc->return_value;
10201 }
10202
10203 typedef struct {
10204         NmCli *nmc;
10205         char *orig_id;
10206         char *orig_uuid;
10207         char *con_id;
10208 } CloneConnectionInfo;
10209
10210 static void
10211 clone_connection_cb (GObject *client,
10212                      GAsyncResult *result,
10213                      gpointer user_data)
10214 {
10215         CloneConnectionInfo *info = (CloneConnectionInfo *) user_data;
10216         NmCli *nmc = info->nmc;
10217         NMRemoteConnection *connection;
10218         GError *error = NULL;
10219
10220         connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
10221         if (error) {
10222                 g_string_printf (nmc->return_text,
10223                                  _("Error: Failed to add '%s' connection: %s"),
10224                                  info->con_id, error->message);
10225                 g_error_free (error);
10226                 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
10227         } else {
10228                 g_print (_("%s (%s) cloned as %s (%s).\n"),
10229                          info->orig_id,
10230                          info->orig_uuid,
10231                          nm_connection_get_id (NM_CONNECTION (connection)),
10232                          nm_connection_get_uuid (NM_CONNECTION (connection)));
10233                 g_object_unref (connection);
10234         }
10235
10236         g_free (info->con_id);
10237         g_free (info->orig_id);
10238         g_free (info->orig_uuid);
10239         g_slice_free (CloneConnectionInfo, info);
10240         quit ();
10241 }
10242
10243 static NMCResultCode
10244 do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv)
10245 {
10246         NMConnection *connection = NULL;
10247         NMConnection *new_connection = NULL;
10248         NMSettingConnection *s_con;
10249         CloneConnectionInfo *info;
10250         const char *name;
10251         const char *new_name;
10252         char *name_ask = NULL;
10253         char *new_name_ask = NULL;
10254         const char *selector = NULL;
10255         char *uuid;
10256
10257         if (argc == 0) {
10258                 if (nmc->ask) {
10259                         name = name_ask = nmc_readline (PROMPT_CONNECTION);
10260                         new_name = new_name_ask = nmc_readline (_("New connection name: "));
10261                 } else {
10262                         g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10263                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10264                         goto finish;
10265                 }
10266         } else {
10267                 if (   strcmp (*argv, "id") == 0
10268                     || strcmp (*argv, "uuid") == 0
10269                     || strcmp (*argv, "path") == 0) {
10270
10271                         selector = *argv;
10272                         if (next_arg (&argc, &argv) != 0) {
10273                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10274                                                  selector);
10275                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10276                                 goto finish;
10277                         }
10278                 }
10279                 name = *argv;
10280                 if (next_arg (&argc, &argv) != 0) {
10281                         g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10282                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10283                         goto finish;
10284                 }
10285                 new_name = *argv;
10286                 if (next_arg (&argc, &argv) == 0) {
10287                         g_string_printf (nmc->return_text, _("Error: unexpected extra argument '%s'."), *argv);
10288                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10289                         goto finish;
10290                 }
10291         }
10292
10293         if (!name) {
10294                 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10295                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10296                 goto finish;
10297         }
10298         if (!new_name) {
10299                 g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10300                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10301                 goto finish;
10302         }
10303
10304         connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10305         if (!connection) {
10306                 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10307                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10308                 goto finish;
10309         }
10310
10311         /* Copy the connection */
10312         new_connection = nm_simple_connection_new_clone (connection);
10313
10314         s_con = nm_connection_get_setting_connection (new_connection);
10315         g_assert (s_con);
10316         uuid = nm_utils_uuid_generate ();
10317         g_object_set (s_con,
10318                       NM_SETTING_CONNECTION_ID, new_name,
10319                       NM_SETTING_CONNECTION_UUID, uuid,
10320                       NULL);
10321         g_free (uuid);
10322
10323         /* Merge secrets into the new connection */
10324         update_secrets_in_connection (NM_REMOTE_CONNECTION (connection), new_connection);
10325
10326         info = g_slice_new0 (CloneConnectionInfo);
10327         info->nmc = nmc;
10328         info->orig_id = g_strdup (nm_connection_get_id (connection));
10329         info->orig_uuid = g_strdup (nm_connection_get_uuid (connection));
10330         info->con_id = g_strdup (nm_connection_get_id (new_connection));
10331
10332         /* Add the new cloned connection to NetworkManager */
10333         add_new_connection (!temporary,
10334                             nmc->client,
10335                             new_connection,
10336                             clone_connection_cb,
10337                             info);
10338
10339         nmc->should_wait = TRUE;
10340 finish:
10341         if (new_connection)
10342                 g_object_unref (new_connection);
10343         g_free (name_ask);
10344         g_free (new_name_ask);
10345
10346         return nmc->return_value;
10347 }
10348
10349 static void
10350 delete_cb (GObject *con, GAsyncResult *result, gpointer user_data)
10351 {
10352         ConnectionCbInfo *info = (ConnectionCbInfo *) user_data;
10353         GError *error = NULL;
10354
10355         if (!nm_remote_connection_delete_finish (NM_REMOTE_CONNECTION (con), result, &error)) {
10356                 g_string_printf (info->nmc->return_text, _("Error: not all connections deleted."));
10357                 g_printerr (_("Error: Connection deletion failed: %s"),
10358                             error->message);
10359                 g_error_free (error);
10360                 info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL;
10361                 connection_cb_info_finish (info, con);
10362         } else {
10363                 if (info->nmc->nowait_flag)
10364                         connection_cb_info_finish (info, con);
10365         }
10366 }
10367
10368 static NMCResultCode
10369 do_connection_delete (NmCli *nmc, int argc, char **argv)
10370 {
10371         NMConnection *connection;
10372         ConnectionCbInfo *info = NULL;
10373         GSList *queue = NULL, *iter;
10374         char **arg_arr = NULL;
10375         char **arg_ptr = argv;
10376         int arg_num = argc;
10377         GString *invalid_cons = NULL;
10378         int pos = 0;
10379
10380         if (nmc->timeout == -1)
10381                 nmc->timeout = 10;
10382
10383         if (argc == 0) {
10384                 if (nmc->ask) {
10385                         char *line = nmc_readline (PROMPT_CONNECTIONS);
10386                         nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
10387                         g_free (line);
10388                         arg_ptr = arg_arr;
10389                 }
10390                 if (arg_num == 0) {
10391                         g_string_printf (nmc->return_text, _("Error: No connection specified."));
10392                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10393                         goto finish;
10394                 }
10395         }
10396
10397         while (arg_num > 0) {
10398                 const char *selector = NULL;
10399
10400                 if (   strcmp (*arg_ptr, "id") == 0
10401                     || strcmp (*arg_ptr, "uuid") == 0
10402                     || strcmp (*arg_ptr, "path") == 0) {
10403                         selector = *arg_ptr;
10404                         if (next_arg (&arg_num, &arg_ptr) != 0) {
10405                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10406                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10407                                 goto finish;
10408                         }
10409                 }
10410
10411                 connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10412                 if (connection) {
10413                         /* Check if the connection is unique. */
10414                         /* Calling delete for the same connection repeatedly would result in
10415                          * NM responding for the last D-Bus call only and we would stall. */
10416                         if (!g_slist_find (queue, connection))
10417                                 queue = g_slist_prepend (queue, g_object_ref (connection));
10418                 } else {
10419                         g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10420                         g_string_printf (nmc->return_text, _("Error: not all active connections found."));
10421                         nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10422                         if (!invalid_cons)
10423                                 invalid_cons = g_string_new (NULL);
10424                         g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr);
10425                 }
10426
10427                 /* Take next argument (if there's no other connection of the same name) */
10428                 if (!pos)
10429                         next_arg (&arg_num, &arg_ptr);
10430         }
10431
10432         if (!queue) {
10433                 g_string_printf (nmc->return_text, _("Error: no connection provided."));
10434                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10435                 goto finish;
10436         }
10437         queue = g_slist_reverse (queue);
10438
10439         info = g_slice_new0 (ConnectionCbInfo);
10440         info->nmc = nmc;
10441         info->queue = queue;
10442         info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
10443
10444         nmc->nowait_flag = (nmc->timeout == 0);
10445         nmc->should_wait++;
10446
10447         g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED,
10448                           G_CALLBACK (connection_removed_cb), info);
10449
10450         /* Now delete the connections */
10451         for (iter = queue; iter; iter = g_slist_next (iter))
10452                 nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (iter->data),
10453                                                    NULL, delete_cb, info);
10454
10455 finish:
10456         if (invalid_cons) {
10457                 g_string_truncate (invalid_cons, invalid_cons->len-2);  /* truncate trailing ", " */
10458                 g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."),
10459                                  invalid_cons->str);
10460                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10461                 g_string_free (invalid_cons, TRUE);
10462         }
10463         g_strfreev (arg_arr);
10464         return nmc->return_value;
10465 }
10466
10467 static void
10468 connection_changed (NMConnection *connection, NmCli *nmc)
10469 {
10470         g_print (_("%s: connection profile changed\n"), nm_connection_get_id (connection));
10471 }
10472
10473 static void
10474 connection_watch (NmCli *nmc, NMConnection *connection)
10475 {
10476         nmc->should_wait++;
10477         g_signal_connect (connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed), nmc);
10478 }
10479
10480 static void
10481 connection_unwatch (NmCli *nmc, NMConnection *connection)
10482 {
10483         if (g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_changed), nmc))
10484                 nmc->should_wait--;
10485
10486         /* Terminate if all the watched connections disappeared. */
10487         if (!nmc->should_wait)
10488                 quit ();
10489 }
10490
10491 static void
10492 connection_added (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10493 {
10494         NMConnection *connection = NM_CONNECTION (con);
10495
10496         g_print (_("%s: connection profile created\n"), nm_connection_get_id (connection));
10497         connection_watch (nmc, connection);
10498 }
10499
10500 static void
10501 connection_removed (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10502 {
10503         NMConnection *connection = NM_CONNECTION (con);
10504
10505         g_print (_("%s: connection profile removed\n"), nm_connection_get_id (connection));
10506         connection_unwatch (nmc, connection);
10507 }
10508
10509 static NMCResultCode
10510 do_connection_monitor (NmCli *nmc, int argc, char **argv)
10511 {
10512         if (argc == 0) {
10513                 /* No connections specified. Monitor all. */
10514                 int i;
10515
10516                 nmc->connections = nm_client_get_connections (nmc->client);
10517                 for (i = 0; i < nmc->connections->len; i++)
10518                         connection_watch (nmc, g_ptr_array_index (nmc->connections, i));
10519
10520                 /* We'll watch the connection additions too, never exit. */
10521                 nmc->should_wait++;
10522                 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_ADDED, G_CALLBACK (connection_added), nmc);
10523         } else {
10524                 /* Look up the specified connections and watch them. */
10525                 NMConnection *connection;
10526                 char **arg_ptr = argv;
10527                 int arg_num = argc;
10528                 int pos = 0;
10529
10530                 do {
10531                         const char *selector = NULL;
10532
10533                         if (   strcmp (*arg_ptr, "id") == 0
10534                             || strcmp (*arg_ptr, "uuid") == 0
10535                             || strcmp (*arg_ptr, "path") == 0) {
10536                                 selector = *arg_ptr;
10537                                 if (next_arg (&arg_num, &arg_ptr) != 0) {
10538                                         g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10539                                         return NMC_RESULT_ERROR_USER_INPUT;
10540                                 }
10541                         }
10542
10543                         connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10544                         if (connection) {
10545                                 connection_watch (nmc, connection);
10546                         } else {
10547                                 g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10548                                 g_string_printf (nmc->return_text, _("Error: not all connections found."));
10549                                 return NMC_RESULT_ERROR_NOT_FOUND;
10550                         }
10551
10552                         /* Take next argument (if there's no other connection of the same name) */
10553                         if (!pos)
10554                                 next_arg (&arg_num, &arg_ptr);
10555                 } while (arg_num > 0);
10556         }
10557
10558         g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED, G_CALLBACK (connection_removed), nmc);
10559
10560         return NMC_RESULT_SUCCESS;
10561 }
10562
10563 static NMCResultCode
10564 do_connection_reload (NmCli *nmc, int argc, char **argv)
10565 {
10566         GError *error = NULL;
10567
10568         nmc->return_value = NMC_RESULT_SUCCESS;
10569
10570         if (!nm_client_reload_connections (nmc->client, NULL, &error)) {
10571                 g_string_printf (nmc->return_text, _("Error: failed to reload connections: %s."),
10572                                  error->message);
10573                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10574                 g_clear_error (&error);
10575         }
10576
10577         return nmc->return_value;
10578 }
10579
10580 static NMCResultCode
10581 do_connection_load (NmCli *nmc, int argc, char **argv)
10582 {
10583         GError *error = NULL;
10584         char **filenames, **failures = NULL;
10585         int i;
10586
10587         nmc->return_value = NMC_RESULT_SUCCESS;
10588
10589         if (argc == 0) {
10590                 g_string_printf (nmc->return_text, _("Error: No connection specified."));
10591                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10592                 return nmc->return_value;
10593         }
10594
10595         filenames = g_new (char *, argc + 1);
10596         for (i = 0; i < argc; i++)
10597                 filenames[i] = argv[i];
10598         filenames[i] = NULL;
10599
10600         nm_client_load_connections (nmc->client, filenames, &failures, NULL, &error);
10601         g_free (filenames);
10602         if (error) {
10603                 g_string_printf (nmc->return_text, _("Error: failed to load connection: %s."),
10604                                  error->message);
10605                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10606                 g_error_free (error);
10607         }
10608
10609         if (failures) {
10610                 for (i = 0; failures[i]; i++)
10611                         g_printerr (_("Could not load file '%s'\n"), failures[i]);
10612                 g_strfreev (failures);
10613         }
10614
10615         return nmc->return_value;
10616 }
10617
10618 // FIXME: change the text when non-VPN connection types are supported
10619 #define PROMPT_IMPORT_TYPE  PROMPT_VPN_TYPE
10620 #define PROMPT_IMPORT_FILE _("File to import: ")
10621
10622 static NMCResultCode
10623 do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv)
10624 {
10625         GError *error = NULL;
10626         const char *type = NULL, *filename = NULL;
10627         char *type_ask = NULL, *filename_ask = NULL;
10628         AddConnectionInfo *info;
10629         NMConnection *connection = NULL;
10630         NMVpnEditorPlugin *plugin;
10631
10632         if (argc == 0) {
10633                 if (nmc->ask) {
10634                         type_ask = nmc_readline (PROMPT_IMPORT_TYPE);
10635                         filename_ask = nmc_readline (PROMPT_IMPORT_FILE);
10636                         type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10637                         filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL;
10638                 } else {
10639                         g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10640                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10641                         goto finish;
10642                 }
10643         }
10644
10645         while (argc > 0) {
10646                 if (strcmp (*argv, "type") == 0) {
10647                         if (next_arg (&argc, &argv) != 0) {
10648                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10649                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10650                                 goto finish;
10651                         }
10652                         if (!type)
10653                                 type = *argv;
10654                         else
10655                                 g_printerr (_("Warning: 'type' already specified, ignoring extra one.\n"));
10656
10657                 } else if (strcmp (*argv, "file") == 0) {
10658                         if (next_arg (&argc, &argv) != 0) {
10659                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10660                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10661                                 goto finish;
10662                         }
10663                         if (!filename)
10664                                 filename = *argv;
10665                         else
10666                                 g_printerr (_("Warning: 'file' already specified, ignoring extra one.\n"));
10667                 } else {
10668                         g_string_printf (nmc->return_text, _("Unknown parameter: %s"), *argv);
10669                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10670                         goto finish;
10671                 }
10672
10673                 argc--;
10674                 argv++;
10675         }
10676
10677         if (!type) {
10678                 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
10679                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10680                 goto finish;
10681         }
10682         if (!filename) {
10683                 g_string_printf (nmc->return_text, _("Error: 'file' argument is required."));
10684                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10685                 goto finish;
10686         }
10687
10688         /* Import VPN configuration */
10689         plugin = nm_vpn_get_plugin_by_service (type, &error);
10690         if (!plugin) {
10691                 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10692                                  error->message);
10693                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10694                 goto finish;
10695         }
10696
10697         connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
10698         if (!connection) {
10699                 g_string_printf (nmc->return_text, _("Error: failed to import '%s': %s."),
10700                                  filename, error->message);
10701                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10702                 goto finish;
10703         }
10704
10705         info = g_malloc0 (sizeof (AddConnectionInfo));
10706         info->nmc = nmc;
10707         info->con_name = g_strdup (nm_connection_get_id (connection));
10708
10709         /* Add the new imported connection to NetworkManager */
10710         add_new_connection (!temporary,
10711                             nmc->client,
10712                             connection,
10713                             add_connection_cb,
10714                             info);
10715
10716         nmc->should_wait = TRUE;
10717 finish:
10718         if (connection)
10719                 g_object_unref (connection);
10720         g_clear_error (&error);
10721         g_free (type_ask);
10722         g_free (filename_ask);
10723         return nmc->return_value;
10724 }
10725
10726 static NMCResultCode
10727 do_connection_export (NmCli *nmc, int argc, char **argv)
10728 {
10729         NMConnection *connection = NULL;
10730         const char *name;
10731         const char *out_name = NULL;
10732         char *name_ask = NULL;
10733         char *out_name_ask = NULL;
10734         const char *path = NULL;
10735         const char *selector = NULL;
10736         const char *type = NULL;
10737         NMVpnEditorPlugin *plugin;
10738         GError *error = NULL;
10739         char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX";
10740
10741         if (argc == 0) {
10742                 if (nmc->ask) {
10743                         name_ask = nmc_readline (PROMPT_VPN_CONNECTION);
10744                         name = name_ask = name_ask ? g_strstrip (name_ask) : NULL;
10745                         out_name = out_name_ask = nmc_readline (_("Output file name: "));
10746                 } else {
10747                         g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10748                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10749                         goto finish;
10750                 }
10751         } else {
10752                 if (   strcmp (*argv, "id") == 0
10753                     || strcmp (*argv, "uuid") == 0
10754                     || strcmp (*argv, "path") == 0) {
10755
10756                         selector = *argv;
10757                         if (next_arg (&argc, &argv) != 0) {
10758                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10759                                                  selector);
10760                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10761                                 goto finish;
10762                         }
10763                 }
10764                 name = *argv;
10765                 if (next_arg (&argc, &argv) == 0)
10766                         out_name = *argv;
10767
10768                 if (next_arg (&argc, &argv) == 0) {
10769                         g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv);
10770                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10771                         goto finish;
10772                 }
10773         }
10774
10775         if (!name) {
10776                 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10777                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10778                 goto finish;
10779         }
10780         connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10781         if (!connection) {
10782                 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10783                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10784                 goto finish;
10785         }
10786
10787         type = nm_connection_get_connection_type (connection);
10788         if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) != 0) {
10789                 g_string_printf (nmc->return_text, _("Error: the connection is not VPN."));
10790                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10791                 goto finish;
10792         }
10793         type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
10794
10795         /* Export VPN configuration */
10796         plugin = nm_vpn_get_plugin_by_service (type, &error);
10797         if (!plugin) {
10798                 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10799                                  error->message);
10800                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10801                 goto finish;
10802         }
10803
10804         if (out_name)
10805                 path = out_name;
10806         else {
10807                 int fd;
10808                 fd = g_mkstemp (tmpfile);
10809                 if (fd == -1) {
10810                         g_string_printf (nmc->return_text, _("Error: failed to create temporary file %s."), tmpfile);
10811                         nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10812                         goto finish;
10813                 }
10814                 close (fd);
10815                 path = tmpfile;
10816         }
10817
10818         if (!nm_vpn_editor_plugin_export (plugin, path, connection, &error)) {
10819                 g_string_printf (nmc->return_text, _("Error: failed to export '%s': %s."),
10820                                  nm_connection_get_id (connection), error->message);
10821                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10822                 goto finish;
10823         }
10824
10825         /* No output file -> copy data to stdout */
10826         if (!out_name) {
10827                 char *contents = NULL;
10828                 gsize len = 0;
10829                 if (!g_file_get_contents (path, &contents, &len, &error)) {
10830                         g_string_printf (nmc->return_text, _("Error: failed to read temporary file '%s': %s."),
10831                                          path, error->message);
10832                         nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10833                         goto finish;
10834                 }
10835                 g_print ("%s", contents);
10836                 g_free (contents);
10837         }
10838
10839 finish:
10840         if (!out_name && path)
10841                 unlink (path);
10842         g_clear_error (&error);
10843         g_free (name_ask);
10844         g_free (out_name_ask);
10845         return nmc->return_value;
10846 }
10847
10848
10849 typedef struct {
10850         NmCli *nmc;
10851         int argc;
10852         char **argv;
10853 } NmcEditorThreadData;
10854
10855 static GThread *editor_thread;
10856 static NmcEditorThreadData editor_thread_data;
10857
10858 /*
10859  * We need to run do_connection_edit() in a thread so that
10860  * glib main loop is not blocked and could receive and process D-Bus
10861  * return messages.
10862  */
10863 static gpointer
10864 connection_editor_thread_func (gpointer data)
10865 {
10866         NmcEditorThreadData *td = (NmcEditorThreadData *) data;
10867
10868         /* run editor for editing/adding connections */
10869         td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv);
10870
10871         /* quit glib main loop now that we are done with this thread */
10872         quit ();
10873
10874         return NULL;
10875 }
10876
10877 static char *
10878 gen_func_connection_names (const char *text, int state)
10879 {
10880         int i;
10881         const char **connections;
10882         char *ret;
10883
10884         if (nm_cli.connections->len == 0)
10885                 return NULL;
10886
10887         connections = g_new (const char *, nm_cli.connections->len + 1);
10888         for (i = 0; i < nm_cli.connections->len; i++) {
10889                 NMConnection *con = NM_CONNECTION (nm_cli.connections->pdata[i]);
10890                 const char *id = nm_connection_get_id (con);
10891                 connections[i] = id;
10892         }
10893         connections[i] = NULL;
10894
10895         ret = nmc_rl_gen_func_basic (text, state, connections);
10896
10897         g_free (connections);
10898         return ret;
10899 }
10900
10901 static char *
10902 gen_func_active_connection_names (const char *text, int state)
10903 {
10904         int i;
10905         const GPtrArray *acs;
10906         const char **connections;
10907         char *ret;
10908
10909         if (!nm_cli.client)
10910                 return NULL;
10911
10912         acs = nm_client_get_active_connections (nm_cli.client);
10913         if (!acs || acs->len == 0)
10914                 return NULL;
10915
10916         connections = g_new (const char *, acs->len + 1);
10917         for (i = 0; i < acs->len; i++)
10918                 connections[i] = nm_active_connection_get_id (acs->pdata[i]);
10919         connections[i] = NULL;
10920
10921         ret = nmc_rl_gen_func_basic (text, state, connections);
10922
10923         g_free (connections);
10924         return ret;
10925 }
10926
10927 static char **
10928 nmcli_con_tab_completion (const char *text, int start, int end)
10929 {
10930         char **match_array = NULL;
10931         rl_compentry_func_t *generator_func = NULL;
10932
10933         /* Disable readline's default filename completion */
10934         rl_attempted_completion_over = 1;
10935
10936         if (g_strcmp0 (rl_prompt, PROMPT_CONNECTION) == 0) {
10937                 /* Disable appending space after completion */
10938                 rl_completion_append_character = '\0';
10939
10940                 if (!is_single_word (rl_line_buffer))
10941                         return NULL;
10942
10943                 generator_func = gen_func_connection_names;
10944         } else if (g_strcmp0 (rl_prompt, PROMPT_CONNECTIONS) == 0) {
10945                 generator_func = gen_func_connection_names;
10946         } else if (g_strcmp0 (rl_prompt, PROMPT_ACTIVE_CONNECTIONS) == 0) {
10947                 generator_func = gen_func_active_connection_names;
10948         } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_TYPE) == 0) {
10949                 generator_func = gen_func_vpn_types;
10950         } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) {
10951                 rl_attempted_completion_over = 0;
10952                 rl_complete_with_tilde_expansion = 1;
10953         } else if (g_strcmp0 (rl_prompt, PROMPT_VPN_CONNECTION) == 0) {
10954                 generator_func = gen_vpn_ids;
10955         }
10956
10957         if (generator_func)
10958                 match_array = rl_completion_matches (text, generator_func);
10959
10960         return match_array;
10961 }
10962
10963 static GArray *
10964 parse_preferred_connection_order (const char *order, GError **error)
10965 {
10966         char **strv, **iter;
10967         const char *str;
10968         GArray *order_arr;
10969         NmcSortOrder val;
10970         gboolean inverse, unique;
10971         int i;
10972
10973         strv = nmc_strsplit_set (order, ":", -1);
10974         if (!strv || !*strv) {
10975                 g_set_error (error, NMCLI_ERROR, 0,
10976                              _("incorrect string '%s' of '--order' option"), order);
10977                 g_strfreev (strv);
10978                 return NULL;
10979         }
10980
10981         order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4);
10982         for (iter = strv; iter && *iter; iter++) {
10983                 str = *iter;
10984                 inverse = FALSE;
10985                 if (str[0] == '-')
10986                         inverse = TRUE;
10987                 if (str[0] == '+' || str[0] == '-')
10988                         str++;
10989
10990                 if (matches (str, "active") == 0)
10991                         val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE;
10992                 else if (matches (str, "name") == 0)
10993                         val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME;
10994                 else if (matches (str, "type") == 0)
10995                         val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE;
10996                 else if (matches (str, "path") == 0)
10997                         val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH;
10998                 else {
10999                         g_array_unref (order_arr);
11000                         order_arr = NULL;
11001                         g_set_error (error, NMCLI_ERROR, 0,
11002                                      _("incorrect item '%s' in '--order' option"), *iter);
11003                         break;
11004                 }
11005                 /* Check for duplicates and ignore them. */
11006                 unique = TRUE;
11007                 for (i = 0; i < order_arr->len; i++) {
11008                         if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) {
11009                                 unique = FALSE;
11010                                 break;
11011                         }
11012                 }
11013
11014                 /* Value is ok and unique, add it to the array */
11015                 if (unique)
11016                         g_array_append_val (order_arr, val);
11017         }
11018
11019         g_strfreev (strv);
11020         return order_arr;
11021 }
11022
11023 /* Entry point function for connections-related commands: 'nmcli connection' */
11024 NMCResultCode
11025 do_connections (NmCli *nmc, int argc, char **argv)
11026 {
11027         GError *error = NULL;
11028
11029         /* Register polkit agent */
11030         nmc_start_polkit_agent_start_try (nmc);
11031
11032         /* Set completion function for 'nmcli con' */
11033         rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion;
11034
11035         /* Exit early on help */
11036         if (nmc_arg_is_help (*argv)) {
11037                 usage ();
11038                 return nmc->return_value;
11039         }
11040         if (argc != 0 && nmc_arg_is_help (*(argv+1))) {
11041                 if (usage_connection_second_level (*argv))
11042                         return nmc->return_value;
11043         }
11044
11045         /* Get NMClient object early */
11046         nmc->get_client (nmc);
11047
11048         /* Check whether NetworkManager is running */
11049         if (!nm_client_get_nm_running (nmc->client)) {
11050                 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
11051                 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
11052                 return nmc->return_value;
11053         }
11054         /* Compare NM and nmcli versions */
11055         if (!nmc_versions_match (nmc))
11056                 return nmc->return_value;
11057
11058         /* Get the connection list */
11059         nmc->connections = nm_client_get_connections (nmc->client);
11060
11061         /* Now parse the command line and perform the required operation */
11062         if (argc == 0) {
11063                 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
11064                         goto opt_error;
11065                 nmc->return_value = do_connections_show (nmc, FALSE, FALSE, NULL, argc, argv);
11066         } else {
11067                 if (matches (*argv, "show") == 0) {
11068                         gboolean active = FALSE;
11069                         gboolean show_secrets = FALSE;
11070                         GArray *order = NULL;
11071                         int i;
11072
11073                         next_arg (&argc, &argv);
11074                         /* check connection show options [--active] [--show-secrets] */
11075                         for (i = 0; i < 3; i++) {
11076                                 if (!active && nmc_arg_is_option (*argv, "active")) {
11077                                         active = TRUE;
11078                                         next_arg (&argc, &argv);
11079                                 }
11080                                 /* --show-secrets is deprecated in favour of global --show-secrets */
11081                                 /* Keep it here for backwards compatibility */
11082                                 if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) {
11083                                         show_secrets = TRUE;
11084                                         next_arg (&argc, &argv);
11085                                 }
11086                                 if (!order && nmc_arg_is_option (*argv, "order")) {
11087                                         if (next_arg (&argc, &argv) != 0) {
11088                                                 g_set_error_literal (&error, NMCLI_ERROR, 0,
11089                                                                      _("'--order' argument is missing"));
11090                                                 goto opt_error;
11091                                         }
11092                                         order = parse_preferred_connection_order (*argv, &error);
11093                                         if (error)
11094                                                 goto opt_error;
11095                                         next_arg (&argc, &argv);
11096                                 }
11097                         }
11098                         show_secrets = nmc->show_secrets || show_secrets;
11099                         nmc->return_value = do_connections_show (nmc, active, show_secrets, order, argc, argv);
11100                         if (order)
11101                                 g_array_unref (order);
11102                 } else if (matches(*argv, "up") == 0) {
11103                         nmc->return_value = do_connection_up (nmc, argc-1, argv+1);
11104                 } else if (matches(*argv, "down") == 0) {
11105                         nmc->return_value = do_connection_down (nmc, argc-1, argv+1);
11106                 } else if (matches(*argv, "add") == 0) {
11107                         nmc->return_value = do_connection_add (nmc, argc-1, argv+1);
11108                 } else if (matches(*argv, "edit") == 0) {
11109                         nmc->should_wait++;
11110                         editor_thread_data.nmc = nmc;
11111                         editor_thread_data.argc = argc - 1;
11112                         editor_thread_data.argv = argv + 1;
11113                         editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data);
11114                         g_thread_unref (editor_thread);
11115                 } else if (matches(*argv, "delete") == 0) {
11116                         nmc->return_value = do_connection_delete (nmc, argc-1, argv+1);
11117                 } else if (matches(*argv, "reload") == 0) {
11118                         nmc->return_value = do_connection_reload (nmc, argc-1, argv+1);
11119                 } else if (matches(*argv, "load") == 0) {
11120                         nmc->return_value = do_connection_load (nmc, argc-1, argv+1);
11121                 } else if (matches (*argv, "modify") == 0) {
11122                         gboolean temporary = FALSE;
11123
11124                         next_arg (&argc, &argv);
11125                         if (nmc_arg_is_option (*argv, "temporary")) {
11126                                 temporary = TRUE;
11127                                 next_arg (&argc, &argv);
11128                         }
11129                         nmc->return_value = do_connection_modify (nmc, temporary, argc, argv);
11130                 } else if (matches (*argv, "clone") == 0) {
11131                         gboolean temporary = FALSE;
11132
11133                         next_arg (&argc, &argv);
11134                         if (nmc_arg_is_option (*argv, "temporary")) {
11135                                 temporary = TRUE;
11136                                 next_arg (&argc, &argv);
11137                         }
11138                         nmc->return_value = do_connection_clone (nmc, temporary, argc, argv);
11139                 } else if (matches(*argv, "import") == 0) {
11140                         gboolean temporary = FALSE;
11141
11142                         next_arg (&argc, &argv);
11143                         if (nmc_arg_is_option (*argv, "temporary")) {
11144                                 temporary = TRUE;
11145                                 next_arg (&argc, &argv);
11146                         }
11147                         nmc->return_value = do_connection_import (nmc, temporary, argc, argv);
11148                 } else if (matches(*argv, "export") == 0) {
11149                         nmc->return_value = do_connection_export (nmc, argc-1, argv+1);
11150                 } else if (matches(*argv, "monitor") == 0) {
11151                         nmc->return_value = do_connection_monitor (nmc, argc-1, argv+1);
11152                 } else {
11153                         usage ();
11154                         g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
11155                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11156                 }
11157         }
11158
11159         return nmc->return_value;
11160
11161 opt_error:
11162         g_string_printf (nmc->return_text, _("Error: %s."), error->message);
11163         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11164         g_error_free (error);
11165         return nmc->return_value;
11166 }
11167
11168 void
11169 monitor_connections (NmCli *nmc)
11170 {
11171         do_connection_monitor (nmc, 0, NULL);
11172 }