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