cli: make the *-slave type option parsing out of the common path
[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                         return TRUE;
4787                 }
4788
4789                 if (!master && ask)
4790                         master = master_ask = nmc_readline (PROMPT_MASTER);
4791                 if (!master) {
4792                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4793                                              _("Error: 'master' is required."));
4794                         return FALSE;
4795                 }
4796                 /* Verify master argument */
4797                 checked_master = normalized_master_for_slave (all_connections, master, slave_type, NULL);
4798
4799                 /* Change properties in 'connection' setting */
4800                 g_object_set (s_con,
4801                               NM_SETTING_CONNECTION_MASTER, checked_master,
4802                               NULL);
4803
4804                 g_free (master_ask);
4805
4806                 return TRUE;
4807 }
4808
4809 static gboolean
4810 complete_connection_by_type (NMConnection *connection,
4811                              const char *con_type,
4812                              const GPtrArray *all_connections,
4813                              gboolean ask,
4814                              gboolean show_secrets,
4815                              int argc,
4816                              char **argv,
4817                              GError **error)
4818 {
4819         NMSettingConnection *s_con;
4820         NMSettingGeneric *s_generic;
4821         NMSettingWired *s_wired;
4822         NMSettingInfiniband *s_infiniband;
4823         NMSettingWireless *s_wifi;
4824         NMSettingWimax *s_wimax;
4825         NMSettingPppoe *s_pppoe;
4826         NMSettingGsm *s_gsm;
4827         NMSettingCdma *s_cdma;
4828         NMSettingBluetooth *s_bt;
4829         NMSettingVlan *s_vlan;
4830         NMSettingBond *s_bond;
4831         NMSettingTeam *s_team;
4832         NMSettingTeamPort *s_team_port;
4833         NMSettingBridge *s_bridge;
4834         NMSettingBridgePort *s_bridge_port;
4835         NMSettingVpn *s_vpn;
4836         NMSettingOlpcMesh *s_olpc_mesh;
4837         NMSettingAdsl *s_adsl;
4838         NMSettingTun *s_tun;
4839         NMSettingIPTunnel *s_ip_tunnel;
4840         NMSettingMacvlan *s_macvlan;
4841         NMSettingVxlan *s_vxlan;
4842         const char *slave_type;
4843
4844         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4845
4846         s_con = nm_connection_get_setting_connection (connection);
4847         g_assert (s_con);
4848
4849         if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) {
4850                 /* Build up the settings required for 'ethernet' */
4851                 gboolean success = FALSE;
4852                 const char *mtu_c = NULL;
4853                 char *mtu = NULL;
4854                 guint32 mtu_int = 0;
4855                 const char *mac_c = NULL;
4856                 char *mac = NULL;
4857                 const char *cloned_mac_c = NULL;
4858                 char *cloned_mac = NULL;
4859                 nmc_arg_t exp_args[] = { {"mtu",        TRUE, &mtu_c,        FALSE},
4860                                          {"mac",        TRUE, &mac_c,        FALSE},
4861                                          {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
4862                                          {NULL} };
4863
4864                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4865                         return FALSE;
4866
4867                 /* Also ask for all optional arguments if '--ask' is specified. */
4868                 mtu = g_strdup (mtu_c);
4869                 mac = g_strdup (mac_c);
4870                 cloned_mac = g_strdup (cloned_mac_c);
4871                 if (ask)
4872                         do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac);
4873
4874                 if (!check_and_convert_mtu (mtu, &mtu_int, error))
4875                         goto cleanup_wired;
4876                 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
4877                         goto cleanup_wired;
4878                 if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error))
4879                         goto cleanup_wired;
4880
4881                 /* Add ethernet setting */
4882                 s_wired = (NMSettingWired *) nm_setting_wired_new ();
4883                 nm_connection_add_setting (connection, NM_SETTING (s_wired));
4884
4885                 if (mtu)
4886                         g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
4887                 if (mac)
4888                         g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
4889                 if (cloned_mac)
4890                         g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_mac, NULL);
4891
4892                 success = TRUE;
4893 cleanup_wired:
4894                 g_free (mtu);
4895                 g_free (mac);
4896                 g_free (cloned_mac);
4897                 if (!success)
4898                         return FALSE;
4899
4900         } else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
4901                 /* Build up the settings required for 'infiniband' */
4902                 gboolean success = FALSE;
4903                 const char *mtu_c = NULL;
4904                 char *mtu = NULL;
4905                 guint32 mtu_int = 0;
4906                 const char *mac_c = NULL;
4907                 char *mac = NULL;
4908                 const char *mode_c = NULL;
4909                 char *mode = NULL;
4910                 const char *parent_c = NULL;
4911                 char *parent = NULL;
4912                 const char *p_key_c = NULL;
4913                 char *p_key = NULL;
4914                 guint32 p_key_int = 0;
4915                 nmc_arg_t exp_args[] = { {"mtu",            TRUE, &mtu_c,    FALSE},
4916                                          {"mac",            TRUE, &mac_c,    FALSE},
4917                                          {"transport-mode", TRUE, &mode_c,   FALSE},
4918                                          {"parent",         TRUE, &parent_c, FALSE},
4919                                          {"p-key",          TRUE, &p_key_c,  FALSE},
4920                                          {NULL} };
4921
4922                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4923                         return FALSE;
4924
4925                 /* Also ask for all optional arguments if '--ask' is specified. */
4926                 mtu = g_strdup (mtu_c);
4927                 mac = g_strdup (mac_c);
4928                 mode = g_strdup (mode_c);
4929                 parent = g_strdup (parent_c);
4930                 p_key = g_strdup (p_key_c);
4931                 if (ask)
4932                         do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key);
4933
4934                 if (!check_and_convert_mtu (mtu, &mtu_int, error))
4935                         goto cleanup_ib;
4936                 if (!check_mac (mac, ARPHRD_INFINIBAND, "mac", error))
4937                         goto cleanup_ib;
4938                 if (!check_infiniband_mode (&mode, error))
4939                         goto cleanup_ib;
4940                 if (p_key) {
4941                         if (!check_infiniband_p_key (p_key, &p_key_int, error))
4942                                 goto cleanup_ib;
4943                         if (!check_infiniband_parent (parent, error))
4944                                 goto cleanup_ib;
4945                 } else if (parent) {
4946                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4947                                      _("Error: 'parent': not valid without 'p-key'."));
4948                         goto cleanup_ib;
4949                 }
4950
4951                 /* Add 'infiniband' setting */
4952                 s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
4953                 nm_connection_add_setting (connection, NM_SETTING (s_infiniband));
4954
4955                 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL);
4956                 if (mtu)
4957                         g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL);
4958                 if (mac)
4959                         g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
4960                 if (p_key)
4961                         g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL);
4962                 if (parent)
4963                         g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL);
4964
4965
4966                 success = TRUE;
4967 cleanup_ib:
4968                 g_free (mtu);
4969                 g_free (mac);
4970                 g_free (mode);
4971                 g_free (parent);
4972                 g_free (p_key);
4973                 if (!success)
4974                         return FALSE;
4975
4976         } else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) {
4977                 /* Build up the settings required for 'wifi' */
4978                 gboolean success = FALSE;
4979                 char *ssid_ask = NULL;
4980                 const char *ssid = NULL;
4981                 GBytes *ssid_bytes;
4982                 const char *mtu_c = NULL;
4983                 char *mtu = NULL;
4984                 guint32 mtu_int = 0;
4985                 const char *mac_c = NULL;
4986                 char *mac = NULL;
4987                 const char *cloned_mac_c = NULL;
4988                 char *cloned_mac = NULL;
4989                 const char *mode_c = NULL;
4990                 char *mode = NULL;
4991                 nmc_arg_t exp_args[] = { {"ssid",       TRUE, &ssid,         !ask},
4992                                          {"mtu",        TRUE, &mtu_c,        FALSE},
4993                                          {"mac",        TRUE, &mac_c,        FALSE},
4994                                          {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
4995                                          {"mode",       TRUE, &mode_c,       FALSE},
4996                                          {NULL} };
4997
4998                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4999                         return FALSE;
5000
5001                 if (!ssid && ask)
5002                         ssid = ssid_ask = nmc_readline (_("SSID: "));
5003                 if (!ssid) {
5004                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5005                                              _("Error: 'ssid' is required."));
5006                         return FALSE;
5007                 }
5008
5009                 /* Also ask for all optional arguments if '--ask' is specified. */
5010                 mtu = g_strdup (mtu_c);
5011                 mac = g_strdup (mac_c);
5012                 cloned_mac = g_strdup (cloned_mac_c);
5013                 mode = g_strdup (mode_c);
5014                 if (ask)
5015                         do_questionnaire_wifi (&mtu, &mac, &cloned_mac, &mode);
5016
5017                 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5018                         goto cleanup_wifi;
5019                 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5020                         goto cleanup_wifi;
5021                 if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error))
5022                         goto cleanup_wifi;
5023                 if (!check_wifi_mode (&mode, error))
5024                         goto cleanup_wifi;
5025
5026                 /* Add wifi setting */
5027                 s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
5028                 nm_connection_add_setting (connection, NM_SETTING (s_wifi));
5029
5030                 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
5031                 g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_bytes, NULL);
5032
5033                 if (mtu)
5034                         g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL);
5035                 if (mac)
5036                         g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL);
5037                 if (cloned_mac)
5038                         g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac, NULL);
5039                 if (mode)
5040                         g_object_set (s_wifi, NM_SETTING_WIRELESS_MODE, mode, NULL);
5041
5042                 g_bytes_unref (ssid_bytes);
5043
5044                 success = TRUE;
5045 cleanup_wifi:
5046                 g_free (ssid_ask);
5047                 g_free (mtu);
5048                 g_free (mac);
5049                 g_free (cloned_mac);
5050                 g_free (mode);
5051                 if (!success)
5052                         return FALSE;
5053
5054         } else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) {
5055                 /* Build up the settings required for 'wimax' */
5056                 gboolean success = FALSE;
5057                 const char *nsp_name = NULL;
5058                 char *nsp_name_ask = NULL;
5059                 const char *mac_c = NULL;
5060                 char *mac = NULL;
5061                 nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask},
5062                                          {"mac", TRUE, &mac_c,    FALSE},
5063                                          {NULL} };
5064
5065                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5066                         return FALSE;
5067
5068                 if (!nsp_name && ask)
5069                         nsp_name = nsp_name_ask = nmc_readline (_("WiMAX NSP name: "));
5070                 if (!nsp_name) {
5071                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5072                                              _("Error: 'nsp' is required."));
5073                         goto cleanup_wimax;
5074                 }
5075
5076                 /* Also ask for all optional arguments if '--ask' is specified. */
5077                 mac = g_strdup (mac_c);
5078                 if (ask)
5079                         do_questionnaire_wimax (&mac);
5080
5081                 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5082                         goto cleanup_wimax;
5083
5084                 /* Add 'wimax' setting */
5085                 s_wimax = (NMSettingWimax *) nm_setting_wimax_new ();
5086                 nm_connection_add_setting (connection, NM_SETTING (s_wimax));
5087                 g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL);
5088
5089                 if (mac)
5090                         g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac, NULL);
5091
5092                 success = TRUE;
5093 cleanup_wimax:
5094                 g_free (nsp_name_ask);
5095                 g_free (mac);
5096                 if (!success)
5097                         return FALSE;
5098
5099         } else if (!strcmp (con_type, NM_SETTING_PPPOE_SETTING_NAME)) {
5100                 /* Build up the settings required for 'pppoe' */
5101                 gboolean success = FALSE;
5102                 const char *username = NULL;
5103                 char *username_ask = NULL;
5104                 const char *password_c = NULL;
5105                 char *password = NULL;
5106                 const char *service_c = NULL;
5107                 char *service = NULL;
5108                 const char *mtu_c = NULL;
5109                 char *mtu = NULL;
5110                 guint32 mtu_int = 0;
5111                 const char *mac_c = NULL;
5112                 char *mac = NULL;
5113                 nmc_arg_t exp_args[] = { {"username", TRUE, &username,   !ask},
5114                                          {"password", TRUE, &password_c, FALSE},
5115                                          {"service",  TRUE, &service_c,  FALSE},
5116                                          {"mtu",      TRUE, &mtu_c,      FALSE},
5117                                          {"mac",      TRUE, &mac_c,      FALSE},
5118                                          {NULL} };
5119
5120                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5121                         return FALSE;
5122
5123                 if (!username && ask)
5124                         username = username_ask = nmc_readline (_("PPPoE username: "));
5125                 if (!username) {
5126                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5127                                              _("Error: 'username' is required."));
5128                         goto cleanup_pppoe;
5129                 }
5130
5131                 /* Also ask for all optional arguments if '--ask' is specified. */
5132                 password = g_strdup (password_c);
5133                 service = g_strdup (service_c);
5134                 mtu = g_strdup (mtu_c);
5135                 mac = g_strdup (mac_c);
5136                 if (ask)
5137                         do_questionnaire_pppoe (show_secrets, &password, &service, &mtu, &mac);
5138
5139                 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5140                         goto cleanup_pppoe;
5141                 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5142                         goto cleanup_pppoe;
5143
5144                 /* Add 'pppoe' setting */
5145                 s_pppoe = (NMSettingPppoe *) nm_setting_pppoe_new ();
5146                 nm_connection_add_setting (connection, NM_SETTING (s_pppoe));
5147                 g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, username, NULL);
5148                 g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, password, NULL);
5149                 g_object_set (s_pppoe, NM_SETTING_PPPOE_SERVICE, service, NULL);
5150
5151                 /* Add ethernet setting */
5152                 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5153                 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5154                 if (mtu)
5155                         g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
5156                 if (mac)
5157                         g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
5158
5159                 success = TRUE;
5160 cleanup_pppoe:
5161                 g_free (username_ask);
5162                 g_free (password);
5163                 g_free (service);
5164                 g_free (mtu);
5165                 g_free (mac);
5166                 if (!success)
5167                         return FALSE;
5168
5169         } else if (   !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME)
5170                    || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) {
5171                 /* Build up the settings required for 'gsm' or 'cdma' mobile broadband */
5172                 gboolean success = FALSE;
5173                 const char *apn = NULL;
5174                 char *apn_ask = NULL;
5175                 const char *user_c = NULL;
5176                 char *user = NULL;
5177                 const char *password_c = NULL;
5178                 char *password = NULL;
5179                 gboolean is_gsm;
5180                 int i = 0;
5181                 nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */
5182                                          {NULL} };
5183
5184                 is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME);
5185
5186                 if (is_gsm)
5187                         gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask};
5188                 gsm_args[i++] = (nmc_arg_t) {"user",     TRUE, &user_c,     FALSE};
5189                 gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE};
5190                 gsm_args[i++] = (nmc_arg_t) {NULL};
5191
5192                 if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error))
5193                         return FALSE;
5194
5195                 if (!apn && ask && is_gsm)
5196                         apn = apn_ask = nmc_readline (_("APN: "));
5197                 if (!apn && is_gsm) {
5198                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5199                                              _("Error: 'apn' is required."));
5200                         goto cleanup_mobile;
5201                 }
5202
5203                 /* Also ask for all optional arguments if '--ask' is specified. */
5204                 user = g_strdup (user_c);
5205                 password = g_strdup (password_c);
5206                 if (ask)
5207                         do_questionnaire_mobile (show_secrets, &user, &password);
5208
5209                 if (is_gsm) {
5210                         g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL);
5211
5212                         /* Add 'gsm' setting */
5213                         s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
5214                         nm_connection_add_setting (connection, NM_SETTING (s_gsm));
5215                         g_object_set (s_gsm,
5216                                       NM_SETTING_GSM_NUMBER, "*99#",
5217                                       NM_SETTING_GSM_APN, apn,
5218                                       NM_SETTING_GSM_USERNAME, user,
5219                                       NM_SETTING_GSM_PASSWORD, password,
5220                                       NULL);
5221                         g_free (apn_ask);
5222                 } else {
5223                         g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL);
5224
5225                         /* Add 'cdma' setting */
5226                         s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
5227                         nm_connection_add_setting (connection, NM_SETTING (s_cdma));
5228                         g_object_set (s_cdma,
5229                                       NM_SETTING_CDMA_NUMBER, "#777",
5230                                       NM_SETTING_CDMA_USERNAME, user,
5231                                       NM_SETTING_CDMA_PASSWORD, password,
5232                                       NULL);
5233                 }
5234
5235                 success = TRUE;
5236 cleanup_mobile:
5237                 g_free (user);
5238                 g_free (password);
5239                 if (!success)
5240                         return FALSE;
5241
5242         } else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
5243                 /* Build up the settings required for 'bluetooth' */
5244                 gboolean success = FALSE;
5245                 const char *addr = NULL;
5246                 char *addr_ask = NULL;
5247                 const char *bt_type_c = NULL;
5248                 char *bt_type = NULL;
5249                 nmc_arg_t exp_args[] = { {"addr",    TRUE, &addr,      !ask},
5250                                          {"bt-type", TRUE, &bt_type_c, FALSE},
5251                                          {NULL} };
5252
5253                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5254                         return FALSE;
5255
5256                 if (!addr && ask)
5257                         addr = addr_ask = nmc_readline (_("Bluetooth device address: "));
5258                 if (!addr) {
5259                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5260                                              _("Error: 'addr' is required."));
5261                         return FALSE;
5262                 }
5263                 if (!check_mac (addr, ARPHRD_ETHER, "addr", error))
5264                         goto cleanup_bt;
5265
5266                 /* Also ask for all optional arguments if '--ask' is specified. */
5267                 bt_type = g_strdup (bt_type_c);
5268                 if (ask)
5269                         do_questionnaire_bluetooth (&bt_type);
5270
5271                 /* Default to 'panu' if bt-type is not provided. */
5272                 if (!bt_type)
5273                         bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU);
5274
5275                 /* Add 'bluetooth' setting */
5276                 s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new ();
5277                 nm_connection_add_setting (connection, NM_SETTING (s_bt));
5278
5279                 if (addr)
5280                         g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, addr, NULL);
5281
5282                 /* 'dun' type requires adding 'gsm' or 'cdma' setting */
5283                 if (   !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
5284                     || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) {
5285                         bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
5286                         s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
5287                         nm_connection_add_setting (connection, NM_SETTING (s_gsm));
5288                         g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
5289 //                      g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL;
5290
5291                 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) {
5292                         bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
5293                         s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
5294                         nm_connection_add_setting (connection, NM_SETTING (s_cdma));
5295                         g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL);
5296
5297                 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
5298                         /* no op */
5299                 } else {
5300                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5301                                      _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."),
5302                                      bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN,
5303                                      NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma");
5304                         goto cleanup_bt;
5305                 }
5306                 g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL);
5307
5308                 success = TRUE;
5309 cleanup_bt:
5310                 g_free (addr_ask);
5311                 g_free (bt_type);
5312                 if (!success)
5313                         return FALSE;
5314
5315         } else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) {
5316                 /* Build up the settings required for 'vlan' */
5317                 gboolean success = FALSE;
5318                 const char *parent = NULL;
5319                 char *parent_ask = NULL;
5320                 const char *vlan_id = NULL;
5321                 char *vlan_id_ask = NULL;
5322                 unsigned long id = 0;
5323                 const char *flags_c = NULL;
5324                 char *flags = NULL;
5325                 guint32 flags_int = 0;
5326                 const char *ingress_c = NULL, *egress_c = NULL;
5327                 char *ingress = NULL, *egress = NULL;
5328                 char **ingress_arr = NULL, **egress_arr = NULL, **p;
5329                 const char *mtu_c = NULL;
5330                 char *mtu = NULL;
5331                 guint32 mtu_int;
5332                 gboolean valid_mac = FALSE;
5333                 nmc_arg_t exp_args[] = { {"dev",     TRUE, &parent,    !ask},
5334                                          {"id",      TRUE, &vlan_id,   !ask},
5335                                          {"flags",   TRUE, &flags_c,   FALSE},
5336                                          {"ingress", TRUE, &ingress_c, FALSE},
5337                                          {"egress",  TRUE, &egress_c,  FALSE},
5338                                          {"mtu",     TRUE, &mtu_c,     FALSE},
5339                                          {NULL} };
5340
5341                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5342                         return FALSE;
5343
5344                 if (!parent && ask)
5345                         parent = parent_ask = nmc_readline (_("VLAN parent device or connection UUID: "));
5346                 if (!parent) {
5347                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5348                                              _("Error: 'dev' is required."));
5349                         return FALSE;
5350                 }
5351                 if (!vlan_id && ask)
5352                         vlan_id = vlan_id_ask = nmc_readline (_("VLAN ID <0-4094>: "));
5353                 if (!vlan_id) {
5354                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5355                                              _("Error: 'id' is required."));
5356                         goto cleanup_vlan;
5357                 }
5358                 if (vlan_id) {
5359                         if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4094, &id)) {
5360                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5361                                              _("Error: 'id': '%s' is not valid; use <0-4094>."),
5362                                              vlan_id);
5363                                 goto cleanup_vlan;
5364                         }
5365                 }
5366
5367                 if (   !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
5368                     && !nm_utils_is_uuid (parent)
5369                     && !nm_utils_iface_valid_name (parent)) {
5370                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5371                                      _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
5372                                      parent);
5373                         goto cleanup_vlan;
5374                 }
5375
5376                 /* Also ask for all optional arguments if '--ask' is specified. */
5377                 mtu = g_strdup (mtu_c);
5378                 flags = g_strdup (flags_c);
5379                 ingress = g_strdup (ingress_c);
5380                 egress = g_strdup (egress_c);
5381                 if (ask)
5382                         do_questionnaire_vlan (&mtu, &flags, &ingress, &egress);
5383
5384                 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5385                         goto cleanup_vlan;
5386                 if (!check_and_convert_vlan_flags (flags, &flags_int, error))
5387                         goto cleanup_vlan;
5388                 if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error))
5389                         goto cleanup_vlan;
5390                 if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &egress_arr, error))
5391                         goto cleanup_vlan;
5392
5393                 /* Add 'vlan' setting */
5394                 s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
5395                 nm_connection_add_setting (connection, NM_SETTING (s_vlan));
5396
5397                 /* Add 'wired' setting if necessary */
5398                 if (mtu || valid_mac) {
5399                         s_wired = (NMSettingWired *) nm_setting_wired_new ();
5400                         nm_connection_add_setting (connection, NM_SETTING (s_wired));
5401
5402                         if (mtu)
5403                                 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
5404                         if (valid_mac)
5405                                 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
5406                 }
5407
5408                 /* Set 'vlan' properties */
5409                 if (!valid_mac)
5410                         g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL);
5411
5412                 g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL);
5413
5414                 if (flags)
5415                         g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL);
5416                 for (p = ingress_arr; p && *p; p++)
5417                         nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p);
5418                 for (p = egress_arr; p && *p; p++)
5419                         nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p);
5420
5421                 success = TRUE;
5422 cleanup_vlan:
5423                 g_free (mtu);
5424                 g_free (flags);
5425                 g_free (ingress);
5426                 g_free (egress);
5427                 g_free (parent_ask);
5428                 g_free (vlan_id_ask);
5429                 g_strfreev (ingress_arr);
5430                 g_strfreev (egress_arr);
5431                 if (!success)
5432                         return FALSE;
5433
5434         } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) {
5435                 /* Build up the settings required for 'bond' */
5436                 gboolean success = FALSE;
5437                 const char *ifname = NULL;
5438                 const char *bond_mode_c = NULL;
5439                 char *bond_mode = NULL;
5440                 const char *bond_primary_c = NULL;
5441                 char *bond_primary = NULL;
5442                 const char *bond_miimon_c = NULL;
5443                 char *bond_miimon = NULL;
5444                 const char *bond_downdelay_c = NULL;
5445                 char *bond_downdelay = NULL;
5446                 const char *bond_updelay_c = NULL;
5447                 char *bond_updelay = NULL;
5448                 const char *bond_arpinterval_c = NULL;
5449                 char *bond_arpinterval = NULL;
5450                 const char *bond_arpiptarget_c = NULL;
5451                 char *bond_arpiptarget = NULL;
5452                 const char *bond_lacp_rate_c = NULL;
5453                 char *bond_lacp_rate = NULL;
5454                 nmc_arg_t exp_args[] = { {"mode",          TRUE, &bond_mode_c,        FALSE},
5455                                          {"primary",       TRUE, &bond_primary_c,     FALSE},
5456                                          {"miimon",        TRUE, &bond_miimon_c,      FALSE},
5457                                          {"downdelay",     TRUE, &bond_downdelay_c,   FALSE},
5458                                          {"updelay",       TRUE, &bond_updelay_c,     FALSE},
5459                                          {"arp-interval",  TRUE, &bond_arpinterval_c, FALSE},
5460                                          {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE},
5461                                          {"lacp-rate",     TRUE, &bond_lacp_rate_c, FALSE},
5462                                          {NULL} };
5463
5464                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5465                         return FALSE;
5466
5467                 /* Also ask for all optional arguments if '--ask' is specified. */
5468                 bond_mode = g_strdup (bond_mode_c);
5469                 bond_primary = g_strdup (bond_primary_c);
5470                 bond_miimon = g_strdup (bond_miimon_c);
5471                 bond_downdelay = g_strdup (bond_downdelay_c);
5472                 bond_updelay = g_strdup (bond_updelay_c);
5473                 bond_arpinterval = g_strdup (bond_arpinterval_c);
5474                 bond_arpiptarget = g_strdup (bond_arpiptarget_c);
5475                 bond_lacp_rate = g_strdup (bond_lacp_rate_c);
5476                 if (ask)
5477                         do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon,
5478                                                &bond_downdelay, &bond_updelay,
5479                                                &bond_arpinterval, &bond_arpiptarget,
5480                                                &bond_lacp_rate);
5481
5482                 /* Generate ifname if connection doesn't have one */
5483                 ifname = nm_setting_connection_get_interface_name (s_con);
5484                 if (!ifname) {
5485                         char *bond_ifname = unique_master_iface_ifname (all_connections, "nm-bond");
5486
5487                         g_object_set (s_con,
5488                                       NM_SETTING_CONNECTION_INTERFACE_NAME, bond_ifname,
5489                                       NULL);
5490                         g_free (bond_ifname);
5491                 }
5492
5493                 /* Add 'bond' setting */
5494                 s_bond = (NMSettingBond *) nm_setting_bond_new ();
5495                 nm_connection_add_setting (connection, NM_SETTING (s_bond));
5496
5497                 /* Set bond options */
5498                 if (bond_mode) {
5499                         GError *err = NULL;
5500                         const char *bm;
5501                         if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) {
5502                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5503                                              _("Error: 'mode': %s."), err->message);
5504                                 g_clear_error (&err);
5505                                 goto cleanup_bond;
5506                         }
5507                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm);
5508                 }
5509                 if (bond_primary) {
5510                         if (!nm_utils_iface_valid_name (bond_primary)) {
5511                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5512                                              _("Error: 'primary': '%s' is not a valid interface name."),
5513                                              bond_primary);
5514                                 goto cleanup_bond;
5515                         }
5516                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary);
5517                 }
5518                 if (bond_miimon)
5519                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon);
5520                 if (bond_downdelay && strcmp (bond_downdelay, "0") != 0)
5521                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay);
5522                 if (bond_updelay && strcmp (bond_updelay, "0") != 0)
5523                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay);
5524                 if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0)
5525                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval);
5526                 if (bond_arpiptarget)
5527                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget);
5528                 if (bond_lacp_rate)
5529                         nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_LACP_RATE, bond_lacp_rate);
5530
5531                 success = TRUE;
5532 cleanup_bond:
5533                 g_free (bond_mode);
5534                 g_free (bond_primary);
5535                 g_free (bond_miimon);
5536                 g_free (bond_downdelay);
5537                 g_free (bond_updelay);
5538                 g_free (bond_arpinterval);
5539                 g_free (bond_arpiptarget);
5540                 g_free (bond_lacp_rate);
5541                 if (!success)
5542                         return FALSE;
5543
5544         } else if (!strcmp (con_type, "bond-slave")) {
5545                 /* Slave types without any specific settings ('bond-slave') */
5546                 const char *master = NULL;
5547                 const char *type = NULL;
5548                 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5549                                          {"type",   TRUE, &type,   FALSE},
5550                                          {NULL} };
5551
5552                 /* Set global variables for use in TAB completion */
5553                 nmc_tab_completion.con_type = NM_SETTING_BOND_SETTING_NAME;
5554
5555                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5556                         return FALSE;
5557
5558                 if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error))
5559                         return FALSE;
5560
5561                 /* Change properties in 'connection' setting */
5562                 g_object_set (s_con,
5563                               NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
5564                               NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME,
5565                               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, slave_type, 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,
5669                               NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
5670                               NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME,
5671                               NULL);
5672
5673                 /* Add ethernet setting */
5674                 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5675                 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5676
5677         } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
5678                 /* Build up the settings required for 'bridge' */
5679                 gboolean success = FALSE;
5680                 const char *ifname = NULL;
5681                 const char *stp_c = NULL;
5682                 char *stp = NULL;
5683                 const char *priority_c = NULL;
5684                 char *priority = NULL;
5685                 const char *fwd_delay_c = NULL;
5686                 char *fwd_delay = NULL;
5687                 const char *hello_time_c = NULL;
5688                 char *hello_time = NULL;
5689                 const char *max_age_c = NULL;
5690                 char *max_age = NULL;
5691                 const char *ageing_time_c = NULL;
5692                 char *ageing_time = NULL;
5693                 const char *mcast_snoop_c = NULL;
5694                 char *mcast_snoop = NULL;
5695                 gboolean stp_bool, mcast_snoop_bool;
5696                 unsigned long stp_prio_int, fwd_delay_int, hello_time_int,
5697                               max_age_int, ageing_time_int;
5698                 const char *mac_c = NULL;
5699                 char *mac = NULL;
5700                 nmc_arg_t exp_args[] = { {"stp",           TRUE, &stp_c,         FALSE},
5701                                          {"priority",      TRUE, &priority_c,    FALSE},
5702                                          {"forward-delay", TRUE, &fwd_delay_c,   FALSE},
5703                                          {"hello-time",    TRUE, &hello_time_c,  FALSE},
5704                                          {"max-age",       TRUE, &max_age_c,     FALSE},
5705                                          {"ageing-time",   TRUE, &ageing_time_c, FALSE},
5706                                          {"multicast-snooping", TRUE, &mcast_snoop_c, FALSE},
5707                                          {"mac",           TRUE, &mac_c,         FALSE},
5708                                          {NULL} };
5709
5710                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5711                         return FALSE;
5712
5713                 /* Also ask for all optional arguments if '--ask' is specified. */
5714                 stp = g_strdup (stp_c);
5715                 priority = g_strdup (priority_c);
5716                 fwd_delay = g_strdup (fwd_delay_c);
5717                 hello_time = g_strdup (hello_time_c);
5718                 max_age = g_strdup (max_age_c);
5719                 ageing_time = g_strdup (ageing_time_c);
5720                 mcast_snoop = g_strdup (mcast_snoop_c);
5721                 mac = g_strdup (mac_c);
5722                 if (ask)
5723                         do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time,
5724                                                  &max_age, &ageing_time, &mcast_snoop, &mac);
5725
5726                 /* Generate ifname if conneciton doesn't have one */
5727                 ifname = nm_setting_connection_get_interface_name (s_con);
5728                 if (!ifname) {
5729                         char *bridge_ifname = unique_master_iface_ifname (all_connections, "nm-bridge");
5730
5731                         g_object_set (s_con,
5732                                       NM_SETTING_CONNECTION_INTERFACE_NAME, bridge_ifname,
5733                                       NULL);
5734                         g_free (bridge_ifname);
5735                 }
5736
5737                 if (stp) {
5738                         GError *tmp_err = NULL;
5739                         if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) {
5740                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5741                                              _("Error: 'stp': %s."), tmp_err->message);
5742                                 g_clear_error (&tmp_err);
5743                                 goto cleanup_bridge;
5744                         }
5745                 }
5746                 if (mcast_snoop) {
5747                         GError *tmp_err = NULL;
5748                         if (!nmc_string_to_bool (mcast_snoop, &mcast_snoop_bool, &tmp_err)) {
5749                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5750                                              _("Error: 'multicast-snooping': %s."), tmp_err->message);
5751                                 g_clear_error (&tmp_err);
5752                                 goto cleanup_bridge;
5753                         }
5754                 }
5755
5756                 /* Add 'bond' setting */
5757                 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5758                 s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
5759                 nm_connection_add_setting (connection, NM_SETTING (s_bridge));
5760
5761                 if (priority)
5762                         if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE,
5763                                                          NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error))
5764                                 goto cleanup_bridge;
5765                 if (fwd_delay)
5766                         if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE,
5767                                                          NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error))
5768                                 goto cleanup_bridge;
5769                 if (hello_time)
5770                         if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE,
5771                                                          NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error))
5772                                 goto cleanup_bridge;
5773                 if (max_age)
5774                         if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE,
5775                                                          NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error))
5776                                 goto cleanup_bridge;
5777                 if (ageing_time)
5778                         if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE,
5779                                                          NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error))
5780                                 goto cleanup_bridge;
5781                 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5782                         goto cleanup_bridge;
5783
5784                 /* Set bridge options */
5785                 if (stp)
5786                         g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL);
5787                 if (priority)
5788                         g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL);
5789                 if (fwd_delay)
5790                         g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL);
5791                 if (hello_time)
5792                         g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL);
5793                 if (max_age)
5794                         g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL);
5795                 if (ageing_time)
5796                         g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL);
5797                 if (mcast_snoop)
5798                         g_object_set (s_bridge, NM_SETTING_BRIDGE_MULTICAST_SNOOPING, mcast_snoop_bool, NULL);
5799                 if (mac)
5800                         g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NULL);
5801
5802                 success = TRUE;
5803 cleanup_bridge:
5804                 g_free (stp);
5805                 g_free (priority);
5806                 g_free (fwd_delay);
5807                 g_free (hello_time);
5808                 g_free (max_age);
5809                 g_free (ageing_time);
5810                 g_free (mcast_snoop);
5811                 g_free (mac);
5812                 if (!success)
5813                         return FALSE;
5814
5815         } else if (!strcmp (con_type, "bridge-slave")) {
5816                 /* Build up the settings required for 'bridge-slave' */
5817                 gboolean success = FALSE;
5818                 const char *master = NULL;
5819                 char *master_ask = NULL;
5820                 const char *type = NULL;
5821                 const char *priority_c = NULL;
5822                 char *priority = NULL;
5823                 const char *path_cost_c = NULL;
5824                 char *path_cost = NULL;
5825                 const char *hairpin_c = NULL;
5826                 char *hairpin = NULL;
5827                 unsigned long prio_int, path_cost_int;
5828                 gboolean hairpin_bool;
5829                 nmc_arg_t exp_args[] = { {"master",    TRUE, &master,      FALSE},
5830                                          {"type",      TRUE, &type,        FALSE},
5831                                          {"priority",  TRUE, &priority_c,  FALSE},
5832                                          {"path-cost", TRUE, &path_cost_c, FALSE},
5833                                          {"hairpin",   TRUE, &hairpin_c,   FALSE},
5834                                          {NULL} };
5835
5836                 /* Set global variables for use in TAB completion */
5837                 nmc_tab_completion.con_type = NM_SETTING_BRIDGE_SETTING_NAME;
5838
5839                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5840                         return FALSE;
5841
5842                 if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error))
5843                         return FALSE;
5844
5845                 /* Add 'bridge-port' setting */
5846                 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5847                 s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new ();
5848                 nm_connection_add_setting (connection, NM_SETTING (s_bridge_port));
5849
5850                 /* Also ask for all optional arguments if '--ask' is specified. */
5851                 priority = g_strdup (priority_c);
5852                 path_cost = g_strdup (path_cost_c);
5853                 hairpin = g_strdup (hairpin_c);
5854                 if (ask)
5855                         do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin);
5856
5857                 if (priority)
5858                         if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
5859                                                          NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error))
5860                                 goto cleanup_bridge_slave;
5861                 if (path_cost)
5862                         if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
5863                                                          NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error))
5864                                 goto cleanup_bridge_slave;
5865                 if (hairpin) {
5866                         GError *tmp_err = NULL;
5867                         if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) {
5868                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5869                                              _("Error: 'hairpin': %s."), tmp_err->message);
5870                                 g_clear_error (&tmp_err);
5871                                 goto cleanup_bridge_slave;
5872                         }
5873                 }
5874
5875                 if (priority)
5876                         g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL);
5877                 if (path_cost)
5878                         g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL);
5879                 if (hairpin)
5880                         g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL);
5881
5882                 success = TRUE;
5883 cleanup_bridge_slave:
5884                 g_free (master_ask);
5885                 g_free (priority);
5886                 g_free (path_cost);
5887                 g_free (hairpin);
5888                 if (!success)
5889                         return FALSE;
5890
5891                 /* Change properties in 'connection' setting */
5892                 g_object_set (s_con,
5893                               NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
5894                               NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
5895                               NULL);
5896
5897                 /* Add ethernet setting */
5898                 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5899                 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5900
5901         } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) {
5902                 /* Build up the settings required for 'vpn' */
5903                 gboolean success = FALSE;
5904                 const char *vpn_type = NULL;
5905                 char *vpn_type_ask = NULL;
5906                 const char *user_c = NULL;
5907                 char *user = NULL;
5908                 const char *st;
5909                 char *service_type = NULL;
5910                 nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask},
5911                                          {"user",     TRUE, &user_c,   FALSE},
5912                                          {NULL} };
5913
5914                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5915                         return FALSE;
5916
5917                 if (!vpn_type && ask)
5918                         vpn_type = vpn_type_ask = nmc_readline (PROMPT_VPN_TYPE);
5919                 if (!vpn_type) {
5920                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5921                                              _("Error: 'vpn-type' is required."));
5922                         goto cleanup_vpn;
5923                 }
5924                 if (vpn_type_ask)
5925                         vpn_type = g_strstrip (vpn_type_ask);
5926
5927                 if (!(st = nmc_string_is_valid (vpn_type, nmc_known_vpns, NULL))) {
5928                         g_print (_("Warning: 'vpn-type': %s not known.\n"), vpn_type);
5929                         st = vpn_type;
5930                 }
5931                 service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st);
5932
5933                 /* Also ask for all optional arguments if '--ask' is specified. */
5934                 user = g_strdup (user_c);
5935                 if (ask)
5936                         do_questionnaire_vpn (&user);
5937
5938                 /* Add 'vpn' setting */
5939                 s_vpn = (NMSettingVpn *) nm_setting_vpn_new ();
5940                 nm_connection_add_setting (connection, NM_SETTING (s_vpn));
5941
5942                 g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL);
5943                 g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL);
5944
5945                 success = TRUE;
5946 cleanup_vpn:
5947                 g_free (vpn_type_ask);
5948                 g_free (service_type);
5949                 g_free (user);
5950                 if (!success)
5951                         return FALSE;
5952
5953         } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) {
5954                 /* Build up the settings required for 'olpc' */
5955                 gboolean success = FALSE;
5956                 char *ssid_ask = NULL;
5957                 const char *ssid = NULL;
5958                 GBytes *ssid_bytes;
5959                 const char *channel_c = NULL;
5960                 char *channel = NULL;
5961                 unsigned long chan;
5962                 const char *dhcp_anycast_c = NULL;
5963                 char *dhcp_anycast = NULL;
5964                 nmc_arg_t exp_args[] = { {"ssid",         TRUE, &ssid,           !ask},
5965                                          {"channel",      TRUE, &channel_c,      FALSE},
5966                                          {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE},
5967                                          {NULL} };
5968
5969                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5970                         return FALSE;
5971
5972                 if (!ssid && ask)
5973                         ssid = ssid_ask = nmc_readline (_("SSID: "));
5974                 if (!ssid) {
5975                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5976                                              _("Error: 'ssid' is required."));
5977                         goto cleanup_olpc;
5978                 }
5979
5980                 /* Also ask for all optional arguments if '--ask' is specified. */
5981                 channel = g_strdup (channel_c);
5982                 dhcp_anycast = g_strdup (dhcp_anycast_c);
5983                 if (ask)
5984                         do_questionnaire_olpc (&channel, &dhcp_anycast);
5985
5986                 if (channel) {
5987                         if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) {
5988                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5989                                              _("Error: 'channel': '%s' is not valid; use <1-13>."),
5990                                              channel);
5991                                 goto cleanup_olpc;
5992                         }
5993                 }
5994                 if (!check_mac (dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", error))
5995                         goto cleanup_olpc;
5996
5997                 /* Add OLPC mesh setting */
5998                 s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new ();
5999                 nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh));
6000
6001                 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
6002                 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_bytes, NULL);
6003                 if (channel)
6004                         g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL);
6005                 else
6006                         g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL);
6007                 if (dhcp_anycast)
6008                         g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, dhcp_anycast, NULL);
6009                 g_bytes_unref (ssid_bytes);
6010
6011                 success = TRUE;
6012 cleanup_olpc:
6013                 g_free (ssid_ask);
6014                 g_free (channel);
6015                 g_free (dhcp_anycast);
6016                 if (!success)
6017                         return FALSE;
6018
6019         } else if (!strcmp (con_type, NM_SETTING_ADSL_SETTING_NAME)) {
6020                 /* Build up the settings required for 'adsl' */
6021                 gboolean success = FALSE;
6022                 char *username_ask = NULL;
6023                 const char *username = NULL;
6024                 char *protocol_ask = NULL, *protocol = NULL;
6025                 const char *protocol_c = NULL;
6026                 const char *password_c = NULL;
6027                 char *password = NULL;
6028                 const char *encapsulation_c = NULL;
6029                 char *encapsulation = NULL;
6030                 nmc_arg_t exp_args[] = { {"username",      TRUE, &username,        !ask},
6031                                          {"protocol",      TRUE, &protocol_c,      !ask},
6032                                          {"password",      TRUE, &password_c,      FALSE},
6033                                          {"encapsulation", TRUE, &encapsulation_c, FALSE},
6034                                          {NULL} };
6035
6036                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6037                         return FALSE;
6038
6039                 if (!username && ask)
6040                         username = username_ask = nmc_readline (_("Username: "));
6041                 if (!username) {
6042                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6043                                              _("Error: 'username' is required."));
6044                         goto cleanup_adsl;
6045                 }
6046
6047 #define PROMPT_ADSL_PROTO "(" NM_SETTING_ADSL_PROTOCOL_PPPOA "/" NM_SETTING_ADSL_PROTOCOL_PPPOE "/" NM_SETTING_ADSL_PROTOCOL_IPOATM "): "
6048                 if (!protocol_c && ask)
6049                         protocol_c = protocol_ask = nmc_readline (_("Protocol %s"), PROMPT_ADSL_PROTO);
6050                 if (!protocol_c) {
6051                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6052                                              _("Error: 'protocol' is required."));
6053                         goto cleanup_adsl;
6054                 }
6055                 protocol = g_strdup (protocol_c);
6056                 if (!check_adsl_protocol (&protocol, error))
6057                         goto cleanup_adsl;
6058
6059                 /* Also ask for all optional arguments if '--ask' is specified. */
6060                 password = g_strdup (password_c);
6061                 encapsulation = g_strdup (encapsulation_c);
6062                 if (ask)
6063                         do_questionnaire_adsl (show_secrets, &password, &encapsulation);
6064
6065                 if (!check_adsl_encapsulation (&encapsulation, error))
6066                         goto cleanup_adsl;
6067
6068                 /* Add ADSL setting */
6069                 s_adsl = (NMSettingAdsl *) nm_setting_adsl_new ();
6070                 nm_connection_add_setting (connection, NM_SETTING (s_adsl));
6071
6072                 g_object_set (s_adsl,
6073                               NM_SETTING_ADSL_USERNAME, username,
6074                               NM_SETTING_ADSL_PROTOCOL, protocol,
6075                               NM_SETTING_ADSL_PASSWORD, password,
6076                               NM_SETTING_ADSL_ENCAPSULATION, encapsulation,
6077                               NULL);
6078
6079                 success = TRUE;
6080 cleanup_adsl:
6081                 g_free (username_ask);
6082                 g_free (password);
6083                 g_free (protocol);
6084                 g_free (protocol_ask);
6085                 g_free (encapsulation);
6086
6087                 if (!success)
6088                         return FALSE;
6089
6090         } else if (!strcmp (con_type, NM_SETTING_MACVLAN_SETTING_NAME)) {
6091                 /* Build up the settings required for 'macvlan' */
6092                 gboolean success = FALSE;
6093                 const char *parent = NULL;
6094                 char *parent_ask = NULL;
6095                 const char *mode = NULL;
6096                 char *mode_ask = NULL;
6097                 const char *tap_c = NULL;
6098                 char *tap = NULL;
6099                 NMSettingMacvlanMode mode_enum;
6100                 gboolean valid_mac = FALSE;
6101                 gboolean tap_bool = FALSE;
6102                 nmc_arg_t exp_args[] = { {"dev",     TRUE, &parent,    !ask},
6103                                          {"mode",    TRUE, &mode,      !ask},
6104                                          {"tap",     TRUE, &tap_c,     FALSE},
6105                                          {NULL} };
6106
6107                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6108                         return FALSE;
6109
6110                 if (!parent && ask)
6111                         parent = parent_ask = nmc_readline (_("MACVLAN parent device or connection UUID: "));
6112                 if (!parent) {
6113                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6114                                              _("Error: 'dev' is required."));
6115                         return FALSE;
6116                 }
6117
6118                 if (   !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
6119                     && !nm_utils_is_uuid (parent)
6120                     && !nm_utils_iface_valid_name (parent)) {
6121                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6122                                      _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
6123                                      parent);
6124                         goto cleanup_macvlan;
6125                 }
6126
6127                 if (!mode && ask)
6128                         mode = mode_ask = nmc_readline (PROMPT_MACVLAN_MODE);
6129                 if (!mode) {
6130                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6131                                              _("Error: 'mode' is required."));
6132                         return FALSE;
6133                 }
6134
6135                 if (!nm_utils_enum_from_str (nm_setting_macvlan_mode_get_type(), mode, (int *) &mode_enum, NULL)) {
6136                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6137                                              _("Error: 'mode' is not valid."));
6138                         return FALSE;
6139                 }
6140
6141                 /* Also ask for all optional arguments if '--ask' is specified. */
6142                 tap = g_strdup (tap_c);
6143                 if (ask)
6144                         do_questionnaire_macvlan (&tap);
6145
6146                 if (tap) {
6147                         GError *tmp_err = NULL;
6148                         if (!nmc_string_to_bool (tap, &tap_bool, &tmp_err)) {
6149                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6150                                              _("Error: 'tap': %s."), tmp_err->message);
6151                                 g_clear_error (&tmp_err);
6152                                 goto cleanup_macvlan;
6153                         }
6154                 }
6155
6156                 /* Add 'macvlan' setting */
6157                 s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new ();
6158                 nm_connection_add_setting (connection, NM_SETTING (s_macvlan));
6159
6160                 /* Add 'wired' setting if necessary */
6161                 if (valid_mac) {
6162                         s_wired = (NMSettingWired *) nm_setting_wired_new ();
6163                         nm_connection_add_setting (connection, NM_SETTING (s_wired));
6164                         g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
6165                 }
6166
6167                 /* Set 'macvlan' properties */
6168                 if (!valid_mac)
6169                         g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, parent, NULL);
6170                 g_object_set (s_macvlan, NM_SETTING_MACVLAN_MODE, mode_enum, NULL);
6171                 g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, tap_bool, NULL);
6172
6173                 success = TRUE;
6174 cleanup_macvlan:
6175                 g_free (parent_ask);
6176                 g_free (mode_ask);
6177                 g_free (tap);
6178
6179                 if (!success)
6180                         return FALSE;
6181
6182         } else if (!strcmp (con_type, NM_SETTING_TUN_SETTING_NAME)) {
6183                 /* Build up the settings required for 'tun' */
6184                 gboolean success = FALSE;
6185                 const char *mode_c = NULL;
6186                 char *mode_ask = NULL, *mode = NULL;
6187                 NMSettingTunMode mode_enum;
6188                 const char *owner_c = NULL, *group_c = NULL;
6189                 char *owner = NULL, *group = NULL;
6190                 const char *pi_c = NULL, *vnet_hdr_c = NULL, *multi_queue_c = NULL;
6191                 char *pi = NULL, *vnet_hdr = NULL, *multi_queue = NULL;
6192                 gboolean pi_bool, vnet_hdr_bool, multi_queue_bool;
6193                 nmc_arg_t exp_args[] = { {"mode",        TRUE,  &mode_c,        !ask},
6194                                          {"owner",       TRUE,  &owner_c,       FALSE},
6195                                          {"group",       TRUE,  &group_c,       FALSE},
6196                                          {"pi",          TRUE,  &pi_c,          FALSE},
6197                                          {"vnet-hdr",    TRUE,  &vnet_hdr_c,    FALSE},
6198                                          {"multi-queue", TRUE,  &multi_queue_c, FALSE},
6199                                          {NULL} };
6200
6201                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6202                         return FALSE;
6203
6204                 if (!mode_c && ask) {
6205                         mode_ask = nmc_readline (_("Mode %s"), PROMPT_TUN_MODE);
6206                         mode_ask = mode_ask ? mode_ask : g_strdup ("tun");
6207                         mode_c = mode_ask;
6208                 }
6209                 if (!mode_c) {
6210                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6211                                              _("Error: 'mode' is required."));
6212                         goto cleanup_tun;
6213                 }
6214                 mode = g_strdup (mode_c);
6215                 if (!check_tun_mode (&mode, error))
6216                         goto cleanup_tun;
6217
6218                 if (owner && !check_user_group_id (owner, error))
6219                         goto cleanup_tun;
6220                 if (group && !check_user_group_id (group, error))
6221                         goto cleanup_tun;
6222
6223                 owner = g_strdup (owner_c);
6224                 group = g_strdup (group_c);
6225                 pi = g_strdup (pi_c);
6226                 vnet_hdr = g_strdup (vnet_hdr_c);
6227                 multi_queue = g_strdup (multi_queue_c);
6228                 if (ask)
6229                         do_questionnaire_tun (&owner, &group, &pi, &vnet_hdr, &multi_queue);
6230
6231                 if (pi) {
6232                         GError *tmp_err = NULL;
6233
6234                         if (!nmc_string_to_bool (pi, &pi_bool, &tmp_err)) {
6235                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6236                                              _("Error: 'pi': %s."), tmp_err->message);
6237                                 g_clear_error (&tmp_err);
6238                                 goto cleanup_tun;
6239                         }
6240                 }
6241
6242                 if (vnet_hdr) {
6243                         GError *tmp_err = NULL;
6244
6245                         if (!nmc_string_to_bool (vnet_hdr, &vnet_hdr_bool, &tmp_err)) {
6246                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6247                                              _("Error: 'vnet-hdr': %s."), tmp_err->message);
6248                                 g_clear_error (&tmp_err);
6249                                 goto cleanup_tun;
6250                         }
6251                 }
6252
6253                 if (multi_queue) {
6254                         GError *tmp_err = NULL;
6255
6256                         if (!nmc_string_to_bool (multi_queue, &multi_queue_bool, &tmp_err)) {
6257                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6258                                              _("Error: 'multi-queue': %s."), tmp_err->message);
6259                                 g_clear_error (&tmp_err);
6260                                 goto cleanup_tun;
6261                         }
6262                 }
6263                 /* Add 'tun' setting */
6264                 s_tun = (NMSettingTun *) nm_setting_tun_new ();
6265                 nm_connection_add_setting (connection, NM_SETTING (s_tun));
6266                 mode_enum = !strcmp (mode, "tun") ? NM_SETTING_TUN_MODE_TUN : NM_SETTING_TUN_MODE_TAP;
6267
6268                 g_object_set (s_tun,
6269                               NM_SETTING_TUN_MODE,   mode_enum,
6270                               NM_SETTING_TUN_OWNER,  owner,
6271                               NM_SETTING_TUN_GROUP,  group,
6272                               NULL);
6273                 if (pi)
6274                         g_object_set (s_tun, NM_SETTING_TUN_PI, pi_bool, NULL);
6275                 if (vnet_hdr)
6276                         g_object_set (s_tun, NM_SETTING_TUN_VNET_HDR, vnet_hdr_bool, NULL);
6277                 if (multi_queue)
6278                         g_object_set (s_tun, NM_SETTING_TUN_MULTI_QUEUE, multi_queue_bool, NULL);
6279
6280                 success = TRUE;
6281 cleanup_tun:
6282                 g_free (mode_ask);
6283                 g_free (mode);
6284                 g_free (owner);
6285                 g_free (group);
6286                 g_free (pi);
6287                 g_free (vnet_hdr);
6288                 g_free (multi_queue);
6289                 if (!success)
6290                         return FALSE;
6291
6292         } else if (!strcmp (con_type, NM_SETTING_IP_TUNNEL_SETTING_NAME)) {
6293                 /* Build up the settings required for 'ip-tunnel' */
6294                 const char *mode_c = NULL, *local_c = NULL, *remote_c = NULL;
6295                 char *mode_ask = NULL, *remote_ask = NULL, *local = NULL;
6296                 const char *parent_c = NULL;
6297                 char *parent = NULL;
6298                 gboolean success = FALSE;
6299                 NMIPTunnelMode mode_enum;
6300                 nmc_arg_t exp_args[] = { {"mode",    TRUE, &mode_c,     !ask},
6301                                          {"local",   TRUE, &local_c,    FALSE},
6302                                          {"remote",  TRUE, &remote_c,   !ask},
6303                                          {"dev",     TRUE, &parent_c,   FALSE},
6304                                          {NULL} };
6305
6306                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6307                         return FALSE;
6308
6309                 if (!mode_c && ask)
6310                         mode_c = mode_ask = nmc_readline (PROMPT_IP_TUNNEL_MODE);
6311                 if (!mode_c) {
6312                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6313                                              _("Error: 'mode' is required."));
6314                         goto cleanup_tunnel;
6315                 }
6316
6317                 if (!nm_utils_enum_from_str (nm_ip_tunnel_mode_get_type (),
6318                                              mode_c, (int *) &mode_enum, NULL)) {
6319                         gs_free const char **values = NULL;
6320                         gs_free char *values_str = NULL;
6321
6322                         values = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6323                                                            NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6324                                                            G_MAXINT);
6325                         values_str = g_strjoinv (",", (char **) values);
6326                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6327                                      _("Error: 'mode': '%s' is not valid, use one of %s"),
6328                                      mode_c, values_str);
6329                         goto cleanup_tunnel;
6330                 }
6331
6332                 if (!remote_c && ask)
6333                         remote_c = remote_ask = nmc_readline (_("Remote endpoint: "));
6334                 if (!remote_c) {
6335                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6336                                              _("Error: 'remote' is required."));
6337                         goto cleanup_tunnel;
6338                 }
6339
6340                 if (   !nm_utils_ipaddr_valid (AF_INET, remote_c)
6341                     && !nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6342                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6343                                      _("Error: 'remote': '%s' is not valid; must be an IP address"),
6344                                      remote_c);
6345                         goto cleanup_tunnel;
6346                 }
6347
6348                 local = g_strdup (local_c);
6349                 parent = g_strdup (parent_c);
6350                 if (ask)
6351                         do_questionnaire_ip_tunnel (&local, &parent);
6352
6353                 if (   local
6354                     && !nm_utils_ipaddr_valid (AF_INET, local)
6355                     && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6356                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6357                                      _("Error: 'local': '%s' is not valid; must be an IP address"),
6358                                      local);
6359                         goto cleanup_tunnel;
6360                 }
6361
6362                 if (parent) {
6363                         if (   !nm_utils_is_uuid (parent)
6364                             && !nm_utils_iface_valid_name (parent)) {
6365                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6366                                              _("Error: 'dev': '%s' is neither UUID nor interface name."),
6367                                              parent);
6368                                 goto cleanup_tunnel;
6369                         }
6370                 }
6371
6372                 /* Add 'tunnel' setting */
6373                 s_ip_tunnel = (NMSettingIPTunnel *) nm_setting_ip_tunnel_new ();
6374                 nm_connection_add_setting (connection, NM_SETTING (s_ip_tunnel));
6375
6376                 /* Set 'tunnel' properties */
6377                 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_MODE, mode_enum, NULL);
6378                 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_REMOTE, remote_c, NULL);
6379                 if (local)
6380                         g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_LOCAL, local, NULL);
6381                 if (parent)
6382                         g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, parent, NULL);
6383
6384                 /* Set default values for IPv6 tunnels */
6385                 if (nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6386                         g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_TOS, 64, NULL);
6387                         g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT, 4, NULL);
6388                 }
6389
6390                 success = TRUE;
6391 cleanup_tunnel:
6392                 g_free (remote_ask);
6393                 g_free (mode_ask);
6394                 g_free (parent);
6395                 g_free (local);
6396                 if (!success)
6397                         return FALSE;
6398
6399         } else if (!strcmp (con_type, NM_SETTING_VXLAN_SETTING_NAME)) {
6400                 /* Build up the settings required for 'vxlan' */
6401                 gboolean success = FALSE;
6402                 char *id_ask = NULL;
6403                 const char *id = NULL;
6404                 char *remote_ask = NULL;
6405                 const char *remote = NULL;
6406                 const char *parent_c = NULL, *local_c = NULL;
6407                 const char *src_port_min_c = NULL, *src_port_max_c = NULL;
6408                 const char *dst_port_c = NULL;
6409                 char *parent = NULL, *local = NULL;
6410                 char *src_port_min = NULL, *src_port_max = NULL, *dst_port = NULL;
6411                 unsigned long int vni;
6412                 unsigned long sport_min = G_MAXULONG, sport_max = G_MAXULONG;
6413                 unsigned long dport = G_MAXULONG;
6414                 nmc_arg_t exp_args[] = { {"id",               TRUE, &id,             !ask},
6415                                          {"remote",           TRUE, &remote,         !ask},
6416                                          {"dev",              TRUE, &parent_c,        FALSE},
6417                                          {"local",            TRUE, &local_c,         FALSE},
6418                                          {"source-port-min",  TRUE, &src_port_min_c,  FALSE},
6419                                          {"source-port-max",  TRUE, &src_port_max_c,  FALSE},
6420                                          {"destination-port", TRUE, &dst_port_c,      FALSE},
6421                                          {NULL} };
6422
6423                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6424                         return FALSE;
6425
6426                 if (!id && ask)
6427                         id = id_ask = nmc_readline (_("VXLAN ID: "));
6428                 if (!id) {
6429                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6430                                              _("Error: 'id' is required."));
6431                         goto cleanup_vxlan;
6432                 }
6433
6434                 if (!remote && ask)
6435                         remote = remote_ask = nmc_readline (_("Remote: "));
6436                 if (!remote) {
6437                         g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6438                                              _("Error: 'remote' is required."));
6439                         goto cleanup_vxlan;
6440                 }
6441
6442                 if (!nmc_string_to_uint (id, TRUE, 0, (1UL << 24) - 1, &vni)) {
6443                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6444                                      _("Error: 'id': '%s' is not valid; use <0-16777215>."), id);
6445                         goto cleanup_vxlan;
6446                 }
6447
6448                 parent = g_strdup (parent_c);
6449                 local = g_strdup (local_c);
6450                 src_port_min = g_strdup (src_port_min_c);
6451                 src_port_max = g_strdup (src_port_max_c);
6452                 dst_port = g_strdup (dst_port_c);
6453
6454                 if (ask)
6455                         do_questionnaire_vxlan (&parent, &local, &src_port_min, &src_port_max, &dst_port);
6456
6457                 if (parent) {
6458                         if (   !nm_utils_is_uuid (parent)
6459                             && !nm_utils_iface_valid_name (parent)) {
6460                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6461                                              _("Error: 'dev': '%s' is neither UUID nor interface name."),
6462                                              parent);
6463                                 goto cleanup_vxlan;
6464                         }
6465                 }
6466
6467                 if (   !nm_utils_ipaddr_valid (AF_INET, remote)
6468                     && !nm_utils_ipaddr_valid (AF_INET6, remote)) {
6469                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6470                                      _("Error: 'remote': '%s' is not a valid IP address"),
6471                                      remote);
6472                         goto cleanup_vxlan;
6473                 }
6474
6475                 if (local) {
6476                         if (   !nm_utils_ipaddr_valid (AF_INET, local)
6477                             && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6478                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6479                                              _("Error: 'local': '%s' is not a valid IP address"),
6480                                              local);
6481                                 goto cleanup_vxlan;
6482                         }
6483                 }
6484
6485                 if (src_port_min) {
6486                         if (!nmc_string_to_uint (src_port_min, TRUE, 0, 65535, &sport_min)) {
6487                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6488                                              _("Error: 'source-port-min': %s is not valid; use <0-65535>."),
6489                                              src_port_min);
6490                                 goto cleanup_vxlan;
6491                         }
6492                 }
6493
6494                 if (src_port_max) {
6495                         if (!nmc_string_to_uint (src_port_max, TRUE, 0, 65535, &sport_max)) {
6496                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6497                                              _("Error: 'source-port-max': %s is not valid; use <0-65535>."),
6498                                              src_port_max);
6499                                 goto cleanup_vxlan;
6500                         }
6501                 }
6502
6503                 if (dst_port) {
6504                         if (!nmc_string_to_uint (dst_port, TRUE, 0, 65535, &dport)) {
6505                                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6506                                              _("Error: 'destination-port': %s is not valid; use <0-65535>."),
6507                                              dst_port);
6508                                 goto cleanup_vxlan;
6509                         }
6510                 }
6511
6512                 /* Add 'vxlan' setting */
6513                 s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new ();
6514                 nm_connection_add_setting (connection, NM_SETTING (s_vxlan));
6515
6516                 g_object_set (s_vxlan, NM_SETTING_VXLAN_ID, (guint) vni, NULL);
6517                 g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, remote, NULL);
6518                 g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, local, NULL);
6519                 g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, parent, NULL);
6520
6521                 if (sport_min != G_MAXULONG)
6522                         g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MIN, sport_min, NULL);
6523                 if (sport_max != G_MAXULONG)
6524                         g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MAX, sport_max, NULL);
6525                 if (dport != G_MAXULONG)
6526                         g_object_set (s_vxlan, NM_SETTING_VXLAN_DESTINATION_PORT, dport, NULL);
6527
6528                 success = TRUE;
6529
6530 cleanup_vxlan:
6531                 g_free (id_ask);
6532                 g_free (remote_ask);
6533                 g_free (parent);
6534                 g_free (local);
6535                 g_free (src_port_min);
6536                 g_free (src_port_max);
6537                 if (!success)
6538                         return FALSE;
6539
6540         } else if (!strcmp (con_type, NM_SETTING_GENERIC_SETTING_NAME)) {
6541                 /* Add 'generic' setting */
6542                 s_generic = (NMSettingGeneric *) nm_setting_generic_new ();
6543                 nm_connection_add_setting (connection, NM_SETTING (s_generic));
6544         } else {
6545                 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6546                              _("Error: '%s' is not a valid connection type."),
6547                              con_type);
6548                 return FALSE;
6549         }
6550
6551         slave_type = nm_setting_connection_get_slave_type (s_con);
6552         if (!slave_type) {
6553                 /* Read and add IP configuration */
6554                 NMIPAddress *ip4addr = NULL, *ip6addr = NULL;
6555                 const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL;
6556                 nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE},
6557                                          {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE},
6558                                          {NULL} };
6559
6560                 while (argc) {
6561                         nmc_arg_t *p;
6562
6563                         /* reset 'found' flag */
6564                         for (p = exp_args; p->name; p++)
6565                                 p->found = FALSE;
6566
6567                         ip4 = gw4 = ip6 = gw6 = NULL;
6568
6569                         if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, NULL))
6570                                 break;
6571
6572                         /* coverity[dead_error_begin] */
6573                         if (ip4) {
6574                                 ip4addr = nmc_parse_and_build_address (AF_INET, ip4, error);
6575                                 if (!ip4addr) {
6576                                         g_prefix_error (error, _("Error: "));
6577                                         return FALSE;
6578                                 }
6579                                 add_ip4_address_to_connection (ip4addr, connection);
6580                         }
6581
6582                         /* coverity[dead_error_begin] */
6583                         if (gw4) {
6584                                 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip4_config (connection);
6585
6586                                 if (!s_ip) {
6587                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6588                                                      _("Error: IPv4 gateway specified without IPv4 addresses"));
6589                                         return FALSE;
6590                                 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6591                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6592                                                      _("Error: multiple IPv4 gateways specified"));
6593                                         return FALSE;
6594                                 } else if (!nm_utils_ipaddr_valid (AF_INET, gw4)) {
6595                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6596                                                      _("Error: Invalid IPv4 gateway '%s'"),
6597                                                      gw4);
6598                                 }
6599
6600                                 g_object_set (s_ip,
6601                                               NM_SETTING_IP_CONFIG_GATEWAY, gw4,
6602                                               NULL);
6603                         }
6604
6605                         /* coverity[dead_error_begin] */
6606                         if (ip6) {
6607                                 ip6addr = nmc_parse_and_build_address (AF_INET6, ip6, error);
6608                                 if (!ip6addr) {
6609                                         g_prefix_error (error, _("Error: "));
6610                                         return FALSE;
6611                                 }
6612                                 add_ip6_address_to_connection (ip6addr, connection);
6613                         }
6614
6615                         /* coverity[dead_error_begin] */
6616                         if (gw6) {
6617                                 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip6_config (connection);
6618
6619                                 if (!s_ip) {
6620                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6621                                                      _("Error: IPv6 gateway specified without IPv6 addresses"));
6622                                         return FALSE;
6623                                 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6624                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6625                                                      _("Error: multiple IPv6 gateways specified"));
6626                                         return FALSE;
6627                                 } else if (!nm_utils_ipaddr_valid (AF_INET, gw6)) {
6628                                         g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6629                                                      _("Error: Invalid IPv6 gateway '%s'"),
6630                                                      gw6);
6631                                 }
6632
6633                                 g_object_set (s_ip,
6634                                               NM_SETTING_IP_CONFIG_GATEWAY, gw6,
6635                                               NULL);
6636                         }
6637                 }
6638
6639                 /* Ask for addresses if '--ask' is specified. */
6640                 if (ask)
6641                         do_questionnaire_ip (connection);
6642         }
6643
6644         if (argc) {
6645                 /* Set extra connection properties. */
6646                 nmc_arg_t exp_args[] = { {"--", FALSE, NULL, TRUE},
6647                                          {NULL} };
6648
6649                 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6650                         return FALSE;
6651
6652                 if (!read_connection_properties (connection, argc, argv, error))
6653                         return FALSE;
6654         }
6655
6656         return TRUE;
6657 }
6658
6659 typedef struct {
6660         NmCli *nmc;
6661         char *con_name;
6662 } AddConnectionInfo;
6663
6664 static void
6665 add_connection_cb (GObject *client,
6666                    GAsyncResult *result,
6667                    gpointer user_data)
6668 {
6669         AddConnectionInfo *info = (AddConnectionInfo *) user_data;
6670         NmCli *nmc = info->nmc;
6671         NMRemoteConnection *connection;
6672         GError *error = NULL;
6673
6674         connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
6675         if (error) {
6676                 g_string_printf (nmc->return_text,
6677                                  _("Error: Failed to add '%s' connection: %s"),
6678                                  info->con_name, error->message);
6679                 g_error_free (error);
6680                 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
6681         } else {
6682                 g_print (_("Connection '%s' (%s) successfully added.\n"),
6683                          nm_connection_get_id (NM_CONNECTION (connection)),
6684                          nm_connection_get_uuid (NM_CONNECTION (connection)));
6685                 g_object_unref (connection);
6686         }
6687
6688         g_free (info->con_name);
6689         g_free (info);
6690         quit ();
6691 }
6692
6693 static void
6694 add_new_connection (gboolean persistent,
6695                     NMClient *client,
6696                     NMConnection *connection,
6697                     GAsyncReadyCallback callback,
6698                     gpointer user_data)
6699 {
6700         nm_client_add_connection_async (client, connection, persistent,
6701                                         NULL, callback, user_data);
6702 }
6703
6704 static void
6705 update_connection (gboolean persistent,
6706                    NMRemoteConnection *connection,
6707                    GAsyncReadyCallback callback,
6708                    gpointer user_data)
6709 {
6710         nm_remote_connection_commit_changes_async (connection, persistent,
6711                                                    NULL, callback, user_data);
6712 }
6713
6714 static char *
6715 gen_func_vpn_types (const char *text, int state)
6716 {
6717         return nmc_rl_gen_func_basic (text, state, nmc_known_vpns);
6718 }
6719
6720 static char *
6721 gen_func_bool_values_l10n (const char *text, int state)
6722 {
6723         const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL };
6724         return nmc_rl_gen_func_basic (text, state, words);
6725 }
6726
6727 static char *
6728 gen_func_wifi_mode (const char *text, int state)
6729 {
6730         const char *words[] = { "infrastructure", "ap", "adhoc", NULL };
6731         return nmc_rl_gen_func_basic (text, state, words);
6732 }
6733
6734 static char *
6735 gen_func_ib_type (const char *text, int state)
6736 {
6737         const char *words[] = { "datagram", "connected", NULL };
6738         return nmc_rl_gen_func_basic (text, state, words);
6739 }
6740
6741 static char *
6742 gen_func_bt_type (const char *text, int state)
6743 {
6744         const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL };
6745         return nmc_rl_gen_func_basic (text, state, words);
6746 }
6747
6748 static char *
6749 gen_func_bond_mode (const char *text, int state)
6750 {
6751         const char *words[] = { "balance-rr", "active-backup", "balance-xor", "broadcast",
6752                                 "802.3ad", "balance-tlb", "balance-alb", NULL };
6753         return nmc_rl_gen_func_basic (text, state, words);
6754 }
6755 static char *
6756 gen_func_bond_mon_mode (const char *text, int state)
6757 {
6758         const char *words[] = { "miimon", "arp", NULL };
6759         return nmc_rl_gen_func_basic (text, state, words);
6760 }
6761
6762 static char *
6763 gen_func_adsl_proto (const char *text, int state)
6764 {
6765         const char *words[] = { "pppoe", "pppoa", "ipoatm", NULL };
6766         return nmc_rl_gen_func_basic (text, state, words);
6767 }
6768
6769 static char *
6770 gen_func_adsl_encap (const char *text, int state)
6771 {
6772         const char *words[] = { "vcmux", "llc", NULL };
6773         return nmc_rl_gen_func_basic (text, state, words);
6774 }
6775
6776 static char *
6777 gen_func_tun_mode (const char *text, int state)
6778 {
6779         const char *words[] = { "tun", "tap", NULL };
6780         return nmc_rl_gen_func_basic (text, state, words);
6781 }
6782
6783 static char *
6784 gen_func_ip_tunnel_mode (const char *text, int state)
6785 {
6786         gs_free const char **words = NULL;
6787
6788         words = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6789                                           NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6790                                           G_MAXINT);
6791         return nmc_rl_gen_func_basic (text, state, words);
6792 }
6793
6794 static char *
6795 gen_func_macvlan_mode (const char *text, int state)
6796 {
6797         gs_free const char **words = NULL;
6798
6799         words = nm_utils_enum_get_values (nm_setting_macvlan_mode_get_type(),
6800                                           NM_SETTING_MACVLAN_MODE_UNKNOWN + 1,
6801                                           G_MAXINT);
6802         return nmc_rl_gen_func_basic (text, state, words);
6803 }
6804
6805 static char *
6806 gen_func_master_ifnames (const char *text, int state)
6807 {
6808         int i;
6809         GPtrArray *ifnames;
6810         char *ret;
6811         NMConnection *con;
6812         NMSettingConnection *s_con;
6813         const char *con_type, *ifname;
6814
6815         if (!nm_cli.connections)
6816                 return NULL;
6817
6818         /* Disable appending space after completion */
6819         rl_completion_append_character = '\0';
6820
6821         ifnames = g_ptr_array_sized_new (20);
6822         for (i = 0; i < nm_cli.connections->len; i++) {
6823                 con = NM_CONNECTION (nm_cli.connections->pdata[i]);
6824                 s_con = nm_connection_get_setting_connection (con);
6825                 g_assert (s_con);
6826                 con_type = nm_setting_connection_get_connection_type (s_con);
6827                 if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0)
6828                         continue;
6829                 ifname = nm_connection_get_interface_name (con);
6830                 g_ptr_array_add (ifnames, (gpointer) ifname);
6831         }
6832         g_ptr_array_add (ifnames, (gpointer) NULL);
6833
6834         ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata);
6835
6836         g_ptr_array_free (ifnames, TRUE);
6837         return ret;
6838 }
6839
6840 static gboolean
6841 is_single_word (const char* line)
6842 {
6843         size_t n1, n2, n3;
6844
6845         n1 = strspn  (line,    " \t");
6846         n2 = strcspn (line+n1, " \t\0") + n1;
6847         n3 = strspn  (line+n2, " \t");
6848
6849         if (n3 == 0)
6850                 return TRUE;
6851         else
6852                 return FALSE;
6853 }
6854
6855 static char **
6856 nmcli_con_add_tab_completion (const char *text, int start, int end)
6857 {
6858         char **match_array = NULL;
6859         rl_compentry_func_t *generator_func = NULL;
6860
6861         /* Disable readline's default filename completion */
6862         rl_attempted_completion_over = 1;
6863
6864         /* Restore standard append character to space */
6865         rl_completion_append_character = ' ';
6866
6867         if (!is_single_word (rl_line_buffer))
6868                 return NULL;
6869
6870         if (g_strcmp0 (rl_prompt, PROMPT_CON_TYPE) == 0)
6871                 generator_func = gen_connection_types;
6872         else if (g_strcmp0 (rl_prompt, PROMPT_VPN_TYPE) == 0)
6873                 generator_func = gen_func_vpn_types;
6874         else if (g_strcmp0 (rl_prompt, PROMPT_MASTER) == 0)
6875                 generator_func = gen_func_master_ifnames;
6876         else if (   g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
6877                  || g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, ":"))
6878                  || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL))
6879                  || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, ":")))
6880                 generator_func = gen_func_bool_values_l10n;
6881         else if (g_str_has_suffix (rl_prompt, PROMPT_WIFI_MODE))
6882                 generator_func = gen_func_wifi_mode;
6883         else if (g_str_has_suffix (rl_prompt, PROMPT_IB_MODE))
6884                 generator_func = gen_func_ib_type;
6885         else if (g_str_has_suffix (rl_prompt, PROMPT_BT_TYPE))
6886                 generator_func = gen_func_bt_type;
6887         else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MODE))
6888                 generator_func = gen_func_bond_mode;
6889         else if (g_str_has_suffix (rl_prompt, PROMPT_BOND_MON_MODE))
6890                 generator_func = gen_func_bond_mon_mode;
6891         else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_PROTO))
6892                 generator_func = gen_func_adsl_proto;
6893         else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_ENCAP))
6894                 generator_func = gen_func_adsl_encap;
6895         else if (g_str_has_suffix (rl_prompt, PROMPT_TUN_MODE))
6896                 generator_func = gen_func_tun_mode;
6897         else if (g_str_has_suffix (rl_prompt, PROMPT_IP_TUNNEL_MODE))
6898                 generator_func = gen_func_ip_tunnel_mode;
6899         else if (g_str_has_suffix (rl_prompt, PROMPT_MACVLAN_MODE))
6900                 generator_func = gen_func_macvlan_mode;
6901
6902         if (generator_func)
6903                 match_array = rl_completion_matches (text, generator_func);
6904
6905         return match_array;
6906 }
6907
6908 static NMCResultCode
6909 do_connection_add (NmCli *nmc, int argc, char **argv)
6910 {
6911         NMConnection *connection = NULL;
6912         NMSettingConnection *s_con;
6913         char *uuid;
6914         char *default_name = NULL;
6915         const char *type = NULL;
6916         char *type_ask = NULL;
6917         const char *con_name = NULL;
6918         const char *autoconnect = NULL;
6919         gboolean auto_bool = TRUE;
6920         const char *ifname = NULL;
6921         char *ifname_ask = NULL;
6922         gboolean ifname_mandatory = TRUE;
6923         const char *save = NULL;
6924         gboolean save_bool = TRUE;
6925         const char *master = NULL;
6926         const char *checked_master = NULL;
6927         const char *slave_type = NULL;
6928         AddConnectionInfo *info = NULL;
6929         const char *setting_name;
6930         GError *error = NULL;
6931         nmc_arg_t exp_args[] = { {"type",        TRUE, &type,        !nmc->ask},
6932                                  {"con-name",    TRUE, &con_name,    FALSE},
6933                                  {"autoconnect", TRUE, &autoconnect, FALSE},
6934                                  {"ifname",      TRUE, &ifname,      FALSE},
6935                                  {"save",        TRUE, &save,        FALSE},
6936                                  {"master",      TRUE, &master,      FALSE},
6937                                  {"slave-type",  TRUE, &slave_type,  FALSE},
6938                                  {NULL} };
6939
6940         rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion;
6941
6942         nmc->return_value = NMC_RESULT_SUCCESS;
6943
6944         if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) {
6945                 g_string_assign (nmc->return_text, error->message);
6946                 nmc->return_value = error->code;
6947                 g_clear_error (&error);
6948                 goto error;
6949         }
6950
6951         if (!type && nmc->ask) {
6952                 char *types_tmp = get_valid_options_string (nmc_valid_connection_types, NULL);
6953                 g_print ("Valid types: [%s]\n", types_tmp);
6954                 type = type_ask = nmc_readline (PROMPT_CON_TYPE);
6955                 g_free (types_tmp);
6956         }
6957         if (!type) {
6958                 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
6959                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6960                 goto error;
6961         }
6962         if (type_ask)
6963                 type = g_strstrip (type_ask);
6964
6965         if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, NULL, &error))) {
6966                 g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."),
6967                                  error->message);
6968                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6969                 g_clear_error (&error);
6970                 goto error;
6971         }
6972         if (autoconnect) {
6973                 GError *tmp_err = NULL;
6974                 if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) {
6975                         g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."),
6976                                          tmp_err->message);
6977                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6978                         g_clear_error (&tmp_err);
6979                         goto error;
6980                 }
6981         }
6982         if (save) {
6983                 GError *tmp_err = NULL;
6984                 if (!nmc_string_to_bool (save, &save_bool, &tmp_err)) {
6985                         g_string_printf (nmc->return_text, _("Error: 'save': %s."),
6986                                          tmp_err->message);
6987                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6988                         g_clear_error (&tmp_err);
6989                         goto error;
6990                 }
6991         }
6992
6993         /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */
6994         if (   strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0
6995             || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0
6996             || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0
6997             || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0)
6998                 ifname_mandatory = FALSE;
6999
7000         if (!ifname && ifname_mandatory) {
7001                 if (nmc->ask) {
7002                         ifname = ifname_ask = nmc_readline (_("Interface name [*]: "));
7003                         if (!ifname)
7004                                 ifname = ifname_ask = g_strdup ("*");
7005                 } else {
7006                         if (!*argv)
7007                                 g_string_printf (nmc->return_text, _("Error: 'ifname' argument is required."));
7008                         else
7009                                 g_string_printf (nmc->return_text, _("Error: mandatory 'ifname' not seen before '%s'."),
7010                                                  *argv);
7011                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7012                         goto error;
7013                 }
7014         }
7015         if (ifname) {
7016                 if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) {
7017                         g_string_printf (nmc->return_text,
7018                                          _("Error: 'ifname': '%s' is not a valid interface nor '*'."),
7019                                          ifname);
7020                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7021                         goto error;
7022                 }
7023                 /* Special value of '*' means no specific interface name */
7024                 if (strcmp (ifname, "*") == 0)
7025                         ifname = NULL;
7026         }
7027
7028         /* Create a new connection object */
7029         connection = nm_simple_connection_new ();
7030
7031         /* Build up the 'connection' setting */
7032         s_con = (NMSettingConnection *) nm_setting_connection_new ();
7033         uuid = nm_utils_uuid_generate ();
7034         if (con_name)
7035                 default_name = g_strdup (con_name);
7036         else {
7037                 char *try_name = ifname ?
7038                                      g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname)
7039                                    : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types));
7040                 default_name = nmc_unique_connection_name (nmc->connections, try_name);
7041                 g_free (try_name);
7042         }
7043
7044         if (master)
7045                 /* Verify master argument */
7046                 checked_master = normalized_master_for_slave (nmc->connections, master, slave_type, &slave_type);
7047
7048         g_object_set (s_con,
7049                       NM_SETTING_CONNECTION_ID, default_name,
7050                       NM_SETTING_CONNECTION_UUID, uuid,
7051                       NM_SETTING_CONNECTION_TYPE, setting_name,
7052                       NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool,
7053                       NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
7054                       NM_SETTING_CONNECTION_MASTER, checked_master,
7055                       NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
7056                       NULL);
7057         g_free (uuid);
7058         g_free (default_name);
7059         nm_connection_add_setting (connection, NM_SETTING (s_con));
7060
7061         if (!complete_connection_by_type (connection,
7062                                           setting_name,
7063                                           nmc->connections,
7064                                           nmc->ask,
7065                                           nmc->show_secrets,
7066                                           argc,
7067                                           argv,
7068                                           &error)) {
7069                 g_string_assign (nmc->return_text, error->message);
7070                 nmc->return_value = error->code;
7071                 g_clear_error (&error);
7072                 goto error;
7073         }
7074
7075         nmc->should_wait++;
7076
7077         info = g_malloc0 (sizeof (AddConnectionInfo));
7078         info->nmc = nmc;
7079         info->con_name = g_strdup (nm_connection_get_id (connection));
7080
7081         /* Tell the settings service to add the new connection */
7082         add_new_connection (save_bool,
7083                             nmc->client,
7084                             connection,
7085                             add_connection_cb,
7086                             info);
7087
7088         if (connection)
7089                 g_object_unref (connection);
7090
7091         return nmc->return_value;
7092
7093 error:
7094         if (connection)
7095                 g_object_unref (connection);
7096         g_free (type_ask);
7097         g_free (ifname_ask);
7098
7099         return nmc->return_value;
7100 }
7101
7102
7103 /*----------------------------------------------------------------------------*/
7104 /* Functions for readline TAB completion in editor */
7105
7106 static void
7107 uuid_display_hook (char **array, int len, int max_len)
7108 {
7109         NMConnection *con;
7110         int i, max = 0;
7111         char *tmp;
7112         const char *id;
7113         for (i = 1; i <= len; i++) {
7114                 con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL);
7115                 id = con ? nm_connection_get_id (con) : NULL;
7116                 if (id) {
7117                         tmp = g_strdup_printf ("%s (%s)", array[i], id);
7118                         g_free (array[i]);
7119                         array[i] = tmp;
7120                         if (max < strlen (id))
7121                                 max = strlen (id);
7122                 }
7123         }
7124         rl_display_match_list (array, len, max_len + max + 3);
7125         rl_forced_update_display ();
7126 }
7127
7128 static char *
7129 gen_nmcli_cmds_menu (const char *text, int state)
7130 {
7131         const char *words[] = { "goto", "set", "remove", "describe", "print", "verify",
7132                                 "save", "activate", "back", "help", "quit", "nmcli",
7133                                 NULL };
7134         return nmc_rl_gen_func_basic (text, state, words);
7135 }
7136
7137 static char *
7138 gen_nmcli_cmds_submenu (const char *text, int state)
7139 {
7140         const char *words[] = { "set", "add", "change", "remove", "describe",
7141                                 "print", "back", "help", "quit",
7142                                 NULL };
7143         return nmc_rl_gen_func_basic (text, state, words);
7144 }
7145
7146 static char *
7147 gen_cmd_nmcli (const char *text, int state)
7148 {
7149         const char *words[] = { "status-line", "save-confirmation", "show-secrets", "prompt-color", NULL };
7150         return nmc_rl_gen_func_basic (text, state, words);
7151 }
7152
7153 static char *
7154 gen_cmd_nmcli_prompt_color (const char *text, int state)
7155 {
7156         const char *words[] = { "normal", "black", "red", "green", "yellow",
7157                                 "blue", "magenta", "cyan", "white", NULL };
7158         return nmc_rl_gen_func_basic (text, state, words);
7159 }
7160
7161 static char *
7162 gen_func_bool_values (const char *text, int state)
7163 {
7164         const char *words[] = { "yes", "no", NULL };
7165         return nmc_rl_gen_func_basic (text, state, words);
7166 }
7167
7168 static char *
7169 gen_cmd_verify0 (const char *text, int state)
7170 {
7171         const char *words[] = { "all", "fix", NULL };
7172         return nmc_rl_gen_func_basic (text, state, words);
7173 }
7174
7175 static char *
7176 gen_cmd_print0 (const char *text, int state)
7177 {
7178         static char **words = NULL;
7179         char *ret = NULL;
7180
7181         if (!state) {
7182                 GVariant *settings;
7183                 GVariantIter iter;
7184                 const char *setting_name;
7185                 int i = 0;
7186
7187                 settings = nm_connection_to_dbus (nmc_tab_completion.connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
7188                 words = g_new (char *, g_variant_n_children (settings) + 2);
7189                 g_variant_iter_init (&iter, settings);
7190                 while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL))
7191                         words [i++] = g_strdup (setting_name);
7192                 words[i++] = g_strdup ("all");
7193                 words[i] = NULL;
7194                 g_variant_unref (settings);
7195         }
7196
7197         if (words) {
7198                 ret = nmc_rl_gen_func_basic (text, state, (const char **) words);
7199                 if (ret == NULL) {
7200                         g_strfreev (words);
7201                         words = NULL;
7202                 }
7203         }
7204         return ret;
7205 }
7206
7207 static char *
7208 gen_cmd_print2 (const char *text, int state)
7209 {
7210         const char *words[] = { "setting", "connection", "all", NULL };
7211         return nmc_rl_gen_func_basic (text, state, words);
7212 }
7213
7214 static char *
7215 gen_cmd_save (const char *text, int state)
7216 {
7217         const char *words[] = { "persistent", "temporary", NULL };
7218         return nmc_rl_gen_func_basic (text, state, words);
7219 }
7220
7221 static char *
7222 gen_connection_types (const char *text, int state)
7223 {
7224         static int list_idx, len;
7225         const char *c_type, *a_type;
7226
7227         if (!state) {
7228                 list_idx = 0;
7229                 len = strlen (text);
7230         }
7231
7232         while (nmc_valid_connection_types[list_idx].name) {
7233                 a_type = nmc_valid_connection_types[list_idx].alias;
7234                 c_type = nmc_valid_connection_types[list_idx].name;
7235                 list_idx++;
7236                 if (a_type && !strncmp (text, a_type, len))
7237                         return g_strdup (a_type);
7238                 if (c_type && !strncmp (text, c_type, len))
7239                         return g_strdup (c_type);
7240         }
7241
7242         return NULL;
7243 }
7244
7245 static char *
7246 gen_setting_names (const char *text, int state)
7247 {
7248         static int list_idx, len, is_slv;
7249         const char *s_name, *a_name;
7250         const NameItem *valid_settings_arr;
7251         NMSettingConnection *s_con;
7252         const char *s_type = NULL;
7253         char *slv_type;
7254
7255         if (!state) {
7256                 list_idx = 0;
7257                 len = strlen (text);
7258                 is_slv = 0;
7259         }
7260
7261         if (!is_slv) {
7262                 valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type);
7263                 if (!valid_settings_arr)
7264                         return NULL;
7265                 while (valid_settings_arr[list_idx].name) {
7266                         a_name = valid_settings_arr[list_idx].alias;
7267                         s_name = valid_settings_arr[list_idx].name;
7268                         list_idx++;
7269                         if (len == 0 && a_name)
7270                                 return g_strdup_printf ("%s (%s)", s_name, a_name);
7271                         if (a_name && !strncmp (text, a_name, len))
7272                                 return g_strdup (a_name);
7273                         if (s_name && !strncmp (text, s_name, len))
7274                                 return g_strdup (s_name);
7275                 }
7276
7277                 /* Let's give a try to parameters related to slave type */
7278                 list_idx = 0;
7279                 is_slv = 1;
7280         }
7281
7282         /* is_slv */
7283         s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7284         if (s_con)
7285                 s_type = nm_setting_connection_get_slave_type (s_con);
7286         slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7287         valid_settings_arr = get_valid_settings_array (slv_type);
7288         g_free (slv_type);
7289
7290         while (valid_settings_arr[list_idx].name) {
7291                 a_name = valid_settings_arr[list_idx].alias;
7292                 s_name = valid_settings_arr[list_idx].name;
7293                 list_idx++;
7294                 if (len == 0 && a_name)
7295                         return g_strdup_printf ("%s (%s)", s_name, a_name);
7296                 if (a_name && !strncmp (text, a_name, len))
7297                         return g_strdup (a_name);
7298                 if (s_name && !strncmp (text, s_name, len))
7299                         return g_strdup (s_name);
7300         }
7301
7302         return NULL;
7303 }
7304
7305 static char *
7306 gen_property_names (const char *text, int state)
7307 {
7308         NMSetting *setting = NULL;
7309         char **valid_props = NULL;
7310         char *ret = NULL;
7311         const char *line = rl_line_buffer;
7312         const char *setting_name;
7313         char **strv = NULL;
7314         const NameItem *valid_settings_main;
7315         const NameItem *valid_settings_slave;
7316         const char *p1;
7317         const char *slv_type;
7318
7319         /* Try to get the setting from 'line' - setting_name.property */
7320         p1 = strchr (line, '.');
7321         if (p1) {
7322                 while (p1 > line && !g_ascii_isspace (*p1))
7323                         p1--;
7324
7325                 strv = g_strsplit (p1+1, ".", 2);
7326
7327                 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7328
7329                 /* Support autocompletion of slave-connection parameters
7330                  * guessing the slave type from the setting name already
7331                  * typed (or autocompleted) */
7332                 if (nm_streq0 (strv[0], NM_SETTING_TEAM_PORT_SETTING_NAME))
7333                         slv_type = "team-slave";
7334                 else if (nm_streq0 (strv[0], NM_SETTING_BRIDGE_PORT_SETTING_NAME))
7335                         slv_type = "bridge-slave";
7336                 else
7337                         slv_type = "no-slave";
7338                 valid_settings_slave = get_valid_settings_array (slv_type);
7339
7340                 setting_name = check_valid_name (strv[0],
7341                                                  valid_settings_main,
7342                                                  valid_settings_slave,
7343                                                  NULL);
7344                 setting = nmc_setting_new_for_name (setting_name);
7345         } else {
7346                 /* Else take the current setting, if any */
7347                 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7348         }
7349
7350         if (setting) {
7351                 valid_props = nmc_setting_get_valid_properties (setting);
7352                 ret = nmc_rl_gen_func_basic (text, state, (const char **) valid_props);
7353         }
7354
7355         g_strfreev (strv);
7356         g_strfreev (valid_props);
7357         if (setting)
7358                 g_object_unref (setting);
7359         return ret;
7360 }
7361
7362 static char *
7363 gen_compat_devices (const char *text, int state)
7364 {
7365         int i, j = 0;
7366         const GPtrArray *devices;
7367         const char **compatible_devices;
7368         char *ret;
7369
7370         devices = nm_client_get_devices (nmc_tab_completion.nmc->client);
7371         if (devices->len == 0)
7372                 return NULL;
7373
7374         compatible_devices = g_new (const char *, devices->len + 1);
7375         for (i = 0; i < devices->len; i++) {
7376                 NMDevice *dev = g_ptr_array_index (devices, i);
7377                 const char *ifname = nm_device_get_iface (dev);
7378                 NMDevice *device = NULL;
7379                 const char *spec_object = NULL;
7380
7381                 if (find_device_for_connection (nmc_tab_completion.nmc, nmc_tab_completion.connection,
7382                                                 ifname, NULL, NULL, &device, &spec_object, NULL)) {
7383                         compatible_devices[j++] = ifname;
7384                 }
7385         }
7386         compatible_devices[j] = NULL;
7387
7388         ret = nmc_rl_gen_func_basic (text, state, compatible_devices);
7389
7390         g_free (compatible_devices);
7391         return ret;
7392 }
7393
7394 static const char **
7395 _create_vpn_array (const GPtrArray *connections, gboolean uuid)
7396 {
7397         int c, idx = 0;
7398         const char **array;
7399
7400         if (connections->len < 1)
7401                 return NULL;
7402
7403         array = g_new (const char *, connections->len + 1);
7404         for (c = 0; c < connections->len; c++) {
7405                 NMConnection *connection = NM_CONNECTION (connections->pdata[c]);
7406                 const char *type = nm_connection_get_connection_type (connection);
7407
7408                 if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) == 0)
7409                         array[idx++] = uuid ? nm_connection_get_uuid (connection) : nm_connection_get_id (connection);
7410         }
7411         array[idx] = NULL;
7412         return array;
7413 }
7414
7415 static char *
7416 gen_vpn_uuids (const char *text, int state)
7417 {
7418         const GPtrArray *connections = nm_cli.connections;
7419         const char **uuids;
7420         char *ret;
7421
7422         if (connections->len < 1)
7423                 return NULL;
7424
7425         uuids = _create_vpn_array (connections, TRUE);
7426         ret = nmc_rl_gen_func_basic (text, state, uuids);
7427         g_free (uuids);
7428         return ret;
7429 }
7430
7431 static char *
7432 gen_vpn_ids (const char *text, int state)
7433 {
7434         const GPtrArray *connections = nm_cli.connections;
7435         const char **ids;
7436         char *ret;
7437
7438         if (connections->len < 1)
7439                 return NULL;
7440
7441         ids = _create_vpn_array (connections, FALSE);
7442         ret = nmc_rl_gen_func_basic (text, state, ids);
7443         g_free (ids);
7444         return ret;
7445 }
7446
7447 static rl_compentry_func_t *
7448 get_gen_func_cmd_nmcli (const char *str)
7449 {
7450         if (!str)
7451                 return NULL;
7452         if (matches (str, "status-line") == 0)
7453                 return gen_func_bool_values;
7454         if (matches (str, "save-confirmation") == 0)
7455                 return gen_func_bool_values;
7456         if (matches (str, "show-secrets") == 0)
7457                 return gen_func_bool_values;
7458         if (matches (str, "prompt-color") == 0)
7459                 return gen_cmd_nmcli_prompt_color;
7460         return NULL;
7461 }
7462
7463 /*
7464  * Helper function parsing line for completion.
7465  * IN:
7466  *   line : the whole line to be parsed
7467  *   end  : the position of cursor in the line
7468  *   cmd  : command to match
7469  * OUT:
7470  *   cw_num    : is set to the word number being completed (1, 2, 3, 4).
7471  *   prev_word : returns the previous word (so that we have some context).
7472  *
7473  * Returns TRUE when the first word of the 'line' matches 'cmd'.
7474  *
7475  * Examples:
7476  * line="rem"              cmd="remove"   -> TRUE  cw_num=1
7477  * line="set con"          cmd="set"      -> TRUE  cw_num=2
7478  * line="go ipv4.method"   cmd="goto"     -> TRUE  cw_num=2
7479  * line="  des eth.mtu "   cmd="describe" -> TRUE  cw_num=3
7480  * line=" bla ipv4.method" cmd="goto"     -> FALSE
7481  */
7482 static gboolean
7483 should_complete_cmd (const char *line, int end, const char *cmd,
7484                      int *cw_num, char **prev_word)
7485 {
7486         char *tmp;
7487         const char *word1, *word2, *word3;
7488         size_t n1, n2, n3, n4, n5, n6;
7489         gboolean word1_done, word2_done, word3_done;
7490         gboolean ret = FALSE;
7491
7492         if (!line)
7493                 return FALSE;
7494
7495         tmp = g_strdup (line);
7496
7497         n1 = strspn  (tmp,    " \t");
7498         n2 = strcspn (tmp+n1, " \t\0") + n1;
7499         n3 = strspn  (tmp+n2, " \t")   + n2;
7500         n4 = strcspn (tmp+n3, " \t\0") + n3;
7501         n5 = strspn  (tmp+n4, " \t")   + n4;
7502         n6 = strcspn (tmp+n5, " \t\0") + n5;
7503
7504         word1_done = end > n2;
7505         word2_done = end > n4;
7506         word3_done = end > n6;
7507         tmp[n2] = tmp[n4] = tmp[n6] = '\0';
7508
7509         word1 = tmp[n1] ? tmp + n1 : NULL;
7510         word2 = tmp[n3] ? tmp + n3 : NULL;
7511         word3 = tmp[n5] ? tmp + n5 : NULL;
7512
7513         if (!word1_done) {
7514                 if (cw_num)
7515                         *cw_num = 1;
7516                 if (prev_word)
7517                         *prev_word = NULL;
7518         } else if (!word2_done) {
7519                 if (cw_num)
7520                         *cw_num = 2;
7521                 if (prev_word)
7522                         *prev_word = g_strdup (word1);
7523         } else if (!word3_done) {
7524                 if (cw_num)
7525                         *cw_num = 3;
7526                 if (prev_word)
7527                         *prev_word = g_strdup (word2);
7528         } else {
7529                 if (cw_num)
7530                         *cw_num = 4;
7531                 if (prev_word)
7532                         *prev_word = g_strdup (word3);
7533         }
7534
7535         if (word1 && matches (word1, cmd) == 0)
7536                 ret = TRUE;
7537
7538         g_free (tmp);
7539         return ret;
7540 }
7541
7542 /*
7543  * extract_setting_and_property:
7544  * prompt: (in) (allow-none): prompt string, or NULL
7545  * line: (in) (allow-none): line, or NULL
7546  * setting: (out) (transfer full) (array zero-terminated=1):
7547  *   return location for setting name
7548  * property: (out) (transfer full) (array zero-terminated=1):
7549  *   return location for property name
7550  *
7551  * Extract setting and property names from prompt and/or line.
7552  */
7553 static void
7554 extract_setting_and_property (const char *prompt, const char *line,
7555                               char **setting, char **property)
7556 {
7557         char *prop = NULL;
7558         char *sett = NULL;
7559
7560         if (prompt) {
7561                 /* prompt looks like this:
7562                   "nmcli 802-1x>" or "nmcli 802-1x.pac-file>" */
7563                 const char *p1, *p2, *dot;
7564                 size_t num1, num2;
7565                 p1 = strchr (prompt, ' ');
7566                 if (p1) {
7567                         dot = strchr (++p1, '.');
7568                         if (dot) {
7569                                 p2 = dot + 1;
7570                                 num1 = strcspn (p1, ".");
7571                                 num2 = strcspn (p2, ">");
7572                                 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7573                                 prop = num2 > 0 ? g_strndup (p2, num2) : NULL;
7574                         } else {
7575                                 num1 = strcspn (p1, ">");
7576                                 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7577                         }
7578                 }
7579         }
7580
7581         if (line) {
7582                 /* line looks like this:
7583                   " set 802-1x.pac-file ..." or " set pac-file ..." */
7584                 const char *p1, *p2, *dot;
7585                 size_t n1, n2, n3, n4;
7586                 size_t num1, num2, len;
7587                 n1 = strspn  (line,    " \t");         /* white-space */
7588                 n2 = strcspn (line+n1, " \t\0") + n1;  /* command */
7589                 n3 = strspn  (line+n2, " \t")   + n2;  /* white-space */
7590                 n4 = strcspn (line+n3, " \t\0") + n3;  /* setting/property */
7591                 p1 = line + n3;
7592                 len = n4 - n3;
7593
7594                 dot = strchr (p1, '.');
7595                 if (dot && dot < p1 + len) {
7596                         p2 = dot + 1;
7597                         num1 = strcspn (p1, ".");
7598                         num2 = len > num1 + 1 ? len - num1 - 1 : 0;
7599                         sett = num1 > 0 ? g_strndup (p1, num1) : sett;
7600                         prop = num2 > 0 ? g_strndup (p2, num2) : prop;
7601                 } else {
7602                         if (!prop)
7603                                 prop = len > 0 ? g_strndup (p1, len) : NULL;
7604                 }
7605         }
7606
7607         if (setting)
7608                 *setting = sett;
7609         else
7610                 g_free (sett);
7611         if (property)
7612                 *property = prop;
7613         else
7614                 g_free (prop);
7615 }
7616
7617 static void
7618 get_setting_and_property (const char *prompt, const char *line,
7619                           NMSetting **setting_out, char**property_out)
7620 {
7621         const NameItem *valid_settings_main;
7622         const NameItem *valid_settings_slave;
7623         const char *setting_name;
7624         NMSetting *setting = NULL;
7625         char *property = NULL;
7626         char *sett = NULL, *prop = NULL;
7627         NMSettingConnection *s_con;
7628         const char *s_type = NULL;
7629         char *slv_type;
7630
7631         extract_setting_and_property (prompt, line, &sett, &prop);
7632         if (sett) {
7633                 /* Is this too much (and useless?) effort for an unlikely case? */
7634                 s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7635                 if (s_con)
7636                         s_type = nm_setting_connection_get_slave_type (s_con);
7637                 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7638
7639                 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7640                 valid_settings_slave = get_valid_settings_array (slv_type);
7641                 g_free (slv_type);
7642
7643                 setting_name = check_valid_name (sett, valid_settings_main,
7644                                                  valid_settings_slave,  NULL);
7645                 setting = nmc_setting_new_for_name (setting_name);
7646         } else
7647                 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7648
7649         if (setting && prop)
7650                 property = is_property_valid (setting, prop, NULL);
7651         else
7652                 property = g_strdup (nmc_tab_completion.property);
7653
7654         *setting_out = setting;
7655         *property_out = property;
7656
7657         g_free (sett);
7658         g_free (prop);
7659 }
7660
7661 static gboolean
7662 _get_and_check_property (const char *prompt,
7663                          const char *line,
7664                          const char **array,
7665                          const char **array_multi,
7666                          gboolean *multi)
7667 {
7668         char *prop;
7669         gboolean found = FALSE;
7670
7671         extract_setting_and_property (prompt, line, NULL, &prop);
7672         if (prop) {
7673                 if (array)
7674                         found = !!nmc_string_is_valid (prop, array, NULL);
7675                 if (array_multi && multi)
7676                         *multi = !!nmc_string_is_valid (prop, array_multi, NULL);
7677                 g_free (prop);
7678         }
7679         return found;
7680 }
7681
7682 static gboolean
7683 should_complete_files (const char *prompt, const char *line)
7684 {
7685         const char *file_properties[] = {
7686                 /* '802-1x' properties */
7687                 "ca-cert",
7688                 "ca-path",
7689                 "client-cert",
7690                 "pac-file",
7691                 "phase2-ca-cert",
7692                 "phase2-ca-path",
7693                 "phase2-client-cert",
7694                 "private-key",
7695                 "phase2-private-key",
7696                 /* 'team' and 'team-port' properties */
7697                 "config",
7698                 NULL
7699         };
7700         return _get_and_check_property (prompt, line, file_properties, NULL, NULL);
7701 }
7702
7703 static gboolean
7704 should_complete_vpn_uuids (const char *prompt, const char *line)
7705 {
7706         const char *uuid_properties[] = {
7707                 /* 'connection' properties */
7708                 "secondaries",
7709                 NULL
7710         };
7711         return _get_and_check_property (prompt, line, uuid_properties, NULL, NULL);
7712 }
7713
7714 static const char **
7715 get_allowed_property_values (void)
7716 {
7717         NMSetting *setting;
7718         char *property;
7719         const char **avals = NULL;
7720
7721         get_setting_and_property (rl_prompt, rl_line_buffer, &setting, &property);
7722         if (setting && property)
7723                 avals = nmc_setting_get_property_allowed_values (setting, property);
7724
7725         if (setting)
7726                 g_object_unref (setting);
7727         g_free (property);
7728
7729         return avals;
7730 }
7731
7732 static gboolean
7733 should_complete_property_values (const char *prompt, const char *line, gboolean *multi)
7734 {
7735         /* properties allowing multiple values */
7736         const char *multi_props[] = {
7737                 /* '802-1x' properties */
7738                 NM_SETTING_802_1X_EAP,
7739                 /* '802-11-wireless-security' properties */
7740                 NM_SETTING_WIRELESS_SECURITY_PROTO,
7741                 NM_SETTING_WIRELESS_SECURITY_PAIRWISE,
7742                 NM_SETTING_WIRELESS_SECURITY_GROUP,
7743                 /* 'bond' properties */
7744                 NM_SETTING_BOND_OPTIONS,
7745                 /* 'ethernet' properties */
7746                 NM_SETTING_WIRED_S390_OPTIONS,
7747                 NULL
7748         };
7749         _get_and_check_property (prompt, line, NULL, multi_props, multi);
7750         return get_allowed_property_values () != NULL;
7751 }
7752
7753 //FIXME: this helper should go to libnm later
7754 static gboolean
7755 _setting_property_is_boolean (NMSetting *setting, const char *property_name)
7756 {
7757         GParamSpec *pspec;
7758
7759         g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
7760         g_return_val_if_fail (property_name, FALSE);
7761
7762         pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
7763         if (pspec && pspec->value_type == G_TYPE_BOOLEAN)
7764                 return TRUE;
7765         return FALSE;
7766 }
7767
7768 static gboolean
7769 should_complete_boolean (const char *prompt, const char *line)
7770 {
7771         NMSetting *setting;
7772         char *property;
7773         gboolean is_boolean = FALSE;
7774
7775         get_setting_and_property (prompt, line, &setting, &property);
7776         if (setting && property)
7777                 is_boolean = _setting_property_is_boolean (setting, property);
7778
7779         if (setting)
7780                 g_object_unref (setting);
7781         g_free (property);
7782
7783         return is_boolean;
7784 }
7785
7786 static char *
7787 gen_property_values (const char *text, int state)
7788 {
7789         char *ret = NULL;
7790         const char **avals;
7791
7792         avals = get_allowed_property_values ();
7793         if (avals)
7794                 ret = nmc_rl_gen_func_basic (text, state, avals);
7795         return ret;
7796 }
7797
7798 /* from readline */
7799 extern int rl_complete_with_tilde_expansion;
7800
7801 /*
7802  * Attempt to complete on the contents of TEXT.  START and END show the
7803  * region of TEXT that contains the word to complete.  We can use the
7804  * entire line in case we want to do some simple parsing.  Return the
7805  * array of matches, or NULL if there aren't any.
7806  */
7807 static char **
7808 nmcli_editor_tab_completion (const char *text, int start, int end)
7809 {
7810         char **match_array = NULL;
7811         const char *line = rl_line_buffer;
7812         rl_compentry_func_t *generator_func = NULL;
7813         char *prompt_tmp;
7814         char *word = NULL;
7815         size_t n1;
7816         int num;
7817
7818         /* Restore standard append character to space */
7819         rl_completion_append_character = ' ';
7820
7821         /* Restore standard function for displaying matches */
7822         rl_completion_display_matches_hook = NULL;
7823
7824         /* Disable default filename completion */
7825         rl_attempted_completion_over = 1;
7826
7827         /* Enable tilde expansion when filenames are completed */
7828         rl_complete_with_tilde_expansion = 1;
7829
7830         /* Filter out possible ANSI color escape sequences */
7831         prompt_tmp = nmc_filter_out_colors ((const char *) rl_prompt);
7832
7833         /* Find the first non-space character */
7834         n1 = strspn (line, " \t");
7835
7836         /* Choose the right generator function */
7837         if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0)
7838                 generator_func = gen_connection_types;
7839         else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0)
7840                 generator_func = gen_setting_names;
7841         else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0)
7842                 generator_func = gen_property_names;
7843         else if (   g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
7844                  || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL)))
7845                 generator_func = gen_func_bool_values_l10n;
7846         else if (g_str_has_prefix (prompt_tmp, "nmcli")) {
7847                 if (!strchr (prompt_tmp, '.')) {
7848                         int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1;
7849                         const char *dot = strchr (line, '.');
7850                         gboolean multi;
7851
7852                         /* Main menu  - level 0,1 */
7853                         if (start == n1)
7854                                 generator_func = gen_nmcli_cmds_menu;
7855                         else {
7856                                 if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) {
7857                                         if (level == 0 && (!dot || dot >= line + end))
7858                                                 generator_func = gen_setting_names;
7859                                         else
7860                                                 generator_func = gen_property_names;
7861                                 } else if (should_complete_cmd (line, end, "set", &num, NULL)) {
7862                                         if (num < 3) {
7863                                                 if (level == 0 && (!dot || dot >= line + end)) {
7864                                                         generator_func = gen_setting_names;
7865                                                         rl_completion_append_character = '.';
7866                                                 } else
7867                                                         generator_func = gen_property_names;
7868                                         } else if (num >= 3) {
7869                                                 if (num == 3 && should_complete_files (NULL, line))
7870                                                         rl_attempted_completion_over = 0;
7871                                                 else if (should_complete_vpn_uuids (NULL, line)) {
7872                                                         rl_completion_display_matches_hook = uuid_display_hook;
7873                                                         generator_func = gen_vpn_uuids;
7874                                                 } else if (   should_complete_property_values (NULL, line, &multi)
7875                                                            && (num == 3 || multi)) {
7876                                                         generator_func = gen_property_values;
7877                                                 } else if (should_complete_boolean (NULL, line) && num == 3)
7878                                                         generator_func = gen_func_bool_values;
7879                                         }
7880                                 } else if (  (   should_complete_cmd (line, end, "remove", &num, NULL)
7881                                               || should_complete_cmd (line, end, "describe", &num, NULL))
7882                                            && num <= 2) {
7883                                         if (level == 0 && (!dot || dot >= line + end)) {
7884                                                 generator_func = gen_setting_names;
7885                                                 rl_completion_append_character = '.';
7886                                         } else
7887                                                 generator_func = gen_property_names;
7888                                 } else if (should_complete_cmd (line, end, "nmcli", &num, &word)) {
7889                                         if (num < 3)
7890                                                 generator_func = gen_cmd_nmcli;
7891                                         else if (num == 3)
7892                                                 generator_func = get_gen_func_cmd_nmcli (word);
7893                                 } else if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) {
7894                                         if (level == 0 && (!dot || dot >= line + end))
7895                                                 generator_func = gen_cmd_print0;
7896                                         else
7897                                                 generator_func = gen_property_names;
7898                                 } else if (should_complete_cmd (line, end, "verify", &num, NULL) && num <= 2) {
7899                                         generator_func = gen_cmd_verify0;
7900                                 } else if (should_complete_cmd (line, end, "activate", &num, NULL) && num <= 2) {
7901                                         generator_func = gen_compat_devices;
7902                                 } else if (should_complete_cmd (line, end, "save", &num, NULL) && num <= 2) {
7903                                         generator_func = gen_cmd_save;
7904                                 } else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7905                                         generator_func = gen_nmcli_cmds_menu;
7906                         }
7907                 } else {
7908                         /* Submenu - level 2 */
7909                         if (start == n1)
7910                                 generator_func = gen_nmcli_cmds_submenu;
7911                         else {
7912                                 gboolean multi;
7913
7914                                 if (   should_complete_cmd (line, end, "add", &num, NULL)
7915                                     || should_complete_cmd (line, end, "set", &num, NULL)) {
7916                                         if (num <= 2 && should_complete_files (prompt_tmp, line))
7917                                                 rl_attempted_completion_over = 0;
7918                                         else if (should_complete_vpn_uuids (prompt_tmp, line)) {
7919                                                 rl_completion_display_matches_hook = uuid_display_hook;
7920                                                 generator_func = gen_vpn_uuids;
7921                                         } else if (   should_complete_property_values (prompt_tmp, NULL, &multi)
7922                                                    && (num <= 2 || multi)) {
7923                                                 generator_func = gen_property_values;
7924                                         } else if (should_complete_boolean (prompt_tmp, NULL) && num <= 2)
7925                                                 generator_func = gen_func_bool_values;
7926                                 }
7927                                 if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2)
7928                                         generator_func = gen_cmd_print2;
7929                                 else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7930                                         generator_func = gen_nmcli_cmds_submenu;
7931                         }
7932                 }
7933         }
7934
7935         if (generator_func)
7936                 match_array = rl_completion_matches (text, generator_func);
7937
7938         g_free (prompt_tmp);
7939         g_free (word);
7940         return match_array;
7941 }
7942
7943 #define NMCLI_EDITOR_HISTORY ".nmcli-history"
7944
7945 static void
7946 load_history_cmds (const char *uuid)
7947 {
7948         GKeyFile *kf;
7949         char *filename;
7950         char **keys;
7951         char *line;
7952         size_t i;
7953         GError *err = NULL;
7954
7955         filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7956         kf = g_key_file_new ();
7957         if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7958                 if (g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE))
7959                         g_print ("Warning: %s parse error: %s\n", filename, err->message);
7960                 g_key_file_free (kf);
7961                 g_free (filename);
7962                 return;
7963         }
7964         keys = g_key_file_get_keys (kf, uuid, NULL, NULL);
7965         for (i = 0; keys && keys[i]; i++) {
7966                 line = g_key_file_get_string (kf, uuid, keys[i], NULL);
7967                 if (line && *line)
7968                         add_history (line);
7969                 g_free (line);
7970         }
7971         g_strfreev (keys);
7972         g_key_file_free (kf);
7973         g_free (filename);
7974 }
7975
7976 static void
7977 save_history_cmds (const char *uuid)
7978 {
7979         HIST_ENTRY **hist = NULL;
7980         GKeyFile *kf;
7981         char *filename;
7982         size_t i;
7983         char *key;
7984         char *data;
7985         gsize len = 0;
7986         GError *err = NULL;
7987
7988         hist = history_list ();
7989         if (hist) {
7990                 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7991                 kf = g_key_file_new ();
7992                 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7993                         if (   !g_error_matches (err, G_FILE_ERROR, G_FILE_ERROR_NOENT)
7994                             && !g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
7995                                 g_print ("Warning: %s parse error: %s\n", filename, err->message);
7996                                 g_key_file_free (kf);
7997                                 g_free (filename);
7998                                 g_clear_error (&err);
7999                                 return;
8000                         }
8001                         g_clear_error (&err);
8002                 }
8003
8004                 /* Remove previous history group and save new history entries */
8005                 g_key_file_remove_group (kf, uuid, NULL);
8006                 for (i = 0; hist[i]; i++)
8007                 {
8008                         key = g_strdup_printf ("%zd", i);
8009                         g_key_file_set_string (kf, uuid, key, hist[i]->line);
8010                         g_free (key);
8011                 }
8012
8013                 /* Write history to file */
8014                 data = g_key_file_to_data (kf, &len, NULL);
8015                 if (data) {
8016                         g_file_set_contents (filename, data, len, NULL);
8017                         g_free (data);
8018                 }
8019                 g_key_file_free (kf);
8020                 g_free (filename);
8021         }
8022 }
8023
8024 /*----------------------------------------------------------------------------*/
8025
8026 static void
8027 editor_show_connection (NMConnection *connection, NmCli *nmc)
8028 {
8029         nmc->print_output = NMC_PRINT_PRETTY;
8030         nmc->multiline_output = TRUE;
8031         nmc->escape_values = 0;
8032
8033         /* Remove any previous data */
8034         nmc_empty_output_fields (nmc);
8035
8036         nmc_connection_profile_details (connection, nmc, nmc->editor_show_secrets);
8037 }
8038
8039 static void
8040 editor_show_setting (NMSetting *setting, NmCli *nmc)
8041 {
8042         g_print (_("['%s' setting values]\n"),
8043                  nm_setting_get_name (setting));
8044
8045         nmc->print_output = NMC_PRINT_NORMAL;
8046         nmc->multiline_output = TRUE;
8047         nmc->escape_values = 0;
8048
8049         /* Remove any previous data */
8050         nmc_empty_output_fields (nmc);
8051
8052         setting_details (setting, nmc, NULL, nmc->editor_show_secrets);
8053 }
8054
8055 typedef enum {
8056         NMC_EDITOR_MAIN_CMD_UNKNOWN = 0,
8057         NMC_EDITOR_MAIN_CMD_GOTO,
8058         NMC_EDITOR_MAIN_CMD_REMOVE,
8059         NMC_EDITOR_MAIN_CMD_SET,
8060         NMC_EDITOR_MAIN_CMD_DESCRIBE,
8061         NMC_EDITOR_MAIN_CMD_PRINT,
8062         NMC_EDITOR_MAIN_CMD_VERIFY,
8063         NMC_EDITOR_MAIN_CMD_SAVE,
8064         NMC_EDITOR_MAIN_CMD_ACTIVATE,
8065         NMC_EDITOR_MAIN_CMD_BACK,
8066         NMC_EDITOR_MAIN_CMD_HELP,
8067         NMC_EDITOR_MAIN_CMD_NMCLI,
8068         NMC_EDITOR_MAIN_CMD_QUIT,
8069 } NmcEditorMainCmd;
8070
8071 static NmcEditorMainCmd
8072 parse_editor_main_cmd (const char *cmd, char **cmd_arg)
8073 {
8074         NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN;
8075         char **vec;
8076
8077         vec = nmc_strsplit_set (cmd, " \t", 2);
8078         if (g_strv_length (vec) < 1) {
8079                 if (cmd_arg)
8080                         *cmd_arg = NULL;
8081                 return NMC_EDITOR_MAIN_CMD_UNKNOWN;
8082         }
8083
8084         if (matches (vec[0], "goto") == 0)
8085                 editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO;
8086         else if (matches (vec[0], "remove") == 0)
8087                 editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE;
8088         else if (matches (vec[0], "set") == 0)
8089                 editor_cmd = NMC_EDITOR_MAIN_CMD_SET;
8090         else if (matches (vec[0], "describe") == 0)
8091                 editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE;
8092         else if (matches (vec[0], "print") == 0)
8093                 editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT;
8094         else if (matches (vec[0], "verify") == 0)
8095                 editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY;
8096         else if (matches (vec[0], "save") == 0)
8097                 editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE;
8098         else if (matches (vec[0], "activate") == 0)
8099                 editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE;
8100         else if (matches (vec[0], "back") == 0)
8101                 editor_cmd = NMC_EDITOR_MAIN_CMD_BACK;
8102         else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8103                 editor_cmd = NMC_EDITOR_MAIN_CMD_HELP;
8104         else if (matches (vec[0], "quit") == 0)
8105                 editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT;
8106         else if (matches (vec[0], "nmcli") == 0)
8107                 editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI;
8108
8109         /* set pointer to command argument */
8110         if (cmd_arg)
8111                 *cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL;
8112
8113         g_strfreev (vec);
8114         return editor_cmd;
8115 }
8116
8117 static void
8118 editor_main_usage (void)
8119 {
8120         g_print ("------------------------------------------------------------------------------\n");
8121         /* TRANSLATORS: do not translate command names and keywords before ::
8122          *              However, you should translate terms enclosed in <>.
8123          */
8124         g_print (_("---[ Main menu ]---\n"
8125                    "goto     [<setting> | <prop>]        :: go to a setting or property\n"
8126                    "remove   <setting>[.<prop>] | <prop> :: remove setting or reset property value\n"
8127                    "set      [<setting>.<prop> <value>]  :: set property value\n"
8128                    "describe [<setting>.<prop>]          :: describe property\n"
8129                    "print    [all | <setting>[.<prop>]]  :: print the connection\n"
8130                    "verify   [all | fix]                 :: verify the connection\n"
8131                    "save     [persistent|temporary]      :: save the connection\n"
8132                    "activate [<ifname>] [/<ap>|<nsp>]    :: activate the connection\n"
8133                    "back                                 :: go one level up (back)\n"
8134                    "help/?   [<command>]                 :: print this help\n"
8135                    "nmcli    <conf-option> <value>       :: nmcli configuration\n"
8136                    "quit                                 :: exit nmcli\n"));
8137         g_print ("------------------------------------------------------------------------------\n");
8138 }
8139
8140 static void
8141 editor_main_help (const char *command)
8142 {
8143         if (!command)
8144                 editor_main_usage ();
8145         else {
8146                 /* detailed command descriptions */
8147                 NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL);
8148
8149                 switch (cmd) {
8150                 case NMC_EDITOR_MAIN_CMD_GOTO:
8151                         g_print (_("goto <setting>[.<prop>] | <prop>  :: enter setting/property for editing\n\n"
8152                                    "This command enters into a setting or property for editing it.\n\n"
8153                                    "Examples: nmcli> goto connection\n"
8154                                    "          nmcli connection> goto secondaries\n"
8155                                    "          nmcli> goto ipv4.addresses\n"));
8156                         break;
8157                 case NMC_EDITOR_MAIN_CMD_REMOVE:
8158                         g_print (_("remove <setting>[.<prop>]  :: remove setting or reset property value\n\n"
8159                                    "This command removes an entire setting from the connection, or if a property\n"
8160                                    "is given, resets that property to the default value.\n\n"
8161                                    "Examples: nmcli> remove wifi-sec\n"
8162                                    "          nmcli> remove eth.mtu\n"));
8163                         break;
8164                 case NMC_EDITOR_MAIN_CMD_SET:
8165                         g_print (_("set [<setting>.<prop> <value>]  :: set property value\n\n"
8166                                    "This command sets property value.\n\n"
8167                                    "Example: nmcli> set con.id My connection\n"));
8168                         break;
8169                 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
8170                         g_print (_("describe [<setting>.<prop>]  :: describe property\n\n"
8171                                    "Shows property description. You can consult nm-settings(5) "
8172                                    "manual page to see all NM settings and properties.\n"));
8173                         break;
8174                 case NMC_EDITOR_MAIN_CMD_PRINT:
8175                         g_print (_("print [all]  :: print setting or connection values\n\n"
8176                                    "Shows current property or the whole connection.\n\n"
8177                                    "Example: nmcli ipv4> print all\n"));
8178                         break;
8179                 case NMC_EDITOR_MAIN_CMD_VERIFY:
8180                         g_print (_("verify [all | fix]  :: verify setting or connection validity\n\n"
8181                                    "Verifies whether the setting or connection is valid and can be saved later.\n"
8182                                    "It indicates invalid values on error. Some errors may be fixed automatically\n"
8183                                    "by 'fix' option.\n\n"
8184                                    "Examples: nmcli> verify\n"
8185                                    "          nmcli> verify fix\n"
8186                                    "          nmcli bond> verify\n"));
8187                         break;
8188                 case NMC_EDITOR_MAIN_CMD_SAVE:
8189                         g_print (_("save [persistent|temporary]  :: save the connection\n\n"
8190                                    "Sends the connection profile to NetworkManager that either will save it\n"
8191                                    "persistently, or will only keep it in memory. 'save' without an argument\n"
8192                                    "means 'save persistent'.\n"
8193                                    "Note that once you save the profile persistently those settings are saved\n"
8194                                    "across reboot or restart. Subsequent changes can also be temporary or\n"
8195                                    "persistent, but any temporary changes will not persist across reboot or\n"
8196                                    "restart. If you want to fully remove the persistent connection, the connection\n"
8197                                    "profile must be deleted.\n"));
8198                         break;
8199                 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
8200                         g_print (_("activate [<ifname>] [/<ap>|<nsp>]  :: activate the connection\n\n"
8201                                    "Activates the connection.\n\n"
8202                                    "Available options:\n"
8203                                    "<ifname>    - device the connection will be activated on\n"
8204                                    "/<ap>|<nsp> - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when <ifname> is not specified)\n"));
8205                         break;
8206                 case NMC_EDITOR_MAIN_CMD_BACK:
8207                         g_print (_("back  :: go to upper menu level\n\n"));
8208                         break;
8209                 case NMC_EDITOR_MAIN_CMD_HELP:
8210                         g_print (_("help/? [<command>]  :: help for the nmcli commands\n\n"));
8211                         break;
8212                 case NMC_EDITOR_MAIN_CMD_NMCLI:
8213                         g_print (_("nmcli [<conf-option> <value>]  :: nmcli configuration\n\n"
8214                                    "Configures nmcli. The following options are available:\n"
8215                                    "status-line yes | no          [default: no]\n"
8216                                    "save-confirmation yes | no    [default: yes]\n"
8217                                    "show-secrets yes | no         [default: no]\n"
8218                                    "prompt-color <color> | <0-8>  [default: 0]\n"
8219                                    "%s"  /* color table description */
8220                                    "\n"
8221                                    "Examples: nmcli> nmcli status-line yes\n"
8222                                    "          nmcli> nmcli save-confirmation no\n"
8223                                    "          nmcli> nmcli prompt-color 3\n"),
8224                                    "  0 = normal\n"
8225                                    "  1 = \33[30mblack\33[0m\n"
8226                                    "  2 = \33[31mred\33[0m\n"
8227                                    "  3 = \33[32mgreen\33[0m\n"
8228                                    "  4 = \33[33myellow\33[0m\n"
8229                                    "  5 = \33[34mblue\33[0m\n"
8230                                    "  6 = \33[35mmagenta\33[0m\n"
8231                                    "  7 = \33[36mcyan\33[0m\n"
8232                                    "  8 = \33[37mwhite\33[0m\n");
8233                         break;
8234                 case NMC_EDITOR_MAIN_CMD_QUIT:
8235                         g_print (_("quit  :: exit nmcli\n\n"
8236                                    "This command exits nmcli. When the connection being edited "
8237                                    "is not saved, the user is asked to confirm the action.\n"));
8238                         break;
8239                 default:
8240                         g_print (_("Unknown command: '%s'\n"), command);
8241                         break;
8242                 }
8243         }
8244 }
8245
8246 typedef enum {
8247         NMC_EDITOR_SUB_CMD_UNKNOWN = 0,
8248         NMC_EDITOR_SUB_CMD_SET,
8249         NMC_EDITOR_SUB_CMD_ADD,
8250         NMC_EDITOR_SUB_CMD_CHANGE,
8251         NMC_EDITOR_SUB_CMD_REMOVE,
8252         NMC_EDITOR_SUB_CMD_DESCRIBE,
8253         NMC_EDITOR_SUB_CMD_PRINT,
8254         NMC_EDITOR_SUB_CMD_BACK,
8255         NMC_EDITOR_SUB_CMD_HELP,
8256         NMC_EDITOR_SUB_CMD_QUIT
8257 } NmcEditorSubCmd;
8258
8259 static NmcEditorSubCmd
8260 parse_editor_sub_cmd (const char *cmd, char **cmd_arg)
8261 {
8262         NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN;
8263         char **vec;
8264
8265         vec = nmc_strsplit_set (cmd, " \t", 2);
8266         if (g_strv_length (vec) < 1) {
8267                 if (cmd_arg)
8268                         *cmd_arg = NULL;
8269                 return NMC_EDITOR_SUB_CMD_UNKNOWN;
8270         }
8271
8272         if (matches (vec[0], "set") == 0)
8273                 editor_cmd = NMC_EDITOR_SUB_CMD_SET;
8274         else if (matches (vec[0], "add") == 0)
8275                 editor_cmd = NMC_EDITOR_SUB_CMD_ADD;
8276         else if (matches (vec[0], "change") == 0)
8277                 editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE;
8278         else if (matches (vec[0], "remove") == 0)
8279                 editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE;
8280         else if (matches (vec[0], "describe") == 0)
8281                 editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE;
8282         else if (matches (vec[0], "print") == 0)
8283                 editor_cmd = NMC_EDITOR_SUB_CMD_PRINT;
8284         else if (matches (vec[0], "back") == 0)
8285                 editor_cmd = NMC_EDITOR_SUB_CMD_BACK;
8286         else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8287                 editor_cmd = NMC_EDITOR_SUB_CMD_HELP;
8288         else if (matches (vec[0], "quit") == 0)
8289                 editor_cmd = NMC_EDITOR_SUB_CMD_QUIT;
8290
8291         /* set pointer to command argument */
8292         if (cmd_arg)
8293                 *cmd_arg = g_strdup (vec[1]);
8294
8295         g_strfreev (vec);
8296         return editor_cmd;
8297 }
8298
8299 static void
8300 editor_sub_help (void)
8301 {
8302         g_print ("------------------------------------------------------------------------------\n");
8303         /* TRANSLATORS: do not translate command names and keywords before ::
8304          *              However, you should translate terms enclosed in <>.
8305          */
8306         g_print (_("---[ Property menu ]---\n"
8307                    "set      [<value>]               :: set new value\n"
8308                    "add      [<value>]               :: add new option to the property\n"
8309                    "change                           :: change current value\n"
8310                    "remove   [<index> | <option>]    :: delete the value\n"
8311                    "describe                         :: describe property\n"
8312                    "print    [setting | connection]  :: print property (setting/connection) value(s)\n"
8313                    "back                             :: go to upper level\n"
8314                    "help/?   [<command>]             :: print this help or command description\n"
8315                    "quit                             :: exit nmcli\n"));
8316         g_print ("------------------------------------------------------------------------------\n");
8317 }
8318
8319 static void
8320 editor_sub_usage (const char *command)
8321 {
8322
8323         if (!command)
8324                 editor_sub_help ();
8325         else {
8326                 /* detailed command descriptions */
8327                 NmcEditorSubCmd cmdsub = parse_editor_sub_cmd (command, NULL);
8328
8329                 switch (cmdsub) {
8330                 case NMC_EDITOR_SUB_CMD_SET:
8331                         g_print (_("set [<value>]  :: set new value\n\n"
8332                                    "This command sets provided <value> to this property\n"));
8333                         break;
8334                 case NMC_EDITOR_SUB_CMD_ADD:
8335                         g_print (_("add [<value>]  :: append new value to the property\n\n"
8336                                    "This command adds provided <value> to this property, if "
8337                                    "the property is of a container type. For single-valued "
8338                                    "properties the property value is replaced (same as 'set').\n"));
8339                         break;
8340                 case NMC_EDITOR_SUB_CMD_CHANGE:
8341                         g_print (_("change  :: change current value\n\n"
8342                                    "Displays current value and allows editing it.\n"));
8343                         break;
8344                 case NMC_EDITOR_SUB_CMD_REMOVE:
8345                         g_print (_("remove [<value>|<index>|<option name>]  :: delete the value\n\n"
8346                                    "Removes the property value. For single-valued properties, this sets the\n"
8347                                    "property back to its default value. For container-type properties, this removes\n"
8348                                    "all the values of that property, or you can specify an argument to remove just\n"
8349                                    "a single item or option. The argument is either a value or index of the item to\n"
8350                                    "remove, or an option name (for properties with named options).\n\n"
8351                                    "Examples: nmcli ipv4.dns> remove 8.8.8.8\n"
8352                                    "          nmcli ipv4.dns> remove 2\n"
8353                                    "          nmcli bond.options> remove downdelay\n\n"));
8354                         break;
8355                 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8356                         g_print (_("describe  :: describe property\n\n"
8357                                    "Shows property description. You can consult nm-settings(5) "
8358                                    "manual page to see all NM settings and properties.\n"));
8359                         break;
8360                 case NMC_EDITOR_SUB_CMD_PRINT:
8361                         g_print (_("print [property|setting|connection]  :: print property (setting, connection) value(s)\n\n"
8362                                    "Shows property value. Providing an argument you can also display "
8363                                    "values for the whole setting or connection.\n"));
8364                         break;
8365                 case NMC_EDITOR_SUB_CMD_BACK:
8366                         g_print (_("back  :: go to upper menu level\n\n"));
8367                         break;
8368                 case NMC_EDITOR_SUB_CMD_HELP:
8369                         g_print (_("help/? [<command>]  :: help for nmcli commands\n\n"));
8370                         break;
8371                 case NMC_EDITOR_SUB_CMD_QUIT:
8372                         g_print (_("quit  :: exit nmcli\n\n"
8373                                    "This command exits nmcli. When the connection being edited "
8374                                    "is not saved, the user is asked to confirm the action.\n"));
8375                         break;
8376                 default:
8377                         g_print (_("Unknown command: '%s'\n"), command);
8378                         break;
8379                 }
8380         }
8381 }
8382
8383 /*----------------------------------------------------------------------------*/
8384
8385 typedef struct {
8386         NMDevice *device;
8387         NMActiveConnection *ac;
8388         guint monitor_id;
8389 } MonitorACInfo;
8390
8391 static gboolean nmc_editor_cb_called;
8392 static GError *nmc_editor_error;
8393 static MonitorACInfo *nmc_editor_monitor_ac;
8394 static GMutex nmc_editor_mutex;
8395 static GCond nmc_editor_cond;
8396
8397 /*
8398  * Store 'error' to shared 'nmc_editor_error' and monitoring info to
8399  * 'nmc_editor_monitor_ac' and signal the condition so that
8400  * the 'editor-thread' thread could process that.
8401  */
8402 static void
8403 set_info_and_signal_editor_thread (GError *error, MonitorACInfo *monitor_ac_info)
8404 {
8405         g_mutex_lock (&nmc_editor_mutex);
8406         nmc_editor_cb_called = TRUE;
8407         nmc_editor_error = error ? g_error_copy (error) : NULL;
8408         nmc_editor_monitor_ac = monitor_ac_info;
8409         g_cond_signal (&nmc_editor_cond);
8410         g_mutex_unlock (&nmc_editor_mutex);
8411 }
8412
8413 static void
8414 add_connection_editor_cb (GObject *client,
8415                           GAsyncResult *result,
8416                           gpointer user_data)
8417 {
8418         NMRemoteConnection *connection;
8419         GError *error = NULL;
8420
8421         connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
8422         set_info_and_signal_editor_thread (error, NULL);
8423
8424         g_clear_object (&connection);
8425         g_clear_error (&error);
8426 }
8427
8428 static void
8429 update_connection_editor_cb (GObject *connection,
8430                              GAsyncResult *result,
8431                              gpointer user_data)
8432 {
8433         GError *error = NULL;
8434
8435         nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
8436                                                     result, &error);
8437         set_info_and_signal_editor_thread (error, NULL);
8438         g_clear_error (&error);
8439 }
8440
8441 static gboolean
8442 progress_activation_editor_cb (gpointer user_data)
8443 {
8444         MonitorACInfo *info = (MonitorACInfo *) user_data;
8445         NMDevice *device = info->device;
8446         NMActiveConnection *ac = info->ac;
8447         NMActiveConnectionState ac_state;
8448         NMDeviceState dev_state;
8449
8450         if (!device || !ac)
8451                 goto finish;
8452
8453         ac_state = nm_active_connection_get_state (ac);
8454         dev_state = nm_device_get_state (device);
8455
8456         nmc_terminal_show_progress (nmc_device_state_to_string (dev_state));
8457
8458         if (   ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
8459             || dev_state == NM_DEVICE_STATE_ACTIVATED) {
8460                 nmc_terminal_erase_line ();
8461                 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
8462                          nm_object_get_path (NM_OBJECT (ac)));
8463                 goto finish; /* we are done */
8464         } else if (   ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
8465                    || dev_state == NM_DEVICE_STATE_FAILED) {
8466                 nmc_terminal_erase_line ();
8467                 g_print (_("Error: Connection activation failed.\n"));
8468                 goto finish; /* we are done */
8469         }
8470
8471         return TRUE;
8472
8473 finish:
8474         if (device)
8475                 g_object_unref (device);
8476         if (ac)
8477                 g_object_unref (ac);
8478         return FALSE;
8479 }
8480
8481 static void
8482 activate_connection_editor_cb (GObject *client,
8483                                GAsyncResult *result,
8484                                gpointer user_data)
8485 {
8486         ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
8487         NMDevice *device = info->device;
8488         const GPtrArray *ac_devs;
8489         MonitorACInfo *monitor_ac_info = NULL;
8490         NMActiveConnection *active;
8491         GError *error = NULL;
8492
8493         active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
8494
8495         if (!error) {
8496                 if (!device) {
8497                         ac_devs = nm_active_connection_get_devices (active);
8498                         device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
8499                 }
8500                 if (device) {
8501                         monitor_ac_info = g_malloc0 (sizeof (AddConnectionInfo));
8502                         monitor_ac_info->device = g_object_ref (device);
8503                         monitor_ac_info->ac = active;
8504                         monitor_ac_info->monitor_id = g_timeout_add (120, progress_activation_editor_cb, monitor_ac_info);
8505                 } else
8506                         g_object_unref (active);
8507         }
8508         set_info_and_signal_editor_thread (error, monitor_ac_info);
8509         g_clear_error (&error);
8510 }
8511
8512 /*----------------------------------------------------------------------------*/
8513
8514 static void
8515 print_property_description (NMSetting *setting, const char *prop_name)
8516 {
8517         char *desc;
8518
8519         desc = nmc_setting_get_property_desc (setting, prop_name);
8520         g_print ("\n=== [%s] ===\n%s\n", prop_name, desc);
8521         g_free (desc);
8522 }
8523
8524 static void
8525 print_setting_description (NMSetting *setting)
8526 {
8527         /* Show description of all properties */
8528         char **all_props;
8529         int i;
8530
8531         all_props = nmc_setting_get_valid_properties (setting);
8532         g_print (("<<< %s >>>\n"), nm_setting_get_name (setting));
8533         for (i = 0; all_props && all_props[i]; i++)
8534                 print_property_description (setting, all_props[i]);
8535         g_strfreev (all_props);
8536 }
8537
8538 static gboolean
8539 connection_remove_setting (NMConnection *connection, NMSetting *setting)
8540 {
8541         gboolean mandatory;
8542
8543         g_return_val_if_fail (setting, FALSE);
8544
8545         mandatory = is_setting_mandatory (connection, setting);
8546         if (!mandatory) {
8547                 nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting));
8548                 return TRUE;
8549         }
8550         g_print (_("Error: setting '%s' is mandatory and cannot be removed.\n"),
8551                  nm_setting_get_name (setting));
8552         return FALSE;
8553 }
8554
8555 static void
8556 editor_show_status_line (NMConnection *connection, gboolean dirty, gboolean temp)
8557 {
8558         NMSettingConnection *s_con;
8559         const char *con_type, *con_id, *con_uuid;
8560
8561         s_con = nm_connection_get_setting_connection (connection);
8562         g_assert (s_con);
8563         con_type = nm_setting_connection_get_connection_type (s_con);
8564         con_id = nm_connection_get_id (connection);
8565         con_uuid = nm_connection_get_uuid (connection);
8566
8567         /* TRANSLATORS: status line in nmcli connection editor */
8568         g_print (_("[ Type: %s | Name: %s | UUID: %s | Dirty: %s | Temp: %s ]\n"),
8569                  con_type, con_id, con_uuid,
8570                  dirty ? _("yes") : _("no"),
8571                  temp ? _("yes") : _("no"));
8572 }
8573
8574 static gboolean
8575 refresh_remote_connection (GWeakRef *weak, NMRemoteConnection **remote)
8576 {
8577         gboolean previous;
8578
8579         g_return_val_if_fail (remote != NULL, FALSE);
8580
8581         previous = (*remote != NULL);
8582         if (*remote)
8583                 g_object_unref (*remote);
8584         *remote = g_weak_ref_get (weak);
8585
8586         return (previous && !*remote);
8587 }
8588
8589 static gboolean
8590 is_connection_dirty (NMConnection *connection, NMRemoteConnection *remote)
8591 {
8592         return !nm_connection_compare (connection,
8593                                        remote ? NM_CONNECTION (remote) : NULL,
8594                                        NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS |
8595                                        NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP);
8596 }
8597
8598 static gboolean
8599 confirm_quit (void)
8600 {
8601         char *answer;
8602         gboolean want_quit = FALSE;
8603
8604         answer = nmc_readline (_("The connection is not saved. "
8605                                  "Do you really want to quit? %s"),
8606                                prompt_yes_no (FALSE, NULL));
8607         answer = answer ? g_strstrip (answer) : NULL;
8608         if (answer && matches (answer, WORD_LOC_YES) == 0)
8609                 want_quit = TRUE;
8610
8611         g_free (answer);
8612         return want_quit;
8613 }
8614
8615 /*
8616  * Submenu for detailed property editing
8617  * Return: TRUE - continue;  FALSE - should quit
8618  */
8619 static gboolean
8620 property_edit_submenu (NmCli *nmc,
8621                        NMConnection *connection,
8622                        NMRemoteConnection **rem_con,
8623                        GWeakRef *rem_con_weak,
8624                        NMSetting *curr_setting,
8625                        const char *prop_name)
8626 {
8627         NmcEditorSubCmd cmdsub;
8628         gboolean cmd_property_loop = TRUE;
8629         gboolean should_quit = FALSE;
8630         char *prop_val_user;
8631         gboolean set_result;
8632         GError *tmp_err = NULL;
8633         char *prompt;
8634         gboolean dirty;
8635         GValue prop_g_value = G_VALUE_INIT;
8636         gboolean temp_changes;
8637         gboolean removed;
8638
8639         /* Set global variable for use in TAB completion */
8640         nmc_tab_completion.property = prop_name;
8641
8642         prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
8643                                "nmcli %s.%s> ",
8644                                nm_setting_get_name (curr_setting), prop_name);
8645
8646         while (cmd_property_loop) {
8647                 char *cmd_property_user;
8648                 char *cmd_property_arg;
8649
8650                 /* Get the remote connection again, it may have disapeared */
8651                 removed = refresh_remote_connection (rem_con_weak, rem_con);
8652                 if (removed)
8653                         g_print (_("The connection profile has been removed from another client. "
8654                                    "You may type 'save' in the main menu to restore it.\n"));
8655
8656                 /* Connection is dirty? (not saved or differs from the saved) */
8657                 dirty = is_connection_dirty (connection, *rem_con);
8658                 temp_changes = *rem_con ? nm_remote_connection_get_unsaved (*rem_con) : TRUE;
8659                 if (nmc->editor_status_line)
8660                         editor_show_status_line (connection, dirty, temp_changes);
8661
8662                 cmd_property_user = nmc_readline ("%s", prompt);
8663                 if (!cmd_property_user || *cmd_property_user == '\0')
8664                         continue;
8665                 cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg);
8666
8667                 switch (cmdsub) {
8668                 case NMC_EDITOR_SUB_CMD_SET:
8669                 case NMC_EDITOR_SUB_CMD_ADD:
8670                         /* list, arrays,...: SET replaces the whole property value
8671                          *                   ADD adds the new value(s)
8672                          * single values:  : both SET and ADD sets the new value
8673                          */
8674                         if (!cmd_property_arg) {
8675                                 const char **avals = nmc_setting_get_property_allowed_values (curr_setting, prop_name);
8676                                 if (avals) {
8677                                         char *avals_str = nmc_util_strv_for_display (avals, FALSE);
8678                                         g_print (_("Allowed values for '%s' property: %s\n"),
8679                                                  prop_name, avals_str);
8680                                         g_free (avals_str);
8681                                 }
8682                                 prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
8683                         } else
8684                                 prop_val_user = g_strdup (cmd_property_arg);
8685
8686                         /* nmc_setting_set_property() only adds new value, thus we have to
8687                          * remove the original value and save it for error cases.
8688                          */
8689                         if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8690                                 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8691                                 nmc_property_set_default_value (curr_setting, prop_name);
8692                         }
8693
8694                         set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err);
8695                         g_free (prop_val_user);
8696                         if (!set_result) {
8697                                 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8698                                 g_clear_error (&tmp_err);
8699                                 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8700                                         /* Block change signals and restore original value */
8701                                         g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8702                                         nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8703                                         g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8704                                 }
8705                         }
8706                         if (G_IS_VALUE (&prop_g_value))
8707                                 g_value_unset (&prop_g_value);
8708                         break;
8709
8710                 case NMC_EDITOR_SUB_CMD_CHANGE:
8711                         rl_startup_hook = nmc_rl_set_deftext;
8712                         nmc_rl_pre_input_deftext = nmc_setting_get_property_parsable (curr_setting, prop_name, NULL);
8713                         prop_val_user = nmc_readline (_("Edit '%s' value: "), prop_name);
8714
8715                         nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8716                         nmc_property_set_default_value (curr_setting, prop_name);
8717
8718                         if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) {
8719                                 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8720                                 g_clear_error (&tmp_err);
8721                                 g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8722                                 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8723                                 g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8724                         }
8725                         g_free (prop_val_user);
8726                         if (G_IS_VALUE (&prop_g_value))
8727                                 g_value_unset (&prop_g_value);
8728                         break;
8729
8730                 case NMC_EDITOR_SUB_CMD_REMOVE:
8731                         if (cmd_property_arg) {
8732                                 unsigned long val_int = G_MAXUINT32;
8733                                 char *option = NULL;
8734
8735                                 if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int))
8736                                         option = g_strdup (cmd_property_arg);
8737
8738                                 if (!nmc_setting_remove_property_option (curr_setting, prop_name,
8739                                                                          option ? g_strstrip (option) : NULL,
8740                                                                          (guint32) val_int,
8741                                                                          &tmp_err)) {
8742                                         g_print (_("Error: %s\n"), tmp_err->message);
8743                                         g_clear_error (&tmp_err);
8744                                 }
8745                                 g_free (option);
8746                         } else {
8747                                 if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) {
8748                                         g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
8749                                                  tmp_err->message);
8750                                         g_clear_error (&tmp_err);
8751                                 }
8752                         }
8753                         break;
8754
8755                 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8756                         /* Show property description */
8757                         print_property_description (curr_setting, prop_name);
8758                         break;
8759
8760                 case NMC_EDITOR_SUB_CMD_PRINT:
8761                         /* Print current connection settings/properties */
8762                         if (cmd_property_arg) {
8763                                 if (matches (cmd_property_arg, "setting") == 0)
8764                                         editor_show_setting (curr_setting, nmc);
8765                                 else if (   matches (cmd_property_arg, "connection") == 0
8766                                          || matches (cmd_property_arg, "all") == 0)
8767                                         editor_show_connection (connection, nmc);
8768                                 else
8769                                         g_print (_("Unknown command argument: '%s'\n"), cmd_property_arg);
8770                         } else {
8771                                 char *prop_val =  nmc_setting_get_property (curr_setting, prop_name, NULL);
8772                                 g_print ("%s: %s\n", prop_name, prop_val);
8773                                 g_free (prop_val);
8774                         }
8775                         break;
8776
8777                 case NMC_EDITOR_SUB_CMD_BACK:
8778                         /* Set global variable for use in TAB completion */
8779                         nmc_tab_completion.property = NULL;
8780                         cmd_property_loop = FALSE;
8781                         break;
8782
8783                 case NMC_EDITOR_SUB_CMD_HELP:
8784                         editor_sub_usage (cmd_property_arg);
8785                         break;
8786
8787                 case NMC_EDITOR_SUB_CMD_QUIT:
8788                         if (is_connection_dirty (connection, *rem_con)) {
8789                                 if (confirm_quit ()) {
8790                                         cmd_property_loop = FALSE;
8791                                         should_quit = TRUE;  /* we will quit nmcli */
8792                                 }
8793                         } else {
8794                                 cmd_property_loop = FALSE;
8795                                 should_quit = TRUE;  /* we will quit nmcli */
8796                         }
8797                         break;
8798
8799                 case NMC_EDITOR_SUB_CMD_UNKNOWN:
8800                 default:
8801                         g_print (_("Unknown command: '%s'\n"), cmd_property_user);
8802                         break;
8803                 }
8804                 g_free (cmd_property_user);
8805                 g_free (cmd_property_arg);
8806         }
8807         g_free (prompt);
8808
8809         return !should_quit;
8810 }
8811
8812 /*
8813  * Split 'str' in the following format:  [[[setting.]property] [value]]
8814  * and return the components in 'setting', 'property' and 'value'
8815  * Use g_free() to deallocate the returned strings.
8816  */
8817 static void
8818 split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value)
8819 {
8820         char **args, **items;
8821
8822         if (!str)
8823                 return;
8824
8825         args = nmc_strsplit_set (str, " \t", 2);
8826         if (args[0]) {
8827                 items = nmc_strsplit_set (args[0], ".", 2);
8828                 if (g_strv_length (items) == 2) {
8829                         if (setting)
8830                                 *setting = g_strdup (items[0]);
8831                         if (property)
8832                                 *property = g_strdup (items[1]);
8833                 } else {
8834                         if (property)
8835                                 *property = g_strdup (items[0]);
8836                 }
8837                 g_strfreev (items);
8838
8839                 if (value && args[1])
8840                         *value = g_strstrip (g_strdup (args[1]));
8841         }
8842         g_strfreev (args);
8843 }
8844
8845 static NMSetting *
8846 create_setting_by_name (const char *name, const NameItem *valid_settings_main, const NameItem *valid_settings_slave)
8847 {
8848         const char *setting_name;
8849         NMSetting *setting = NULL;
8850
8851         /* Get a valid setting name */
8852         setting_name = check_valid_name (name, valid_settings_main, valid_settings_slave, NULL);
8853
8854         if (setting_name) {
8855                 setting = nmc_setting_new_for_name (setting_name);
8856                 if (!setting)
8857                         return NULL; /* This should really not happen */
8858                 nmc_setting_custom_init (setting);
8859         }
8860         return setting;
8861 }
8862
8863 static const char *
8864 ask_check_setting (const char *arg,
8865                    const NameItem *valid_settings_main,
8866                    const NameItem *valid_settings_slave,
8867                    const char *valid_settings_str)
8868 {
8869         char *setting_name_user;
8870         const char *setting_name;
8871         GError *err = NULL;
8872
8873         if (!arg) {
8874                 g_print (_("Available settings: %s\n"), valid_settings_str);
8875                 setting_name_user = nmc_readline (EDITOR_PROMPT_SETTING);
8876         } else
8877                 setting_name_user = g_strdup (arg);
8878
8879         if (setting_name_user)
8880                 g_strstrip (setting_name_user);
8881
8882         if (!(setting_name = check_valid_name (setting_name_user,
8883                                                valid_settings_main,
8884                                                valid_settings_slave,
8885                                                &err))) {
8886                 g_print (_("Error: invalid setting name; %s\n"), err->message);
8887                 g_clear_error (&err);
8888         }
8889         g_free (setting_name_user);
8890         return setting_name;
8891 }
8892
8893 static const char *
8894 ask_check_property (const char *arg,
8895                     const char **valid_props,
8896                     const char *valid_props_str)
8897 {
8898         char *prop_name_user;
8899         const char *prop_name;
8900         GError *tmp_err = NULL;
8901
8902         if (!arg) {
8903                 g_print (_("Available properties: %s\n"), valid_props_str);
8904                 prop_name_user = nmc_readline (EDITOR_PROMPT_PROPERTY);
8905                 if (prop_name_user)
8906                         g_strstrip (prop_name_user);
8907         } else
8908                 prop_name_user = g_strdup (arg);
8909
8910         if (!(prop_name = nmc_string_is_valid (prop_name_user, valid_props, &tmp_err))) {
8911                 g_print (_("Error: property %s\n"), tmp_err->message);
8912                 g_clear_error (&tmp_err);
8913         }
8914         g_free (prop_name_user);
8915         return prop_name;
8916 }
8917
8918 /* Copy timestamp from src do dst */
8919 static void
8920 update_connection_timestamp (NMConnection *src, NMConnection *dst)
8921 {
8922         NMSettingConnection *s_con_src, *s_con_dst;
8923
8924         s_con_src = nm_connection_get_setting_connection (src);
8925         s_con_dst = nm_connection_get_setting_connection (dst);
8926         if (s_con_src && s_con_dst) {
8927                 guint64 timestamp = nm_setting_connection_get_timestamp (s_con_src);
8928                 g_object_set (s_con_dst, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
8929         }
8930 }
8931
8932 static gboolean
8933 confirm_connection_saving (NMConnection *local, NMConnection *remote)
8934 {
8935         NMSettingConnection *s_con_loc, *s_con_rem;
8936         gboolean ac_local, ac_remote;
8937         gboolean confirmed = TRUE;
8938
8939         s_con_loc = nm_connection_get_setting_connection (local);
8940         g_assert (s_con_loc);
8941         ac_local = nm_setting_connection_get_autoconnect (s_con_loc);
8942
8943         if (remote) {
8944                 s_con_rem = nm_connection_get_setting_connection (remote);
8945                 g_assert (s_con_rem);
8946                 ac_remote = nm_setting_connection_get_autoconnect (s_con_rem);
8947         } else
8948                 ac_remote = FALSE;
8949
8950         if (ac_local && !ac_remote) {
8951                 char *answer;
8952                 answer = nmc_readline (_("Saving the connection with 'autoconnect=yes'. "
8953                                          "That might result in an immediate activation of the connection.\n"
8954                                          "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL));
8955                 answer = answer ? g_strstrip (answer) : NULL;
8956                 if (!answer || matches (answer, WORD_LOC_YES) == 0)
8957                         confirmed = TRUE;
8958                 else
8959                         confirmed = FALSE;
8960                 g_free (answer);
8961         }
8962         return confirmed;
8963 }
8964
8965 typedef struct {
8966         guint level;
8967         char *main_prompt;
8968         NMSetting *curr_setting;
8969         char **valid_props;
8970         char *valid_props_str;
8971 } NmcEditorMenuContext;
8972
8973 static void
8974 menu_switch_to_level0 (NmCli *nmc,
8975                        NmcEditorMenuContext *menu_ctx,
8976                        const char *prompt,
8977                        NmcTermColor prompt_color)
8978 {
8979         menu_ctx->level = 0;
8980         g_free (menu_ctx->main_prompt);
8981         menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL, "%s", prompt);
8982         menu_ctx->curr_setting = NULL;
8983         g_strfreev (menu_ctx->valid_props);
8984         menu_ctx->valid_props = NULL;
8985         g_free (menu_ctx->valid_props_str);
8986         menu_ctx->valid_props_str = NULL;
8987 }
8988
8989 static void
8990 menu_switch_to_level1 (NmCli *nmc,
8991                        NmcEditorMenuContext *menu_ctx,
8992                        NMSetting *setting,
8993                        const char *setting_name,
8994                        NmcTermColor prompt_color)
8995 {
8996         menu_ctx->level = 1;
8997         g_free (menu_ctx->main_prompt);
8998         menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL,
8999                                               "nmcli %s> ", setting_name);
9000         menu_ctx->curr_setting = setting;
9001         g_strfreev (menu_ctx->valid_props);
9002         menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting);
9003         g_free (menu_ctx->valid_props_str);
9004         menu_ctx->valid_props_str = g_strjoinv (", ", menu_ctx->valid_props);
9005 }
9006
9007 static gboolean
9008 editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
9009 {
9010         NMSettingConnection *s_con;
9011         NMRemoteConnection *rem_con;
9012         NMRemoteConnection *con_tmp;
9013         GWeakRef weak = { { NULL } };
9014         gboolean removed;
9015         NmcEditorMainCmd cmd;
9016         char *cmd_user;
9017         gboolean cmd_loop = TRUE;
9018         char *cmd_arg = NULL;
9019         char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v;
9020         const char *BASE_PROMPT = "nmcli> ";
9021         const NameItem *valid_settings_main = NULL;
9022         const NameItem *valid_settings_slave = NULL;
9023         char *valid_settings_str = NULL;
9024         const char *s_type = NULL;
9025         char *slv_type;
9026         AddConnectionInfo *info = NULL;
9027         gboolean dirty;
9028         gboolean temp_changes;
9029         GError *err1 = NULL;
9030         NmcEditorMenuContext menu_ctx;
9031
9032         s_con = nm_connection_get_setting_connection (connection);
9033         if (s_con)
9034                 s_type = nm_setting_connection_get_slave_type (s_con);
9035         slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
9036
9037         valid_settings_main = get_valid_settings_array (connection_type);
9038         valid_settings_slave = get_valid_settings_array (slv_type);
9039         g_free (slv_type);
9040
9041         valid_settings_str = get_valid_options_string (valid_settings_main, valid_settings_slave);
9042         g_print (_("You may edit the following settings: %s\n"), valid_settings_str);
9043
9044         menu_ctx.level = 0;
9045         menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9046                                              BASE_PROMPT);
9047         menu_ctx.curr_setting = NULL;
9048         menu_ctx.valid_props = NULL;
9049         menu_ctx.valid_props_str = NULL;
9050
9051         /* Get remote connection */
9052         con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9053                                                     nm_connection_get_uuid (connection));
9054         g_weak_ref_init (&weak, con_tmp);
9055         rem_con = g_weak_ref_get (&weak);
9056
9057         while (cmd_loop) {
9058                 /* Connection is dirty? (not saved or differs from the saved) */
9059                 dirty = is_connection_dirty (connection, rem_con);
9060                 temp_changes = rem_con ? nm_remote_connection_get_unsaved (rem_con) : TRUE;
9061                 if (nmc->editor_status_line)
9062                         editor_show_status_line (connection, dirty, temp_changes);
9063
9064                 /* Read user input */
9065                 cmd_user = nmc_readline ("%s", menu_ctx.main_prompt);
9066
9067                 /* Get the remote connection again, it may have disapeared */
9068                 removed = refresh_remote_connection (&weak, &rem_con);
9069                 if (removed)
9070                         g_print (_("The connection profile has been removed from another client. "
9071                                    "You may type 'save' to restore it.\n"));
9072
9073                 if (!cmd_user || *cmd_user == '\0')
9074                         continue;
9075                 cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
9076
9077                 cmd_arg_s = NULL;
9078                 cmd_arg_p = NULL;
9079                 cmd_arg_v = NULL;
9080                 split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
9081                 switch (cmd) {
9082                 case NMC_EDITOR_MAIN_CMD_SET:
9083                         /* Set property value */
9084                         if (!cmd_arg) {
9085                                 if (menu_ctx.level == 1) {
9086                                         const char *prop_name;
9087                                         char *prop_val_user = NULL;
9088                                         const char **avals;
9089                                         GError *tmp_err = NULL;
9090
9091                                         prop_name = ask_check_property (cmd_arg,
9092                                                                         (const char **) menu_ctx.valid_props,
9093                                                                         menu_ctx.valid_props_str);
9094                                         if (!prop_name)
9095                                                 break;
9096
9097                                         avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name);
9098                                         if (avals) {
9099                                                 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9100                                                 g_print (_("Allowed values for '%s' property: %s\n"),
9101                                                          prop_name, avals_str);
9102                                                 g_free (avals_str);
9103                                         }
9104                                         prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
9105
9106                                         /* Set property value */
9107                                         if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) {
9108                                                 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
9109                                                 g_clear_error (&tmp_err);
9110                                         }
9111                                 } else {
9112                                         g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9113                                         g_print (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n"));
9114                                 }
9115                         } else {
9116                                 NMSetting *ss = NULL;
9117                                 gboolean created_ss = FALSE;
9118                                 char *prop_name;
9119                                 GError *tmp_err = NULL;
9120
9121                                 if (cmd_arg_s) {
9122                                         /* setting provided as "setting.property" */
9123                                         ss = is_setting_valid (connection, valid_settings_main, valid_settings_slave, cmd_arg_s);
9124                                         if (!ss) {
9125                                                 ss = create_setting_by_name (cmd_arg_s, valid_settings_main, valid_settings_slave);
9126                                                 if (!ss) {
9127                                                         g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9128                                                                  cmd_arg_s, valid_settings_str);
9129                                                         break;
9130                                                 }
9131                                                 created_ss = TRUE;
9132                                         }
9133                                 } else {
9134                                         if (menu_ctx.curr_setting)
9135                                                 ss = menu_ctx.curr_setting;
9136                                         else {
9137                                                 g_print (_("Error: missing setting for '%s' property\n"), cmd_arg_p);
9138                                                 break;
9139                                         }
9140                                 }
9141
9142                                 prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9143                                 if (!prop_name) {
9144                                         g_print (_("Error: invalid property: %s\n"), tmp_err->message);
9145                                         g_clear_error (&tmp_err);
9146                                         if (created_ss)
9147                                                 g_object_unref (ss);
9148                                         break;
9149                                 }
9150
9151
9152
9153                                 /* Ask for value */
9154                                 if (!cmd_arg_v) {
9155                                         const char **avals = nmc_setting_get_property_allowed_values (ss, prop_name);
9156                                         if (avals) {
9157                                                 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9158                                                 g_print (_("Allowed values for '%s' property: %s\n"),
9159                                                          prop_name, avals_str);
9160                                                 g_free (avals_str);
9161                                         }
9162                                         cmd_arg_v = nmc_readline (_("Enter '%s' value: "), prop_name);
9163                                 }
9164
9165                                 /* Set property value */
9166                                 if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) {
9167                                         g_print (_("Error: failed to set '%s' property: %s\n"),
9168                                                  prop_name, tmp_err->message);
9169                                         g_clear_error (&tmp_err);
9170                                 }
9171
9172                                 if (created_ss)
9173                                         nm_connection_add_setting (connection, ss);
9174                                 g_free (prop_name);
9175                         }
9176                         break;
9177
9178                 case NMC_EDITOR_MAIN_CMD_GOTO:
9179                         /* cmd_arg_s != NULL means 'setting.property' argument */
9180                         if (menu_ctx.level == 0 || cmd_arg_s) {
9181                                 /* in top level - no setting selected yet */
9182                                 const char *setting_name;
9183                                 NMSetting *setting;
9184                                 const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p;
9185
9186                                 setting_name = ask_check_setting (user_arg,
9187                                                                   valid_settings_main,
9188                                                                   valid_settings_slave,
9189                                                                   valid_settings_str);
9190                                 if (!setting_name)
9191                                         break;
9192
9193                                 setting = nm_connection_get_setting_by_name (connection, setting_name);
9194                                 if (!setting) {
9195                                         setting = nmc_setting_new_for_name (setting_name);
9196                                         if (!setting) {
9197                                                 g_print (_("Error: unknown setting '%s'\n"), setting_name);
9198                                                 break;
9199                                         }
9200                                         nmc_setting_custom_init (setting);
9201                                         nm_connection_add_setting (connection, setting);
9202                                 }
9203                                 /* Set global variable for use in TAB completion */
9204                                 nmc_tab_completion.setting = setting;
9205
9206                                 /* Switch to level 1 */
9207                                 menu_switch_to_level1 (nmc, &menu_ctx, setting, setting_name, nmc->editor_prompt_color);
9208
9209                                 if (!cmd_arg_s) {
9210                                         g_print (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str);
9211                                         break;
9212                                 }
9213                         }
9214                         if (menu_ctx.level == 1 || cmd_arg_s) {
9215                                 /* level 1 - setting selected */
9216                                 const char *prop_name;
9217
9218                                 prop_name = ask_check_property (cmd_arg_p,
9219                                                                 (const char **) menu_ctx.valid_props,
9220                                                                 menu_ctx.valid_props_str);
9221                                 if (!prop_name)
9222                                         break;
9223
9224                                 /* submenu - level 2 - editing properties */
9225                                 cmd_loop = property_edit_submenu (nmc,
9226                                                                   connection,
9227                                                                   &rem_con,
9228                                                                   &weak,
9229                                                                   menu_ctx.curr_setting,
9230                                                                   prop_name);
9231                         }
9232                         break;
9233
9234                 case NMC_EDITOR_MAIN_CMD_REMOVE:
9235                         /* Remove setting from connection, or delete value of a property */
9236                         if (!cmd_arg) {
9237                                 if (menu_ctx.level == 1) {
9238                                         GError *tmp_err = NULL;
9239                                         const char *prop_name;
9240
9241                                         prop_name = ask_check_property (cmd_arg,
9242                                                                         (const char **) menu_ctx.valid_props,
9243                                                                         menu_ctx.valid_props_str);
9244                                         if (!prop_name)
9245                                                 break;
9246
9247                                         /* Delete property value */
9248                                         if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) {
9249                                                 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9250                                                          tmp_err->message);
9251                                                 g_clear_error (&tmp_err);
9252                                         }
9253                                 } else
9254                                         g_print (_("Error: no argument given; valid are [%s]\n"), valid_settings_str);
9255                         } else {
9256                                 NMSetting *ss = NULL;
9257                                 gboolean descr_all;
9258                                 char *user_s;
9259
9260                                 /* cmd_arg_s != NULL means argument is "setting.property" */
9261                                 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9262                                 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9263                                 if (user_s) {
9264                                         ss = is_setting_valid (connection,
9265                                                                valid_settings_main,
9266                                                                valid_settings_slave,
9267                                                                user_s);
9268                                         if (!ss) {
9269                                                 if (check_valid_name (user_s,
9270                                                                       valid_settings_main,
9271                                                                       valid_settings_slave,
9272                                                                       NULL))
9273                                                         g_print (_("Setting '%s' is not present in the connection.\n"),
9274                                                                  user_s);
9275                                                 else
9276                                                         g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9277                                                                  user_s, valid_settings_str);
9278                                                 break;
9279                                         }
9280                                 } else
9281                                         ss = menu_ctx.curr_setting;
9282
9283                                 if (descr_all) {
9284                                         /* Remove setting from the connection */
9285                                         connection_remove_setting (connection, ss);
9286                                         if (ss == menu_ctx.curr_setting) {
9287                                                 /* If we removed the setting we are in, go up */
9288                                                 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9289                                                 nmc_tab_completion.setting = NULL;  /* for TAB completion */
9290                                         }
9291                                 } else {
9292                                         GError *tmp_err = NULL;
9293                                         char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9294                                         if (prop_name) {
9295                                                 /* Delete property value */
9296                                                 if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) {
9297                                                         g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9298                                                                  tmp_err->message);
9299                                                         g_clear_error (&tmp_err);
9300                                                 }
9301                                         } else {
9302                                                 /* If the string is not a property, try it as a setting */
9303                                                 NMSetting *s_tmp;
9304                                                 s_tmp = is_setting_valid (connection,
9305                                                                           valid_settings_main,
9306                                                                           valid_settings_slave,
9307                                                                           cmd_arg_p);
9308                                                 if (s_tmp) {
9309                                                         /* Remove setting from the connection */
9310                                                         connection_remove_setting (connection, s_tmp);
9311                                                         /* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */
9312                                                         if (ss == menu_ctx.curr_setting) {
9313                                                                 /* If we removed the setting we are in, go up */
9314                                                                 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9315                                                                 nmc_tab_completion.setting = NULL;  /* for TAB completion */
9316                                                         }
9317                                                 } else
9318                                                         g_print (_("Error: %s properties, nor it is a setting name.\n"),
9319                                                                  tmp_err->message);
9320                                                 g_clear_error (&tmp_err);
9321                                         }
9322                                         g_free (prop_name);
9323                                 }
9324                         }
9325                         break;
9326
9327                 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
9328                         /* Print property description */
9329                         if (!cmd_arg) {
9330                                 if (menu_ctx.level == 1) {
9331                                         const char *prop_name;
9332
9333                                         prop_name = ask_check_property (cmd_arg,
9334                                                                         (const char **) menu_ctx.valid_props,
9335                                                                         menu_ctx.valid_props_str);
9336                                         if (!prop_name)
9337                                                 break;
9338
9339                                         /* Show property description */
9340                                         print_property_description (menu_ctx.curr_setting, prop_name);
9341                                 } else {
9342                                         g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9343                                         g_print (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n"));
9344                                 }
9345                         } else {
9346                                 NMSetting *ss = NULL;
9347                                 gboolean unref_ss = FALSE;
9348                                 gboolean descr_all;
9349                                 char *user_s;
9350
9351                                 /* cmd_arg_s != NULL means argument is "setting.property" */
9352                                 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9353                                 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9354                                 if (user_s) {
9355                                         ss = is_setting_valid (connection,
9356                                                                valid_settings_main,
9357                                                                valid_settings_slave,
9358                                                                user_s);
9359                                         if (!ss) {
9360                                                 ss = create_setting_by_name (user_s,
9361                                                                              valid_settings_main,
9362                                                                              valid_settings_slave);
9363                                                 if (!ss) {
9364                                                         g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9365                                                                  user_s, valid_settings_str);
9366                                                         break;
9367                                                 }
9368                                                 unref_ss = TRUE;
9369                                         }
9370                                 } else
9371                                         ss = menu_ctx.curr_setting;
9372
9373                                 if (descr_all) {
9374                                         /* Show description for all properties */
9375                                         print_setting_description (ss);
9376                                 } else {
9377                                         GError *tmp_err = NULL;
9378                                         char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9379                                         if (prop_name) {
9380                                                 /* Show property description */
9381                                                 print_property_description (ss, prop_name);
9382                                         } else {
9383                                                 /* If the string is not a property, try it as a setting */
9384                                                 NMSetting *s_tmp;
9385                                                 s_tmp = is_setting_valid (connection,
9386                                                                           valid_settings_main,
9387                                                                           valid_settings_slave,
9388                                                                           cmd_arg_p);
9389                                                 if (s_tmp)
9390                                                         print_setting_description (s_tmp);
9391                                                 else
9392                                                         g_print (_("Error: invalid property: %s, "
9393                                                                    "neither a valid setting name.\n"),
9394                                                                  tmp_err->message);
9395                                                 g_clear_error (&tmp_err);
9396                                         }
9397                                         g_free (prop_name);
9398                                 }
9399                                 if (unref_ss)
9400                                         g_object_unref (ss);
9401                         }
9402                         break;
9403
9404                 case NMC_EDITOR_MAIN_CMD_PRINT:
9405                         /* Print current connection settings/properties */
9406                         if (cmd_arg) {
9407                                 if (strcmp (cmd_arg, "all") == 0)
9408                                         editor_show_connection (connection, nmc);
9409                                 else {
9410                                         NMSetting *ss = NULL;
9411                                         gboolean whole_setting;
9412                                         char *user_s;
9413
9414                                         /* cmd_arg_s != NULL means argument is "setting.property" */
9415                                         whole_setting = !cmd_arg_s && !menu_ctx.curr_setting;
9416                                         user_s = whole_setting ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9417                                         if (user_s) {
9418                                                 const char *s_name;
9419                                                 s_name = check_valid_name (user_s,
9420                                                                            valid_settings_main,
9421                                                                            valid_settings_slave,
9422                                                                            NULL);
9423                                                 if (!s_name) {
9424                                                         g_print (_("Error: unknown setting: '%s'\n"), user_s);
9425                                                         break;
9426                                                 }
9427                                                 ss = nm_connection_get_setting_by_name (connection, s_name);
9428                                                 if (!ss) {
9429                                                         g_print (_("Error: '%s' setting not present in the connection\n"), s_name);
9430                                                         break;
9431                                                 }
9432                                         } else
9433                                                 ss = menu_ctx.curr_setting;
9434
9435                                         if (whole_setting) {
9436                                                 /* Print the whole setting */
9437                                                 editor_show_setting (ss, nmc);
9438                                         } else {
9439                                                 GError *err = NULL;
9440                                                 char *prop_name = is_property_valid (ss, cmd_arg_p, &err);
9441                                                 if (prop_name) {
9442                                                         /* Print one property */
9443                                                         char *prop_val = nmc_setting_get_property (ss, prop_name, NULL);
9444                                                         g_print ("%s.%s: %s\n", nm_setting_get_name (ss),prop_name , prop_val);
9445                                                         g_free (prop_val);
9446                                                 } else {
9447                                                         /* If the string is not a property, try it as a setting */
9448                                                         NMSetting *s_tmp;
9449                                                         s_tmp = is_setting_valid (connection,
9450                                                                                   valid_settings_main,
9451                                                                                   valid_settings_slave,
9452                                                                                   cmd_arg_p);
9453                                                         if (s_tmp) {
9454                                                                 /* Print the whole setting */
9455                                                                 editor_show_setting (s_tmp, nmc);
9456                                                         } else
9457                                                                 g_print (_("Error: invalid property: %s%s\n"),
9458                                                                          err->message,
9459                                                                          cmd_arg_s ? "" : _(", neither a valid setting name"));
9460                                                         g_clear_error (&err);
9461                                                 }
9462                                                 g_free (prop_name);
9463                                         }
9464                                 }
9465                         } else {
9466                                 if (menu_ctx.curr_setting)
9467                                         editor_show_setting (menu_ctx.curr_setting, nmc);
9468                                 else
9469                                         editor_show_connection (connection, nmc);
9470                         }
9471                         break;
9472
9473                 case NMC_EDITOR_MAIN_CMD_VERIFY:
9474                         /* Verify current setting or the whole connection */
9475                         if (cmd_arg && strcmp (cmd_arg, "all") && strcmp (cmd_arg, "fix")) {
9476                                 g_print (_("Invalid verify option: %s\n"), cmd_arg);
9477                                 break;
9478                         }
9479
9480                         if (   menu_ctx.curr_setting
9481                             && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) {
9482                                 GError *tmp_err = NULL;
9483                                 (void) nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err);
9484                                 g_print (_("Verify setting '%s': %s\n"),
9485                                          nm_setting_get_name (menu_ctx.curr_setting),
9486                                          tmp_err ? tmp_err->message : "OK");
9487                                 g_clear_error (&tmp_err);
9488                         } else {
9489                                 GError *tmp_err = NULL;
9490                                 gboolean valid, modified;
9491                                 gboolean fixed = TRUE;
9492
9493                                 valid = nm_connection_verify (connection, &tmp_err);
9494                                 if (!valid && (g_strcmp0 (cmd_arg, "fix") == 0)) {
9495                                         /* Try to fix normalizable errors */
9496                                         g_clear_error (&tmp_err);
9497                                         fixed = nm_connection_normalize (connection, NULL, &modified, &tmp_err);
9498                                 }
9499                                 g_print (_("Verify connection: %s\n"),
9500                                          tmp_err ? tmp_err->message : "OK");
9501                                 if (!fixed)
9502                                         g_print (_("The error cannot be fixed automatically.\n"));
9503                                 g_clear_error (&tmp_err);
9504                         }
9505                         break;
9506
9507                 case NMC_EDITOR_MAIN_CMD_SAVE:
9508                         /* Save the connection */
9509                         if (nm_connection_verify (connection, &err1)) {
9510                                 gboolean persistent = TRUE;
9511
9512                                 /* parse argument */
9513                                 if (cmd_arg) {
9514                                         if (matches (cmd_arg, "temporary") == 0)
9515                                                 persistent = FALSE;
9516                                         else if (matches (cmd_arg, "persistent") == 0)
9517                                                 persistent = TRUE;
9518                                         else {
9519                                                 g_print (_("Error: invalid argument '%s'\n"), cmd_arg);
9520                                                 break;
9521                                         }
9522                                 }
9523
9524                                 /* Ask for save confirmation if the connection changes to autoconnect=yes */
9525                                 if (nmc->editor_save_confirmation)
9526                                         if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con)))
9527                                                 break;
9528
9529                                 if (!rem_con) {
9530                                         /* Tell the settings service to add the new connection */
9531                                         info = g_malloc0 (sizeof (AddConnectionInfo));
9532                                         info->nmc = nmc;
9533                                         info->con_name = g_strdup (nm_connection_get_id (connection));
9534                                         add_new_connection (persistent,
9535                                                             nmc->client,
9536                                                             connection,
9537                                                             add_connection_editor_cb,
9538                                                             info);
9539                                 } else {
9540                                         /* Save/update already saved (existing) connection */
9541                                         nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con),
9542                                                                                         connection);
9543                                         update_connection (persistent, rem_con, update_connection_editor_cb, NULL);
9544                                 }
9545
9546                                 g_mutex_lock (&nmc_editor_mutex);
9547                                 //FIXME: add also a timeout for cases the callback is not called
9548                                 while (!nmc_editor_cb_called)
9549                                         g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9550
9551                                 if (nmc_editor_error) {
9552                                         g_print (_("Error: Failed to save '%s' (%s) connection: %s\n"),
9553                                                  nm_connection_get_id (connection),
9554                                                  nm_connection_get_uuid (connection),
9555                                                  nmc_editor_error->message);
9556                                         g_error_free (nmc_editor_error);
9557                                 } else {
9558                                         g_print (!rem_con ?
9559                                                  _("Connection '%s' (%s) successfully saved.\n") :
9560                                                  _("Connection '%s' (%s) successfully updated.\n"),
9561                                                  nm_connection_get_id (connection),
9562                                                  nm_connection_get_uuid (connection));
9563
9564                                         con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9565                                                                                     nm_connection_get_uuid (connection));
9566                                         g_weak_ref_set (&weak, con_tmp);
9567                                         refresh_remote_connection (&weak, &rem_con);
9568
9569                                         /* Replace local connection with the remote one to be sure they are equal.
9570                                          * This mitigates problems with plugins not preserving some properties or
9571                                          * adding ipv{4,6} settings when not present.
9572                                          */
9573                                         if (con_tmp) {
9574                                                 char *s_name = NULL;
9575                                                 if (menu_ctx.curr_setting)
9576                                                         s_name = g_strdup (nm_setting_get_name (menu_ctx.curr_setting));
9577
9578                                                 /* Update settings in the local connection */
9579                                                 nm_connection_replace_settings_from_connection (connection,
9580                                                                                                 NM_CONNECTION (con_tmp));
9581
9582                                                 /* Also update setting for menu context and TAB-completion */
9583                                                 menu_ctx.curr_setting = s_name ? nm_connection_get_setting_by_name (connection, s_name) : NULL;
9584                                                 nmc_tab_completion.setting = menu_ctx.curr_setting;
9585                                                 g_free (s_name);
9586                                         }
9587                                 }
9588
9589                                 nmc_editor_cb_called = FALSE;
9590                                 nmc_editor_error = NULL;
9591                                 g_mutex_unlock (&nmc_editor_mutex);
9592                         } else {
9593                                 g_print (_("Error: connection verification failed: %s\n"),
9594                                          err1 ? err1->message : _("(unknown error)"));
9595                                 g_print (_("You may try running 'verify fix' to fix errors.\n"));
9596                         }
9597
9598                         g_clear_error (&err1);
9599                         break;
9600
9601                 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
9602                         {
9603                         GError *tmp_err = NULL;
9604                         const char *ifname = cmd_arg_p;
9605                         const char *ap_nsp = cmd_arg_v;
9606
9607                         /* When only AP/NSP is specified it is prepended with '/' */
9608                         if (!cmd_arg_v) {
9609                                 if (ifname && ifname[0] == '/') {
9610                                         ap_nsp = ifname + 1;
9611                                         ifname = NULL;
9612                                 }
9613                         } else
9614                                 ap_nsp = ap_nsp && ap_nsp[0] == '/' ? ap_nsp + 1 : ap_nsp;
9615
9616                         if (is_connection_dirty (connection, rem_con)) {
9617                                 g_print (_("Error: connection is not saved. Type 'save' first.\n"));
9618                                 break;
9619                         }
9620                         if (!nm_connection_verify (NM_CONNECTION (rem_con), &tmp_err)) {
9621                                 g_print (_("Error: connection is not valid: %s\n"), tmp_err->message);
9622                                 g_clear_error (&tmp_err);
9623                                 break;
9624                         }
9625
9626                         nmc->nowait_flag = FALSE;
9627                         nmc->should_wait++;
9628                         nmc->print_output = NMC_PRINT_PRETTY;
9629                         if (!nmc_activate_connection (nmc, NM_CONNECTION (rem_con), ifname, ap_nsp, ap_nsp, NULL,
9630                                                       activate_connection_editor_cb, &tmp_err)) {
9631                                 g_print (_("Error: Cannot activate connection: %s.\n"), tmp_err->message);
9632                                 g_clear_error (&tmp_err);
9633                                 break;
9634                         }
9635
9636                         g_mutex_lock (&nmc_editor_mutex);
9637                         while (!nmc_editor_cb_called)
9638                                 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9639
9640                         if (nmc_editor_error) {
9641                                 g_print (_("Error: Failed to activate '%s' (%s) connection: %s\n"),
9642                                          nm_connection_get_id (connection),
9643                                          nm_connection_get_uuid (connection),
9644                                          nmc_editor_error->message);
9645                                 g_error_free (nmc_editor_error);
9646                         } else {
9647                                 g_print (_("Monitoring connection activation (press any key to continue)\n"));
9648                                 nmc_get_user_input ("");
9649                         }
9650
9651                         if (nmc_editor_monitor_ac) {
9652                                 if (nmc_editor_monitor_ac->monitor_id)
9653                                         g_source_remove (nmc_editor_monitor_ac->monitor_id);
9654                                 g_free (nmc_editor_monitor_ac);
9655                         }
9656                         nmc_editor_cb_called = FALSE;
9657                         nmc_editor_error = NULL;
9658                         nmc_editor_monitor_ac = NULL;
9659                         g_mutex_unlock (&nmc_editor_mutex);
9660
9661                         /* Update timestamp in local connection */
9662                         update_connection_timestamp (NM_CONNECTION (rem_con), connection);
9663
9664                         }
9665                         break;
9666
9667                 case NMC_EDITOR_MAIN_CMD_BACK:
9668                         /* Go back (up) an the menu */
9669                         if (menu_ctx.level == 1) {
9670                                 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9671                                 nmc_tab_completion.setting = NULL;  /* for TAB completion */
9672                         }
9673                         break;
9674
9675                 case NMC_EDITOR_MAIN_CMD_HELP:
9676                         /* Print command help */
9677                         editor_main_help (cmd_arg);
9678                         break;
9679
9680                 case NMC_EDITOR_MAIN_CMD_NMCLI:
9681                         if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) {
9682                                 GError *tmp_err = NULL;
9683                                 gboolean bb;
9684                                 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9685                                         g_print (_("Error: status-line: %s\n"), tmp_err->message);
9686                                         g_clear_error (&tmp_err);
9687                                 } else
9688                                         nmc->editor_status_line = bb;
9689                         } else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) {
9690                                 GError *tmp_err = NULL;
9691                                 gboolean bb;
9692                                 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9693                                         g_print (_("Error: save-confirmation: %s\n"), tmp_err->message);
9694                                         g_clear_error (&tmp_err);
9695                                 } else
9696                                         nmc->editor_save_confirmation = bb;
9697                         } else if (cmd_arg_p && matches (cmd_arg_p, "show-secrets") == 0) {
9698                                 GError *tmp_err = NULL;
9699                                 gboolean bb;
9700                                 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9701                                         g_print (_("Error: show-secrets: %s\n"), tmp_err->message);
9702                                         g_clear_error (&tmp_err);
9703                                 } else
9704                                         nmc->editor_show_secrets = bb;
9705                         } else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) {
9706                                 GError *tmp_err = NULL;
9707                                 NmcTermColor color;
9708                                 color = nmc_term_color_parse_string (cmd_arg_v ? g_strstrip (cmd_arg_v) : " ", &tmp_err);
9709                                 if (tmp_err) {
9710                                         g_print (_("Error: bad color: %s\n"), tmp_err->message);
9711                                         g_clear_error (&tmp_err);
9712                                 } else {
9713                                         nmc->editor_prompt_color = color;
9714                                         g_free (menu_ctx.main_prompt);
9715                                         if (menu_ctx.level == 0)
9716                                                 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9717                                                                                      BASE_PROMPT);
9718                                         else
9719                                                 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9720                                                                                      "nmcli %s> ",
9721                                                                                      nm_setting_get_name (menu_ctx.curr_setting));
9722                                 }
9723                         } else if (!cmd_arg_p) {
9724                                 g_print (_("Current nmcli configuration:\n"));
9725                                 g_print ("status-line: %s\n"
9726                                          "save-confirmation: %s\n"
9727                                          "show-secrets: %s\n"
9728                                          "prompt-color: %d\n",
9729                                          nmc->editor_status_line ? "yes" : "no",
9730                                          nmc->editor_save_confirmation ? "yes" : "no",
9731                                          nmc->editor_show_secrets ? "yes" : "no",
9732                                          nmc->editor_prompt_color);
9733                         } else
9734                                 g_print (_("Invalid configuration option '%s'; allowed [%s]\n"),
9735                                          cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, show-secrets, prompt-color");
9736
9737                         break;
9738
9739                 case NMC_EDITOR_MAIN_CMD_QUIT:
9740                         if (is_connection_dirty (connection, rem_con)) {
9741                                 if (confirm_quit ())
9742                                         cmd_loop = FALSE;  /* quit command loop */
9743                         } else
9744                                 cmd_loop = FALSE;  /* quit command loop */
9745                         break;
9746
9747                 case NMC_EDITOR_MAIN_CMD_UNKNOWN:
9748                 default:
9749                         g_print (_("Unknown command: '%s'\n"), cmd_user);
9750                         break;
9751                 }
9752
9753                 g_free (cmd_user);
9754                 g_free (cmd_arg);
9755                 g_free (cmd_arg_s);
9756                 g_free (cmd_arg_p);
9757                 g_free (cmd_arg_v);
9758         }
9759         g_free (valid_settings_str);
9760         g_free (menu_ctx.main_prompt);
9761         g_strfreev (menu_ctx.valid_props);
9762         g_free (menu_ctx.valid_props_str);
9763         if (rem_con)
9764                 g_object_unref (rem_con);
9765         g_weak_ref_clear (&weak);
9766
9767         /* Save history file */
9768         save_history_cmds (nm_connection_get_uuid (connection));
9769
9770         return TRUE;
9771 }
9772
9773 static const char *
9774 get_ethernet_device_name (NmCli *nmc)
9775 {
9776         const GPtrArray *devices;
9777         int i;
9778
9779         devices = nm_client_get_devices (nmc->client);
9780         for (i = 0; i < devices->len; i++) {
9781                 NMDevice *dev = g_ptr_array_index (devices, i);
9782                 if (NM_IS_DEVICE_ETHERNET (dev))
9783                         return nm_device_get_iface (dev);
9784         }
9785         return NULL;
9786 }
9787
9788 static void
9789 editor_init_new_connection (NmCli *nmc, NMConnection *connection)
9790 {
9791         NMSetting *setting, *base_setting;
9792         NMSettingConnection *s_con;
9793         const char *con_type;
9794         const char *slave_type = NULL;
9795
9796         s_con = nm_connection_get_setting_connection (connection);
9797         g_assert (s_con);
9798         con_type = nm_setting_connection_get_connection_type (s_con);
9799
9800         /* Initialize new connection according to its type using sensible defaults. */
9801
9802         nmc_setting_connection_connect_handlers (s_con, connection);
9803
9804         if (g_strcmp0 (con_type, "bond-slave") == 0)
9805                 slave_type = NM_SETTING_BOND_SETTING_NAME;
9806         if (g_strcmp0 (con_type, "team-slave") == 0)
9807                 slave_type = NM_SETTING_TEAM_SETTING_NAME;
9808         if (g_strcmp0 (con_type, "bridge-slave") == 0)
9809                 slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
9810
9811         if (slave_type) {
9812                 const char *dev_ifname = get_ethernet_device_name (nmc);
9813
9814                 /* For bond/team/bridge slaves add 'wired' setting */
9815                 setting = nm_setting_wired_new ();
9816                 nm_connection_add_setting (connection, setting);
9817
9818                 g_object_set (s_con,
9819                               NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
9820                               NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9821                               NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
9822                               NULL);
9823         } else {
9824                 /* Add a "base" setting to the connection by default */
9825                 base_setting = nmc_setting_new_for_name (con_type);
9826                 if (!base_setting)
9827                         return;
9828                 nm_connection_add_setting (connection, base_setting);
9829
9830                 /* Set a sensible bond/team/bridge interface name by default */
9831                 if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0)
9832                         g_object_set (s_con,
9833                                       NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bond",
9834                                       NULL);
9835                 if (g_strcmp0 (con_type, NM_SETTING_TEAM_SETTING_NAME) == 0)
9836                         g_object_set (s_con,
9837                                       NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-team",
9838                                       NULL);
9839                 if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0)
9840                         g_object_set (s_con,
9841                                       NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bridge",
9842                                       NULL);
9843
9844                 /* Set sensible initial VLAN values */
9845                 if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) {
9846                         const char *dev_ifname = get_ethernet_device_name (nmc);
9847
9848                         g_object_set (NM_SETTING_VLAN (base_setting),
9849                                       NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0",
9850                                       NM_SETTING_VLAN_ID, 1,
9851                                       NULL);
9852                         g_object_set (s_con,
9853                                       NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9854                                       NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME,
9855                                       NULL);
9856                 }
9857
9858                 /* Initialize 'transport-mode' so that 'infiniband' is valid */
9859                 if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0)
9860                         g_object_set (NM_SETTING_INFINIBAND (base_setting),
9861                                       NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
9862                                       NULL);
9863
9864                 /* Initialize 'number' so that 'cdma' is valid */
9865                 if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0)
9866                         g_object_set (NM_SETTING_CDMA (base_setting),
9867                                       NM_SETTING_CDMA_NUMBER, "#777",
9868                                       NULL);
9869
9870                 /* Initialize 'number' so that 'gsm' is valid */
9871                 if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0)
9872                         g_object_set (NM_SETTING_GSM (base_setting),
9873                                       NM_SETTING_GSM_NUMBER, "*99#",
9874                                       NULL);
9875
9876                 /* Wi-Fi */
9877                 if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0) {
9878                         /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL
9879                          * is regarded as "infrastructure", explicit value makes no doubts.
9880                          */
9881                         g_object_set (NM_SETTING_WIRELESS (base_setting),
9882                                       NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
9883                                       NULL);
9884
9885                         /* Do custom initialization for wifi setting */
9886                         nmc_setting_custom_init (base_setting);
9887                 }
9888
9889                 /* ADSL */
9890                 if (g_strcmp0 (con_type, NM_SETTING_ADSL_SETTING_NAME) == 0) {
9891                         /* Initialize a protocol */
9892                         g_object_set (NM_SETTING_ADSL (base_setting),
9893                                       NM_SETTING_ADSL_PROTOCOL, NM_SETTING_ADSL_PROTOCOL_PPPOE,
9894                                       NULL);
9895                 }
9896
9897                 /* Always add IPv4 and IPv6 settings for non-slave connections */
9898                 setting = nm_setting_ip4_config_new ();
9899                 nmc_setting_custom_init (setting);
9900                 nm_connection_add_setting (connection, setting);
9901
9902                 setting = nm_setting_ip6_config_new ();
9903                 nmc_setting_custom_init (setting);
9904                 nm_connection_add_setting (connection, setting);
9905         }
9906 }
9907
9908 static void
9909 editor_init_existing_connection (NMConnection *connection)
9910 {
9911         NMSettingIPConfig *s_ip4, *s_ip6;
9912         NMSettingWireless *s_wireless;
9913         NMSettingConnection *s_con;
9914
9915         s_ip4 = nm_connection_get_setting_ip4_config (connection);
9916         s_ip6 = nm_connection_get_setting_ip6_config (connection);
9917         s_wireless = nm_connection_get_setting_wireless (connection);
9918         s_con = nm_connection_get_setting_connection (connection);
9919
9920         if (s_ip4)
9921                 nmc_setting_ip4_connect_handlers (s_ip4);
9922         if (s_ip6)
9923                 nmc_setting_ip6_connect_handlers (s_ip6);
9924         if (s_wireless)
9925                 nmc_setting_wireless_connect_handlers (s_wireless);
9926         if (s_con)
9927                 nmc_setting_connection_connect_handlers (s_con, connection);
9928 }
9929
9930 static NMCResultCode
9931 do_connection_edit (NmCli *nmc, int argc, char **argv)
9932 {
9933         NMConnection *connection = NULL;
9934         NMSettingConnection *s_con;
9935         const char *connection_type;
9936         char *uuid;
9937         char *default_name = NULL;
9938         const char *type = NULL;
9939         char *type_ask = NULL;
9940         const char *con_name = NULL;
9941         const char *con = NULL;
9942         const char *con_id = NULL;
9943         const char *con_uuid = NULL;
9944         const char *con_path = NULL;
9945         const char *selector = NULL;
9946         char *tmp_str;
9947         GError *error = NULL;
9948         GError *err1 = NULL;
9949         nmc_arg_t exp_args[] = { {"type",     TRUE, &type,     FALSE},
9950                                  {"con-name", TRUE, &con_name, FALSE},
9951                                  {"id",       TRUE, &con_id,   FALSE},
9952                                  {"uuid",     TRUE, &con_uuid, FALSE},
9953                                  {"path",     TRUE, &con_path, FALSE},
9954                                  {NULL} };
9955
9956         nmc->return_value = NMC_RESULT_SUCCESS;
9957
9958         if (argc == 1)
9959                 con = *argv;
9960         else {
9961                 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) {
9962                         g_string_assign (nmc->return_text, error->message);
9963                         nmc->return_value = error->code;
9964                         g_clear_error (&error);
9965                         goto error;
9966                 }
9967         }
9968
9969         /* Setup some readline completion stuff */
9970         /* Set a pointer to an alternative function to create matches */
9971         rl_attempted_completion_function = (rl_completion_func_t *) nmcli_editor_tab_completion;
9972         /* Use ' ' and '.' as word break characters */
9973         rl_completer_word_break_characters = ". ";
9974
9975         if (!con) {
9976                 if (con_id && !con_uuid && !con_path) {
9977                         con = con_id;
9978                         selector = "id";
9979                 } else if (con_uuid && !con_id && !con_path) {
9980                         con = con_uuid;
9981                         selector = "uuid";
9982                 } else if (con_path && !con_id && !con_uuid) {
9983                         con = con_path;
9984                         selector = "path";
9985                 } else if (!con_path && !con_id && !con_uuid) {
9986                         /* no-op */
9987                 } else {
9988                         g_string_printf (nmc->return_text,
9989                                          _("Error: only one of 'id', uuid, or 'path' can be provided."));
9990                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
9991                         goto error;
9992                 }
9993         }
9994
9995         if (con) {
9996                 /* Existing connection */
9997                 NMConnection *found_con;
9998
9999                 found_con = nmc_find_connection (nmc->connections, selector, con, NULL);
10000                 if (!found_con) {
10001                         g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
10002                         nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10003                         goto error;
10004                 }
10005
10006                 /* Duplicate the connection and use that so that we need not
10007                  * differentiate existing vs. new later
10008                  */
10009                 connection = nm_simple_connection_new_clone (found_con);
10010
10011                 /* Merge secrets into the connection */
10012                 update_secrets_in_connection (NM_REMOTE_CONNECTION (found_con), connection);
10013
10014                 s_con = nm_connection_get_setting_connection (connection);
10015                 g_assert (s_con);
10016                 connection_type = nm_setting_connection_get_connection_type (s_con);
10017
10018                 if (type)
10019                         g_print (_("Warning: editing existing connection '%s'; 'type' argument is ignored\n"),
10020                                  nm_connection_get_id (connection));
10021                 if (con_name)
10022                         g_print (_("Warning: editing existing connection '%s'; 'con-name' argument is ignored\n"),
10023                                  nm_connection_get_id (connection));
10024
10025                 /* Load previously saved history commands for the connection */
10026                 load_history_cmds (nm_connection_get_uuid (connection));
10027
10028                 editor_init_existing_connection (connection);
10029         } else {
10030                 /* New connection */
10031                 connection_type = check_valid_name (type, nmc_valid_connection_types, NULL, &err1);
10032                 tmp_str = get_valid_options_string (nmc_valid_connection_types, NULL);
10033
10034                 while (!connection_type) {
10035                         if (!type)
10036                                 g_print (_("Valid connection types: %s\n"), tmp_str);
10037                         else
10038                                 g_print (_("Error: invalid connection type; %s\n"), err1->message);
10039                         g_clear_error (&err1);
10040
10041                         type_ask = nmc_readline (EDITOR_PROMPT_CON_TYPE);
10042                         type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10043                         connection_type = check_valid_name (type_ask, nmc_valid_connection_types, NULL, &err1);
10044                         g_free (type_ask);
10045                 }
10046                 g_free (tmp_str);
10047
10048                 /* Create a new connection object */
10049                 connection = nm_simple_connection_new ();
10050
10051                 /* Build up the 'connection' setting */
10052                 s_con = (NMSettingConnection *) nm_setting_connection_new ();
10053                 uuid = nm_utils_uuid_generate ();
10054                 if (con_name)
10055                         default_name = g_strdup (con_name);
10056                 else
10057                         default_name = nmc_unique_connection_name (nmc->connections,
10058                                                                    get_name_alias (connection_type, nmc_valid_connection_types));
10059
10060                 g_object_set (s_con,
10061                               NM_SETTING_CONNECTION_ID, default_name,
10062                               NM_SETTING_CONNECTION_UUID, uuid,
10063                               NM_SETTING_CONNECTION_TYPE, connection_type,
10064                               NULL);
10065                 g_free (uuid);
10066                 g_free (default_name);
10067                 nm_connection_add_setting (connection, NM_SETTING (s_con));
10068
10069                 /* Initialize the new connection so that it is valid from the start */
10070                 editor_init_new_connection (nmc, connection);
10071         }
10072
10073         /* nmcli runs the editor */
10074         nmc->in_editor = TRUE;
10075
10076         g_print ("\n");
10077         g_print (_("===| nmcli interactive connection editor |==="));
10078         g_print ("\n\n");
10079         if (con)
10080                 g_print (_("Editing existing '%s' connection: '%s'"), connection_type, con);
10081         else
10082                 g_print (_("Adding a new '%s' connection"), connection_type);
10083         g_print ("\n\n");
10084         g_print (_("Type 'help' or '?' for available commands."));
10085         g_print ("\n");
10086         g_print (_("Type 'describe [<setting>.<prop>]' for detailed property description."));
10087         g_print ("\n\n");
10088
10089         /* Set global variables for use in TAB completion */
10090         nmc_tab_completion.nmc = nmc;
10091         nmc_tab_completion.con_type = g_strdup (connection_type);
10092         nmc_tab_completion.connection = connection;
10093
10094         /* Run menu loop */
10095         editor_menu_main (nmc, connection, connection_type);
10096
10097         if (connection)
10098                 g_object_unref (connection);
10099         g_free (nmc_tab_completion.con_type);
10100
10101         nmc->should_wait++;
10102         return nmc->return_value;
10103
10104 error:
10105         g_assert (!connection);
10106         g_free (type_ask);
10107
10108         nmc->should_wait++;
10109         return nmc->return_value;
10110 }
10111
10112
10113 static void
10114 modify_connection_cb (GObject *connection,
10115                       GAsyncResult *result,
10116                       gpointer user_data)
10117 {
10118         NmCli *nmc = (NmCli *) user_data;
10119         GError *error = NULL;
10120
10121         if (!nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
10122                                                          result, &error)) {
10123                 g_string_printf (nmc->return_text,
10124                                  _("Error: Failed to modify connection '%s': %s"),
10125                                  nm_connection_get_id (NM_CONNECTION (connection)),
10126                                  error->message);
10127                 g_error_free (error);
10128                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10129         } else {
10130                 if (nmc->print_output == NMC_PRINT_PRETTY)
10131                         g_print (_("Connection '%s' (%s) successfully modified.\n"),
10132                                  nm_connection_get_id (NM_CONNECTION (connection)),
10133                                  nm_connection_get_uuid (NM_CONNECTION (connection)));
10134         }
10135         quit ();
10136 }
10137
10138 static NMCResultCode
10139 do_connection_modify (NmCli *nmc,
10140                       gboolean temporary,
10141                       int argc,
10142                       char **argv)
10143 {
10144         NMConnection *connection = NULL;
10145         NMRemoteConnection *rc = NULL;
10146         const char *name;
10147         const char *selector = NULL;
10148         GError *error = NULL;
10149
10150         nmc->return_value = NMC_RESULT_SUCCESS;
10151
10152         if (argc == 0) {
10153                 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10154                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10155                 goto finish;
10156         }
10157         if (   strcmp (*argv, "id") == 0
10158             || strcmp (*argv, "uuid") == 0
10159             || strcmp (*argv, "path") == 0) {
10160
10161                 selector = *argv;
10162                 if (next_arg (&argc, &argv) != 0) {
10163                         g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10164                                          selector);
10165                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10166                         goto finish;
10167                 }
10168                 name = *argv;
10169         }
10170         name = *argv;
10171         if (!name) {
10172                 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10173                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10174                 goto finish;
10175         }
10176         connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10177         if (!connection) {
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         rc = nm_client_get_connection_by_uuid (nmc->client,
10183                                                nm_connection_get_uuid (connection));
10184         if (!rc) {
10185                 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10186                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10187                 goto finish;
10188         }
10189
10190         if (next_arg (&argc, &argv) != 0) {
10191                 g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing."));
10192                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10193                 goto finish;
10194         }
10195
10196         if (!read_connection_properties (NM_CONNECTION (rc), argc, argv, &error)) {
10197                 g_string_assign (nmc->return_text, error->message);
10198                 nmc->return_value = error->code;
10199                 g_clear_error (&error);
10200                 goto finish;
10201         }
10202
10203         update_connection (!temporary, rc, modify_connection_cb, nmc);
10204
10205         nmc->should_wait++;
10206 finish:
10207         return nmc->return_value;
10208 }
10209
10210 typedef struct {
10211         NmCli *nmc;
10212         char *orig_id;
10213         char *orig_uuid;
10214         char *con_id;
10215 } CloneConnectionInfo;
10216
10217 static void
10218 clone_connection_cb (GObject *client,
10219                      GAsyncResult *result,
10220                      gpointer user_data)
10221 {
10222         CloneConnectionInfo *info = (CloneConnectionInfo *) user_data;
10223         NmCli *nmc = info->nmc;
10224         NMRemoteConnection *connection;
10225         GError *error = NULL;
10226
10227         connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
10228         if (error) {
10229                 g_string_printf (nmc->return_text,
10230                                  _("Error: Failed to add '%s' connection: %s"),
10231                                  info->con_id, error->message);
10232                 g_error_free (error);
10233                 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
10234         } else {
10235                 g_print (_("%s (%s) cloned as %s (%s).\n"),
10236                          info->orig_id,
10237                          info->orig_uuid,
10238                          nm_connection_get_id (NM_CONNECTION (connection)),
10239                          nm_connection_get_uuid (NM_CONNECTION (connection)));
10240                 g_object_unref (connection);
10241         }
10242
10243         g_free (info->con_id);
10244         g_free (info->orig_id);
10245         g_free (info->orig_uuid);
10246         g_slice_free (CloneConnectionInfo, info);
10247         quit ();
10248 }
10249
10250 static NMCResultCode
10251 do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv)
10252 {
10253         NMConnection *connection = NULL;
10254         NMConnection *new_connection = NULL;
10255         NMSettingConnection *s_con;
10256         CloneConnectionInfo *info;
10257         const char *name;
10258         const char *new_name;
10259         char *name_ask = NULL;
10260         char *new_name_ask = NULL;
10261         const char *selector = NULL;
10262         char *uuid;
10263
10264         if (argc == 0) {
10265                 if (nmc->ask) {
10266                         name = name_ask = nmc_readline (PROMPT_CONNECTION);
10267                         new_name = new_name_ask = nmc_readline (_("New connection name: "));
10268                 } else {
10269                         g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10270                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10271                         goto finish;
10272                 }
10273         } else {
10274                 if (   strcmp (*argv, "id") == 0
10275                     || strcmp (*argv, "uuid") == 0
10276                     || strcmp (*argv, "path") == 0) {
10277
10278                         selector = *argv;
10279                         if (next_arg (&argc, &argv) != 0) {
10280                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10281                                                  selector);
10282                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10283                                 goto finish;
10284                         }
10285                 }
10286                 name = *argv;
10287                 if (next_arg (&argc, &argv) != 0) {
10288                         g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10289                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10290                         goto finish;
10291                 }
10292                 new_name = *argv;
10293                 if (next_arg (&argc, &argv) == 0) {
10294                         g_string_printf (nmc->return_text, _("Error: unexpected extra argument '%s'."), *argv);
10295                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10296                         goto finish;
10297                 }
10298         }
10299
10300         if (!name) {
10301                 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10302                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10303                 goto finish;
10304         }
10305         if (!new_name) {
10306                 g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10307                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10308                 goto finish;
10309         }
10310
10311         connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10312         if (!connection) {
10313                 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10314                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10315                 goto finish;
10316         }
10317
10318         /* Copy the connection */
10319         new_connection = nm_simple_connection_new_clone (connection);
10320
10321         s_con = nm_connection_get_setting_connection (new_connection);
10322         g_assert (s_con);
10323         uuid = nm_utils_uuid_generate ();
10324         g_object_set (s_con,
10325                       NM_SETTING_CONNECTION_ID, new_name,
10326                       NM_SETTING_CONNECTION_UUID, uuid,
10327                       NULL);
10328         g_free (uuid);
10329
10330         /* Merge secrets into the new connection */
10331         update_secrets_in_connection (NM_REMOTE_CONNECTION (connection), new_connection);
10332
10333         info = g_slice_new0 (CloneConnectionInfo);
10334         info->nmc = nmc;
10335         info->orig_id = g_strdup (nm_connection_get_id (connection));
10336         info->orig_uuid = g_strdup (nm_connection_get_uuid (connection));
10337         info->con_id = g_strdup (nm_connection_get_id (new_connection));
10338
10339         /* Add the new cloned connection to NetworkManager */
10340         add_new_connection (!temporary,
10341                             nmc->client,
10342                             new_connection,
10343                             clone_connection_cb,
10344                             info);
10345
10346         nmc->should_wait = TRUE;
10347 finish:
10348         if (new_connection)
10349                 g_object_unref (new_connection);
10350         g_free (name_ask);
10351         g_free (new_name_ask);
10352
10353         return nmc->return_value;
10354 }
10355
10356 static void
10357 delete_cb (GObject *con, GAsyncResult *result, gpointer user_data)
10358 {
10359         ConnectionCbInfo *info = (ConnectionCbInfo *) user_data;
10360         GError *error = NULL;
10361
10362         if (!nm_remote_connection_delete_finish (NM_REMOTE_CONNECTION (con), result, &error)) {
10363                 g_string_printf (info->nmc->return_text, _("Error: not all connections deleted."));
10364                 g_printerr (_("Error: Connection deletion failed: %s"),
10365                             error->message);
10366                 g_error_free (error);
10367                 info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL;
10368                 connection_cb_info_finish (info, con);
10369         } else {
10370                 if (info->nmc->nowait_flag)
10371                         connection_cb_info_finish (info, con);
10372         }
10373 }
10374
10375 static NMCResultCode
10376 do_connection_delete (NmCli *nmc, int argc, char **argv)
10377 {
10378         NMConnection *connection;
10379         ConnectionCbInfo *info = NULL;
10380         GSList *queue = NULL, *iter;
10381         char **arg_arr = NULL;
10382         char **arg_ptr = argv;
10383         int arg_num = argc;
10384         GString *invalid_cons = NULL;
10385         int pos = 0;
10386
10387         if (nmc->timeout == -1)
10388                 nmc->timeout = 10;
10389
10390         if (argc == 0) {
10391                 if (nmc->ask) {
10392                         char *line = nmc_readline (PROMPT_CONNECTIONS);
10393                         nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
10394                         g_free (line);
10395                         arg_ptr = arg_arr;
10396                 }
10397                 if (arg_num == 0) {
10398                         g_string_printf (nmc->return_text, _("Error: No connection specified."));
10399                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10400                         goto finish;
10401                 }
10402         }
10403
10404         while (arg_num > 0) {
10405                 const char *selector = NULL;
10406
10407                 if (   strcmp (*arg_ptr, "id") == 0
10408                     || strcmp (*arg_ptr, "uuid") == 0
10409                     || strcmp (*arg_ptr, "path") == 0) {
10410                         selector = *arg_ptr;
10411                         if (next_arg (&arg_num, &arg_ptr) != 0) {
10412                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10413                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10414                                 goto finish;
10415                         }
10416                 }
10417
10418                 connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10419                 if (connection) {
10420                         /* Check if the connection is unique. */
10421                         /* Calling delete for the same connection repeatedly would result in
10422                          * NM responding for the last D-Bus call only and we would stall. */
10423                         if (!g_slist_find (queue, connection))
10424                                 queue = g_slist_prepend (queue, g_object_ref (connection));
10425                 } else {
10426                         g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10427                         g_string_printf (nmc->return_text, _("Error: not all active connections found."));
10428                         nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10429                         if (!invalid_cons)
10430                                 invalid_cons = g_string_new (NULL);
10431                         g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr);
10432                 }
10433
10434                 /* Take next argument (if there's no other connection of the same name) */
10435                 if (!pos)
10436                         next_arg (&arg_num, &arg_ptr);
10437         }
10438
10439         if (!queue) {
10440                 g_string_printf (nmc->return_text, _("Error: no connection provided."));
10441                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10442                 goto finish;
10443         }
10444         queue = g_slist_reverse (queue);
10445
10446         info = g_slice_new0 (ConnectionCbInfo);
10447         info->nmc = nmc;
10448         info->queue = queue;
10449         info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
10450
10451         nmc->nowait_flag = (nmc->timeout == 0);
10452         nmc->should_wait++;
10453
10454         g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED,
10455                           G_CALLBACK (connection_removed_cb), info);
10456
10457         /* Now delete the connections */
10458         for (iter = queue; iter; iter = g_slist_next (iter))
10459                 nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (iter->data),
10460                                                    NULL, delete_cb, info);
10461
10462 finish:
10463         if (invalid_cons) {
10464                 g_string_truncate (invalid_cons, invalid_cons->len-2);  /* truncate trailing ", " */
10465                 g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."),
10466                                  invalid_cons->str);
10467                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10468                 g_string_free (invalid_cons, TRUE);
10469         }
10470         g_strfreev (arg_arr);
10471         return nmc->return_value;
10472 }
10473
10474 static void
10475 connection_changed (NMConnection *connection, NmCli *nmc)
10476 {
10477         g_print (_("%s: connection profile changed\n"), nm_connection_get_id (connection));
10478 }
10479
10480 static void
10481 connection_watch (NmCli *nmc, NMConnection *connection)
10482 {
10483         nmc->should_wait++;
10484         g_signal_connect (connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed), nmc);
10485 }
10486
10487 static void
10488 connection_unwatch (NmCli *nmc, NMConnection *connection)
10489 {
10490         if (g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_changed), nmc))
10491                 nmc->should_wait--;
10492
10493         /* Terminate if all the watched connections disappeared. */
10494         if (!nmc->should_wait)
10495                 quit ();
10496 }
10497
10498 static void
10499 connection_added (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10500 {
10501         NMConnection *connection = NM_CONNECTION (con);
10502
10503         g_print (_("%s: connection profile created\n"), nm_connection_get_id (connection));
10504         connection_watch (nmc, connection);
10505 }
10506
10507 static void
10508 connection_removed (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10509 {
10510         NMConnection *connection = NM_CONNECTION (con);
10511
10512         g_print (_("%s: connection profile removed\n"), nm_connection_get_id (connection));
10513         connection_unwatch (nmc, connection);
10514 }
10515
10516 static NMCResultCode
10517 do_connection_monitor (NmCli *nmc, int argc, char **argv)
10518 {
10519         if (argc == 0) {
10520                 /* No connections specified. Monitor all. */
10521                 int i;
10522
10523                 nmc->connections = nm_client_get_connections (nmc->client);
10524                 for (i = 0; i < nmc->connections->len; i++)
10525                         connection_watch (nmc, g_ptr_array_index (nmc->connections, i));
10526
10527                 /* We'll watch the connection additions too, never exit. */
10528                 nmc->should_wait++;
10529                 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_ADDED, G_CALLBACK (connection_added), nmc);
10530         } else {
10531                 /* Look up the specified connections and watch them. */
10532                 NMConnection *connection;
10533                 char **arg_ptr = argv;
10534                 int arg_num = argc;
10535                 int pos = 0;
10536
10537                 do {
10538                         const char *selector = NULL;
10539
10540                         if (   strcmp (*arg_ptr, "id") == 0
10541                             || strcmp (*arg_ptr, "uuid") == 0
10542                             || strcmp (*arg_ptr, "path") == 0) {
10543                                 selector = *arg_ptr;
10544                                 if (next_arg (&arg_num, &arg_ptr) != 0) {
10545                                         g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10546                                         return NMC_RESULT_ERROR_USER_INPUT;
10547                                 }
10548                         }
10549
10550                         connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10551                         if (connection) {
10552                                 connection_watch (nmc, connection);
10553                         } else {
10554                                 g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10555                                 g_string_printf (nmc->return_text, _("Error: not all connections found."));
10556                                 return NMC_RESULT_ERROR_NOT_FOUND;
10557                         }
10558
10559                         /* Take next argument (if there's no other connection of the same name) */
10560                         if (!pos)
10561                                 next_arg (&arg_num, &arg_ptr);
10562                 } while (arg_num > 0);
10563         }
10564
10565         g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED, G_CALLBACK (connection_removed), nmc);
10566
10567         return NMC_RESULT_SUCCESS;
10568 }
10569
10570 static NMCResultCode
10571 do_connection_reload (NmCli *nmc, int argc, char **argv)
10572 {
10573         GError *error = NULL;
10574
10575         nmc->return_value = NMC_RESULT_SUCCESS;
10576
10577         if (!nm_client_reload_connections (nmc->client, NULL, &error)) {
10578                 g_string_printf (nmc->return_text, _("Error: failed to reload connections: %s."),
10579                                  error->message);
10580                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10581                 g_clear_error (&error);
10582         }
10583
10584         return nmc->return_value;
10585 }
10586
10587 static NMCResultCode
10588 do_connection_load (NmCli *nmc, int argc, char **argv)
10589 {
10590         GError *error = NULL;
10591         char **filenames, **failures = NULL;
10592         int i;
10593
10594         nmc->return_value = NMC_RESULT_SUCCESS;
10595
10596         if (argc == 0) {
10597                 g_string_printf (nmc->return_text, _("Error: No connection specified."));
10598                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10599                 return nmc->return_value;
10600         }
10601
10602         filenames = g_new (char *, argc + 1);
10603         for (i = 0; i < argc; i++)
10604                 filenames[i] = argv[i];
10605         filenames[i] = NULL;
10606
10607         nm_client_load_connections (nmc->client, filenames, &failures, NULL, &error);
10608         g_free (filenames);
10609         if (error) {
10610                 g_string_printf (nmc->return_text, _("Error: failed to load connection: %s."),
10611                                  error->message);
10612                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10613                 g_error_free (error);
10614         }
10615
10616         if (failures) {
10617                 for (i = 0; failures[i]; i++)
10618                         g_printerr (_("Could not load file '%s'\n"), failures[i]);
10619                 g_strfreev (failures);
10620         }
10621
10622         return nmc->return_value;
10623 }
10624
10625 // FIXME: change the text when non-VPN connection types are supported
10626 #define PROMPT_IMPORT_TYPE  PROMPT_VPN_TYPE
10627 #define PROMPT_IMPORT_FILE _("File to import: ")
10628
10629 static NMCResultCode
10630 do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv)
10631 {
10632         GError *error = NULL;
10633         const char *type = NULL, *filename = NULL;
10634         char *type_ask = NULL, *filename_ask = NULL;
10635         AddConnectionInfo *info;
10636         NMConnection *connection = NULL;
10637         NMVpnEditorPlugin *plugin;
10638
10639         if (argc == 0) {
10640                 if (nmc->ask) {
10641                         type_ask = nmc_readline (PROMPT_IMPORT_TYPE);
10642                         filename_ask = nmc_readline (PROMPT_IMPORT_FILE);
10643                         type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10644                         filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL;
10645                 } else {
10646                         g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10647                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10648                         goto finish;
10649                 }
10650         }
10651
10652         while (argc > 0) {
10653                 if (strcmp (*argv, "type") == 0) {
10654                         if (next_arg (&argc, &argv) != 0) {
10655                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10656                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10657                                 goto finish;
10658                         }
10659                         if (!type)
10660                                 type = *argv;
10661                         else
10662                                 g_printerr (_("Warning: 'type' already specified, ignoring extra one.\n"));
10663
10664                 } else if (strcmp (*argv, "file") == 0) {
10665                         if (next_arg (&argc, &argv) != 0) {
10666                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10667                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10668                                 goto finish;
10669                         }
10670                         if (!filename)
10671                                 filename = *argv;
10672                         else
10673                                 g_printerr (_("Warning: 'file' already specified, ignoring extra one.\n"));
10674                 } else {
10675                         g_string_printf (nmc->return_text, _("Unknown parameter: %s"), *argv);
10676                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10677                         goto finish;
10678                 }
10679
10680                 argc--;
10681                 argv++;
10682         }
10683
10684         if (!type) {
10685                 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
10686                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10687                 goto finish;
10688         }
10689         if (!filename) {
10690                 g_string_printf (nmc->return_text, _("Error: 'file' argument is required."));
10691                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10692                 goto finish;
10693         }
10694
10695         /* Import VPN configuration */
10696         plugin = nm_vpn_get_plugin_by_service (type, &error);
10697         if (!plugin) {
10698                 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10699                                  error->message);
10700                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10701                 goto finish;
10702         }
10703
10704         connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
10705         if (!connection) {
10706                 g_string_printf (nmc->return_text, _("Error: failed to import '%s': %s."),
10707                                  filename, error->message);
10708                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10709                 goto finish;
10710         }
10711
10712         info = g_malloc0 (sizeof (AddConnectionInfo));
10713         info->nmc = nmc;
10714         info->con_name = g_strdup (nm_connection_get_id (connection));
10715
10716         /* Add the new imported connection to NetworkManager */
10717         add_new_connection (!temporary,
10718                             nmc->client,
10719                             connection,
10720                             add_connection_cb,
10721                             info);
10722
10723         nmc->should_wait = TRUE;
10724 finish:
10725         if (connection)
10726                 g_object_unref (connection);
10727         g_clear_error (&error);
10728         g_free (type_ask);
10729         g_free (filename_ask);
10730         return nmc->return_value;
10731 }
10732
10733 static NMCResultCode
10734 do_connection_export (NmCli *nmc, int argc, char **argv)
10735 {
10736         NMConnection *connection = NULL;
10737         const char *name;
10738         const char *out_name = NULL;
10739         char *name_ask = NULL;
10740         char *out_name_ask = NULL;
10741         const char *path = NULL;
10742         const char *selector = NULL;
10743         const char *type = NULL;
10744         NMVpnEditorPlugin *plugin;
10745         GError *error = NULL;
10746         char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX";
10747
10748         if (argc == 0) {
10749                 if (nmc->ask) {
10750                         name_ask = nmc_readline (PROMPT_VPN_CONNECTION);
10751                         name = name_ask = name_ask ? g_strstrip (name_ask) : NULL;
10752                         out_name = out_name_ask = nmc_readline (_("Output file name: "));
10753                 } else {
10754                         g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10755                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10756                         goto finish;
10757                 }
10758         } else {
10759                 if (   strcmp (*argv, "id") == 0
10760                     || strcmp (*argv, "uuid") == 0
10761                     || strcmp (*argv, "path") == 0) {
10762
10763                         selector = *argv;
10764                         if (next_arg (&argc, &argv) != 0) {
10765                                 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10766                                                  selector);
10767                                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10768                                 goto finish;
10769                         }
10770                 }
10771                 name = *argv;
10772                 if (next_arg (&argc, &argv) == 0)
10773                         out_name = *argv;
10774
10775                 if (next_arg (&argc, &argv) == 0) {
10776                         g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv);
10777                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10778                         goto finish;
10779                 }
10780         }
10781
10782         if (!name) {
10783                 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10784                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10785                 goto finish;
10786         }
10787         connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10788         if (!connection) {
10789                 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10790                 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10791                 goto finish;
10792         }
10793
10794         type = nm_connection_get_connection_type (connection);
10795         if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) != 0) {
10796                 g_string_printf (nmc->return_text, _("Error: the connection is not VPN."));
10797                 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10798                 goto finish;
10799         }
10800         type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
10801
10802         /* Export VPN configuration */
10803         plugin = nm_vpn_get_plugin_by_service (type, &error);
10804         if (!plugin) {
10805                 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10806                                  error->message);
10807                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10808                 goto finish;
10809         }
10810
10811         if (out_name)
10812                 path = out_name;
10813         else {
10814                 int fd;
10815                 fd = g_mkstemp (tmpfile);
10816                 if (fd == -1) {
10817                         g_string_printf (nmc->return_text, _("Error: failed to create temporary file %s."), tmpfile);
10818                         nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10819                         goto finish;
10820                 }
10821                 close (fd);
10822                 path = tmpfile;
10823         }
10824
10825         if (!nm_vpn_editor_plugin_export (plugin, path, connection, &error)) {
10826                 g_string_printf (nmc->return_text, _("Error: failed to export '%s': %s."),
10827                                  nm_connection_get_id (connection), error->message);
10828                 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10829                 goto finish;
10830         }
10831
10832         /* No output file -> copy data to stdout */
10833         if (!out_name) {
10834                 char *contents = NULL;
10835                 gsize len = 0;
10836                 if (!g_file_get_contents (path, &contents, &len, &error)) {
10837                         g_string_printf (nmc->return_text, _("Error: failed to read temporary file '%s': %s."),
10838                                          path, error->message);
10839                         nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10840                         goto finish;
10841                 }
10842                 g_print ("%s", contents);
10843                 g_free (contents);
10844         }
10845
10846 finish:
10847         if (!out_name && path)
10848                 unlink (path);
10849         g_clear_error (&error);
10850         g_free (name_ask);
10851         g_free (out_name_ask);
10852         return nmc->return_value;
10853 }
10854
10855
10856 typedef struct {
10857         NmCli *nmc;
10858         int argc;
10859         char **argv;
10860 } NmcEditorThreadData;
10861
10862 static GThread *editor_thread;
10863 static NmcEditorThreadData editor_thread_data;
10864
10865 /*
10866  * We need to run do_connection_edit() in a thread so that
10867  * glib main loop is not blocked and could receive and process D-Bus
10868  * return messages.
10869  */
10870 static gpointer
10871 connection_editor_thread_func (gpointer data)
10872 {
10873         NmcEditorThreadData *td = (NmcEditorThreadData *) data;
10874
10875         /* run editor for editing/adding connections */
10876         td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv);
10877
10878         /* quit glib main loop now that we are done with this thread */
10879         quit ();
10880
10881         return NULL;
10882 }
10883
10884 static char *
10885 gen_func_connection_names (const char *text, int state)
10886 {
10887         int i;
10888         const char **connections;
10889         char *ret;
10890
10891         if (nm_cli.connections->len == 0)
10892                 return NULL;
10893
10894         connections = g_new (const char *, nm_cli.connections->len + 1);
10895         for (i = 0; i < nm_cli.connections->len; i++) {
10896                 NMConnection *con = NM_CONNECTION (nm_cli.connections->pdata[i]);
10897                 const char *id = nm_connection_get_id (con);
10898                 connections[i] = id;
10899         }
10900         connections[i] = NULL;
10901
10902         ret = nmc_rl_gen_func_basic (text, state, connections);
10903
10904         g_free (connections);
10905         return ret;
10906 }
10907
10908 static char *
10909 gen_func_active_connection_names (const char *text, int state)
10910 {
10911         int i;
10912         const GPtrArray *acs;
10913         const char **connections;
10914         char *ret;
10915
10916         if (!nm_cli.client)
10917                 return NULL;
10918
10919         acs = nm_client_get_active_connections (nm_cli.client);
10920         if (!acs || acs->len == 0)
10921                 return NULL;
10922
10923         connections = g_new (const char *, acs->len + 1);
10924         for (i = 0; i < acs->len; i++)
10925                 connections[i] = nm_active_connection_get_id (acs->pdata[i]);
10926         connections[i] = NULL;
10927
10928         ret = nmc_rl_gen_func_basic (text, state, connections);
10929
10930         g_free (connections);
10931         return ret;
10932 }
10933
10934 static char **
10935 nmcli_con_tab_completion (const char *text, int start, int end)
10936 {
10937         char **match_array = NULL;
10938         rl_compentry_func_t *generator_func = NULL;
10939
10940         /* Disable readline's default filename completion */
10941         rl_attempted_completion_over = 1;
10942
10943         if (g_strcmp0 (rl_prompt, PROMPT_CONNECTION) == 0) {
10944                 /* Disable appending space after completion */
10945                 rl_completion_append_character = '\0';
10946
10947                 if (!is_single_word (rl_line_buffer))
10948                         return NULL;
10949
10950                 generator_func = gen_func_connection_names;
10951         } else if (g_strcmp0 (rl_prompt, PROMPT_CONNECTIONS) == 0) {
10952                 generator_func = gen_func_connection_names;
10953         } else if (g_strcmp0 (rl_prompt, PROMPT_ACTIVE_CONNECTIONS) == 0) {
10954                 generator_func = gen_func_active_connection_names;
10955         } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_TYPE) == 0) {
10956                 generator_func = gen_func_vpn_types;
10957         } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) {
10958                 rl_attempted_completion_over = 0;
10959                 rl_complete_with_tilde_expansion = 1;
10960         } else if (g_strcmp0 (rl_prompt, PROMPT_VPN_CONNECTION) == 0) {
10961                 generator_func = gen_vpn_ids;
10962         }
10963
10964         if (generator_func)
10965                 match_array = rl_completion_matches (text, generator_func);
10966
10967         return match_array;
10968 }
10969
10970 static GArray *
10971 parse_preferred_connection_order (const char *order, GError **error)
10972 {
10973         char **strv, **iter;
10974         const char *str;
10975         GArray *order_arr;
10976         NmcSortOrder val;
10977         gboolean inverse, unique;
10978         int i;
10979
10980         strv = nmc_strsplit_set (order, ":", -1);
10981         if (!strv || !*strv) {
10982                 g_set_error (error, NMCLI_ERROR, 0,
10983                              _("incorrect string '%s' of '--order' option"), order);
10984                 g_strfreev (strv);
10985                 return NULL;
10986         }
10987
10988         order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4);
10989         for (iter = strv; iter && *iter; iter++) {
10990                 str = *iter;
10991                 inverse = FALSE;
10992                 if (str[0] == '-')
10993                         inverse = TRUE;
10994                 if (str[0] == '+' || str[0] == '-')
10995                         str++;
10996
10997                 if (matches (str, "active") == 0)
10998                         val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE;
10999                 else if (matches (str, "name") == 0)
11000                         val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME;
11001                 else if (matches (str, "type") == 0)
11002                         val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE;
11003                 else if (matches (str, "path") == 0)
11004                         val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH;
11005                 else {
11006                         g_array_unref (order_arr);
11007                         order_arr = NULL;
11008                         g_set_error (error, NMCLI_ERROR, 0,
11009                                      _("incorrect item '%s' in '--order' option"), *iter);
11010                         break;
11011                 }
11012                 /* Check for duplicates and ignore them. */
11013                 unique = TRUE;
11014                 for (i = 0; i < order_arr->len; i++) {
11015                         if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) {
11016                                 unique = FALSE;
11017                                 break;
11018                         }
11019                 }
11020
11021                 /* Value is ok and unique, add it to the array */
11022                 if (unique)
11023                         g_array_append_val (order_arr, val);
11024         }
11025
11026         g_strfreev (strv);
11027         return order_arr;
11028 }
11029
11030 /* Entry point function for connections-related commands: 'nmcli connection' */
11031 NMCResultCode
11032 do_connections (NmCli *nmc, int argc, char **argv)
11033 {
11034         GError *error = NULL;
11035
11036         /* Register polkit agent */
11037         nmc_start_polkit_agent_start_try (nmc);
11038
11039         /* Set completion function for 'nmcli con' */
11040         rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion;
11041
11042         /* Exit early on help */
11043         if (nmc_arg_is_help (*argv)) {
11044                 usage ();
11045                 return nmc->return_value;
11046         }
11047         if (argc != 0 && nmc_arg_is_help (*(argv+1))) {
11048                 if (usage_connection_second_level (*argv))
11049                         return nmc->return_value;
11050         }
11051
11052         /* Get NMClient object early */
11053         nmc->get_client (nmc);
11054
11055         /* Check whether NetworkManager is running */
11056         if (!nm_client_get_nm_running (nmc->client)) {
11057                 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
11058                 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
11059                 return nmc->return_value;
11060         }
11061         /* Compare NM and nmcli versions */
11062         if (!nmc_versions_match (nmc))
11063                 return nmc->return_value;
11064
11065         /* Get the connection list */
11066         nmc->connections = nm_client_get_connections (nmc->client);
11067
11068         /* Now parse the command line and perform the required operation */
11069         if (argc == 0) {
11070                 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
11071                         goto opt_error;
11072                 nmc->return_value = do_connections_show (nmc, FALSE, FALSE, NULL, argc, argv);
11073         } else {
11074                 if (matches (*argv, "show") == 0) {
11075                         gboolean active = FALSE;
11076                         gboolean show_secrets = FALSE;
11077                         GArray *order = NULL;
11078                         int i;
11079
11080                         next_arg (&argc, &argv);
11081                         /* check connection show options [--active] [--show-secrets] */
11082                         for (i = 0; i < 3; i++) {
11083                                 if (!active && nmc_arg_is_option (*argv, "active")) {
11084                                         active = TRUE;
11085                                         next_arg (&argc, &argv);
11086                                 }
11087                                 /* --show-secrets is deprecated in favour of global --show-secrets */
11088                                 /* Keep it here for backwards compatibility */
11089                                 if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) {
11090                                         show_secrets = TRUE;
11091                                         next_arg (&argc, &argv);
11092                                 }
11093                                 if (!order && nmc_arg_is_option (*argv, "order")) {
11094                                         if (next_arg (&argc, &argv) != 0) {
11095                                                 g_set_error_literal (&error, NMCLI_ERROR, 0,
11096                                                                      _("'--order' argument is missing"));
11097                                                 goto opt_error;
11098                                         }
11099                                         order = parse_preferred_connection_order (*argv, &error);
11100                                         if (error)
11101                                                 goto opt_error;
11102                                         next_arg (&argc, &argv);
11103                                 }
11104                         }
11105                         show_secrets = nmc->show_secrets || show_secrets;
11106                         nmc->return_value = do_connections_show (nmc, active, show_secrets, order, argc, argv);
11107                         if (order)
11108                                 g_array_unref (order);
11109                 } else if (matches(*argv, "up") == 0) {
11110                         nmc->return_value = do_connection_up (nmc, argc-1, argv+1);
11111                 } else if (matches(*argv, "down") == 0) {
11112                         nmc->return_value = do_connection_down (nmc, argc-1, argv+1);
11113                 } else if (matches(*argv, "add") == 0) {
11114                         nmc->return_value = do_connection_add (nmc, argc-1, argv+1);
11115                 } else if (matches(*argv, "edit") == 0) {
11116                         nmc->should_wait++;
11117                         editor_thread_data.nmc = nmc;
11118                         editor_thread_data.argc = argc - 1;
11119                         editor_thread_data.argv = argv + 1;
11120                         editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data);
11121                         g_thread_unref (editor_thread);
11122                 } else if (matches(*argv, "delete") == 0) {
11123                         nmc->return_value = do_connection_delete (nmc, argc-1, argv+1);
11124                 } else if (matches(*argv, "reload") == 0) {
11125                         nmc->return_value = do_connection_reload (nmc, argc-1, argv+1);
11126                 } else if (matches(*argv, "load") == 0) {
11127                         nmc->return_value = do_connection_load (nmc, argc-1, argv+1);
11128                 } else if (matches (*argv, "modify") == 0) {
11129                         gboolean temporary = FALSE;
11130
11131                         next_arg (&argc, &argv);
11132                         if (nmc_arg_is_option (*argv, "temporary")) {
11133                                 temporary = TRUE;
11134                                 next_arg (&argc, &argv);
11135                         }
11136                         nmc->return_value = do_connection_modify (nmc, temporary, argc, argv);
11137                 } else if (matches (*argv, "clone") == 0) {
11138                         gboolean temporary = FALSE;
11139
11140                         next_arg (&argc, &argv);
11141                         if (nmc_arg_is_option (*argv, "temporary")) {
11142                                 temporary = TRUE;
11143                                 next_arg (&argc, &argv);
11144                         }
11145                         nmc->return_value = do_connection_clone (nmc, temporary, argc, argv);
11146                 } else if (matches(*argv, "import") == 0) {
11147                         gboolean temporary = FALSE;
11148
11149                         next_arg (&argc, &argv);
11150                         if (nmc_arg_is_option (*argv, "temporary")) {
11151                                 temporary = TRUE;
11152                                 next_arg (&argc, &argv);
11153                         }
11154                         nmc->return_value = do_connection_import (nmc, temporary, argc, argv);
11155                 } else if (matches(*argv, "export") == 0) {
11156                         nmc->return_value = do_connection_export (nmc, argc-1, argv+1);
11157                 } else if (matches(*argv, "monitor") == 0) {
11158                         nmc->return_value = do_connection_monitor (nmc, argc-1, argv+1);
11159                 } else {
11160                         usage ();
11161                         g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
11162                         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11163                 }
11164         }
11165
11166         return nmc->return_value;
11167
11168 opt_error:
11169         g_string_printf (nmc->return_text, _("Error: %s."), error->message);
11170         nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11171         g_error_free (error);
11172         return nmc->return_value;
11173 }
11174
11175 void
11176 monitor_connections (NmCli *nmc)
11177 {
11178         do_connection_monitor (nmc, 0, NULL);
11179 }