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