Base Code merged to SPIN 2.4
[platform/upstream/connman.git] / client / commands.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <stdbool.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <ctype.h>
35
36 #include <glib.h>
37 #include <gdbus.h>
38
39 #include "dbus_helpers.h"
40 #include "input.h"
41 #include "services.h"
42 #include "peers.h"
43 #include "commands.h"
44 #include "agent.h"
45 #include "vpnconnections.h"
46
47 static DBusConnection *connection;
48 static GHashTable *service_hash;
49 static GHashTable *peer_hash;
50 static GHashTable *technology_hash;
51 static char *session_notify_path;
52 static char *session_path;
53 static bool session_connected;
54
55 struct connman_option {
56         const char *name;
57         const char val;
58         const char *desc;
59 };
60
61 static char *ipv4[] = {
62         "Method",
63         "Address",
64         "Netmask",
65         "Gateway",
66         NULL
67 };
68
69 static char *ipv6[] = {
70         "Method",
71         "Address",
72         "PrefixLength",
73         "Gateway",
74         NULL
75 };
76
77 static int cmd_help(char *args[], int num, struct connman_option *options);
78
79 static bool check_dbus_name(const char *name)
80 {
81         /*
82          * Valid dbus chars should be [A-Z][a-z][0-9]_
83          * and should not start with number.
84          */
85         unsigned int i;
86
87         if (!name || name[0] == '\0')
88                 return false;
89
90         for (i = 0; name[i] != '\0'; i++)
91                 if (!((name[i] >= 'A' && name[i] <= 'Z') ||
92                                 (name[i] >= 'a' && name[i] <= 'z') ||
93                                 (name[i] >= '0' && name[i] <= '9') ||
94                                 name[i] == '_'))
95                         return false;
96
97         return true;
98 }
99
100 static int parse_boolean(char *arg)
101 {
102         if (!arg)
103                 return -1;
104
105         if (strcasecmp(arg, "no") == 0 ||
106                         strcasecmp(arg, "false") == 0 ||
107                         strcasecmp(arg, "off" ) == 0 ||
108                         strcasecmp(arg, "disable" ) == 0 ||
109                         strcasecmp(arg, "n") == 0 ||
110                         strcasecmp(arg, "f") == 0 ||
111                         strcasecmp(arg, "0") == 0)
112                 return 0;
113
114         if (strcasecmp(arg, "yes") == 0 ||
115                         strcasecmp(arg, "true") == 0 ||
116                         strcasecmp(arg, "on") == 0 ||
117                         strcasecmp(arg, "enable" ) == 0 ||
118                         strcasecmp(arg, "y") == 0 ||
119                         strcasecmp(arg, "t") == 0 ||
120                         strcasecmp(arg, "1") == 0)
121                 return 1;
122
123         return -1;
124 }
125
126 static int parse_args(char *arg, struct connman_option *options)
127 {
128         int i;
129
130         if (!arg)
131                 return -1;
132
133         for (i = 0; options[i].name; i++) {
134                 if (strcmp(options[i].name, arg) == 0 ||
135                                 (strncmp(arg, "--", 2) == 0 &&
136                                         strcmp(&arg[2], options[i].name) == 0))
137                         return options[i].val;
138         }
139
140         return '?';
141 }
142
143 static int enable_return(DBusMessageIter *iter, const char *error,
144                 void *user_data)
145 {
146         char *tech = user_data;
147         char *str;
148
149         str = strrchr(tech, '/');
150         if (str)
151                 str++;
152         else
153                 str = tech;
154
155         if (!error)
156                 fprintf(stdout, "Enabled %s\n", str);
157         else
158                 fprintf(stderr, "Error %s: %s\n", str, error);
159
160         g_free(user_data);
161
162         return 0;
163 }
164
165 static int cmd_enable(char *args[], int num, struct connman_option *options)
166 {
167         char *tech;
168         dbus_bool_t b = TRUE;
169
170         if (num > 2)
171                 return -E2BIG;
172
173         if (num < 2)
174                 return -EINVAL;
175
176         if (check_dbus_name(args[1]) == false)
177                 return -EINVAL;
178
179         if (strcmp(args[1], "offline") == 0) {
180                 tech = g_strdup(args[1]);
181                 return __connmanctl_dbus_set_property(connection, "/",
182                                 "net.connman.Manager", enable_return, tech,
183                                 "OfflineMode", DBUS_TYPE_BOOLEAN, &b);
184         }
185
186         tech = g_strdup_printf("/net/connman/technology/%s", args[1]);
187         return __connmanctl_dbus_set_property(connection, tech,
188                                 "net.connman.Technology", enable_return, tech,
189                                 "Powered", DBUS_TYPE_BOOLEAN, &b);
190 }
191
192 static int disable_return(DBusMessageIter *iter, const char *error,
193                 void *user_data)
194 {
195         char *tech = user_data;
196         char *str;
197
198         str = strrchr(tech, '/');
199         if (str)
200                 str++;
201         else
202                 str = tech;
203
204         if (!error)
205                 fprintf(stdout, "Disabled %s\n", str);
206         else
207                 fprintf(stderr, "Error %s: %s\n", str, error);
208
209         g_free(user_data);
210
211         return 0;
212 }
213
214 static int cmd_disable(char *args[], int num, struct connman_option *options)
215 {
216         char *tech;
217         dbus_bool_t b = FALSE;
218
219         if (num > 2)
220                 return -E2BIG;
221
222         if (num < 2)
223                 return -EINVAL;
224
225         if (check_dbus_name(args[1]) == false)
226                 return -EINVAL;
227
228         if (strcmp(args[1], "offline") == 0) {
229                 tech = g_strdup(args[1]);
230                 return __connmanctl_dbus_set_property(connection, "/",
231                                 "net.connman.Manager", disable_return, tech,
232                                 "OfflineMode", DBUS_TYPE_BOOLEAN, &b);
233         }
234
235         tech = g_strdup_printf("/net/connman/technology/%s", args[1]);
236         return __connmanctl_dbus_set_property(connection, tech,
237                                 "net.connman.Technology", disable_return, tech,
238                                 "Powered", DBUS_TYPE_BOOLEAN, &b);
239 }
240
241 static int state_print(DBusMessageIter *iter, const char *error,
242                 void *user_data)
243 {
244         DBusMessageIter entry;
245
246         if (error) {
247                 fprintf(stderr, "Error: %s", error);
248                 return 0;
249         }
250
251         dbus_message_iter_recurse(iter, &entry);
252         __connmanctl_dbus_print(&entry, "  ", " = ", "\n");
253         fprintf(stdout, "\n");
254
255         return 0;
256 }
257
258 static int cmd_state(char *args[], int num, struct connman_option *options)
259 {
260         if (num > 1)
261                 return -E2BIG;
262
263         return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
264                         CONNMAN_PATH, "net.connman.Manager", "GetProperties",
265                         state_print, NULL, NULL, NULL);
266 }
267
268 static int services_list(DBusMessageIter *iter, const char *error,
269                 void *user_data)
270 {
271         if (!error) {
272                 __connmanctl_services_list(iter);
273                 fprintf(stdout, "\n");
274         } else {
275                 fprintf(stderr, "Error: %s\n", error);
276         }
277
278         return 0;
279 }
280
281 static int peers_list(DBusMessageIter *iter,
282                                         const char *error, void *user_data)
283 {
284         if (!error) {
285                 __connmanctl_peers_list(iter);
286                 fprintf(stdout, "\n");
287         } else
288                 fprintf(stderr, "Error: %s\n", error);
289
290         return 0;
291 }
292
293 static int object_properties(DBusMessageIter *iter,
294                                         const char *error, void *user_data)
295 {
296         char *path = user_data;
297         char *str;
298         DBusMessageIter dict;
299
300         if (!error) {
301                 fprintf(stdout, "%s\n", path);
302
303                 dbus_message_iter_recurse(iter, &dict);
304                 __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
305
306                 fprintf(stdout, "\n");
307
308         } else {
309                 str = strrchr(path, '/');
310                 if (str)
311                         str++;
312                 else
313                         str = path;
314
315                 fprintf(stderr, "Error %s: %s\n", str, error);
316         }
317
318         g_free(user_data);
319
320         return 0;
321 }
322
323 static int cmd_services(char *args[], int num, struct connman_option *options)
324 {
325         char *service_name = NULL;
326         char *path;
327         int c;
328
329         if (num > 3)
330                 return -E2BIG;
331
332         c = parse_args(args[1], options);
333         switch (c) {
334         case -1:
335                 break;
336         case 'p':
337                 if (num < 3)
338                         return -EINVAL;
339                 service_name = args[2];
340                 break;
341         default:
342                 if (num > 2)
343                         return -E2BIG;
344                 service_name = args[1];
345                 break;
346         }
347
348         if (!service_name) {
349                 return __connmanctl_dbus_method_call(connection,
350                                 CONNMAN_SERVICE, CONNMAN_PATH,
351                                 "net.connman.Manager", "GetServices",
352                                 services_list, NULL, NULL, NULL);
353         }
354
355         if (check_dbus_name(service_name) == false)
356                 return -EINVAL;
357
358         path = g_strdup_printf("/net/connman/service/%s", service_name);
359         return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
360                         "net.connman.Service", "GetProperties",
361                         object_properties, path, NULL, NULL);
362 }
363
364 static int cmd_peers(char *args[], int num, struct connman_option *options)
365 {
366         char *peer_name = NULL;
367         char *path;
368
369         if (num > 2)
370                 return -E2BIG;
371
372         if (num == 2)
373                 peer_name = args[1];
374
375         if (!peer_name) {
376                 return __connmanctl_dbus_method_call(connection,
377                                         CONNMAN_SERVICE, CONNMAN_PATH,
378                                         "net.connman.Manager", "GetPeers",
379                                         peers_list, NULL, NULL, NULL);
380         }
381
382         if (check_dbus_name(peer_name) == false)
383                 return -EINVAL;
384
385         path = g_strdup_printf("/net/connman/peer/%s", peer_name);
386         return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
387                                 path, "net.connman.Peer", "GetProperties",
388                                 object_properties, path, NULL, NULL);
389 }
390
391 static int technology_print(DBusMessageIter *iter, const char *error,
392                 void *user_data)
393 {
394         DBusMessageIter array;
395
396         if (error) {
397                 fprintf(stderr, "Error: %s\n", error);
398                 return 0;
399         }
400
401         dbus_message_iter_recurse(iter, &array);
402         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
403                 DBusMessageIter entry, dict;
404                 const char *path;
405
406                 dbus_message_iter_recurse(&array, &entry);
407                 dbus_message_iter_get_basic(&entry, &path);
408                 fprintf(stdout, "%s\n", path);
409
410                 dbus_message_iter_next(&entry);
411
412                 dbus_message_iter_recurse(&entry, &dict);
413                 __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
414                 fprintf(stdout, "\n");
415
416                 dbus_message_iter_next(&array);
417         }
418
419         return 0;
420 }
421
422 static int cmd_technologies(char *args[], int num,
423                 struct connman_option *options)
424 {
425         if (num > 1)
426                 return -E2BIG;
427
428         return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
429                         CONNMAN_PATH, "net.connman.Manager", "GetTechnologies",
430                         technology_print, NULL, NULL, NULL);
431 }
432
433 struct tether_enable {
434         char *path;
435         dbus_bool_t enable;
436 };
437
438 static int tether_set_return(DBusMessageIter *iter, const char *error,
439                 void *user_data)
440 {
441         struct tether_enable *tether = user_data;
442         char *str;
443
444         str = strrchr(tether->path, '/');
445         if (str)
446                 str++;
447         else
448                 str = tether->path;
449
450         if (!error) {
451                 fprintf(stdout, "%s tethering for %s\n",
452                                 tether->enable ? "Enabled" : "Disabled",
453                                 str);
454         } else
455                 fprintf(stderr, "Error %s %s tethering: %s\n",
456                                 tether->enable ?
457                                 "enabling" : "disabling", str, error);
458
459         g_free(tether->path);
460         g_free(user_data);
461
462         return 0;
463 }
464
465 static int tether_set(char *technology, int set_tethering)
466 {
467         struct tether_enable *tether = g_new(struct tether_enable, 1);
468
469         switch(set_tethering) {
470         case 1:
471                 tether->enable = TRUE;
472                 break;
473         case 0:
474                 tether->enable = FALSE;
475                 break;
476         default:
477                 g_free(tether);
478                 return 0;
479         }
480
481         tether->path = g_strdup_printf("/net/connman/technology/%s",
482                         technology);
483
484         return __connmanctl_dbus_set_property(connection, tether->path,
485                         "net.connman.Technology", tether_set_return,
486                         tether, "Tethering", DBUS_TYPE_BOOLEAN,
487                         &tether->enable);
488 }
489
490 struct tether_properties {
491         int ssid_result;
492         int passphrase_result;
493         int set_tethering;
494 };
495
496 static int tether_update(struct tether_properties *tether)
497 {
498         if (tether->ssid_result == 0 && tether->passphrase_result == 0)
499                 return tether_set("wifi", tether->set_tethering);
500
501         if (tether->ssid_result != -EINPROGRESS &&
502                         tether->passphrase_result != -EINPROGRESS) {
503                 g_free(tether);
504                 return 0;
505         }
506
507         return -EINPROGRESS;
508 }
509
510 static int tether_set_ssid_return(DBusMessageIter *iter, const char *error,
511                 void *user_data)
512 {
513         struct tether_properties *tether = user_data;
514
515         if (!error) {
516                 fprintf(stdout, "Wifi SSID set\n");
517                 tether->ssid_result = 0;
518         } else {
519                 fprintf(stderr, "Error setting wifi SSID: %s\n", error);
520                 tether->ssid_result = -EINVAL;
521         }
522
523         return tether_update(tether);
524 }
525
526 static int tether_set_passphrase_return(DBusMessageIter *iter,
527                 const char *error, void *user_data)
528 {
529         struct tether_properties *tether = user_data;
530
531         if (!error) {
532                 fprintf(stdout, "Wifi passphrase set\n");
533                 tether->passphrase_result = 0;
534         } else {
535                 fprintf(stderr, "Error setting wifi passphrase: %s\n", error);
536                 tether->passphrase_result = -EINVAL;
537         }
538
539         return tether_update(tether);
540 }
541
542 static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering)
543 {
544         struct tether_properties *tether = g_new(struct tether_properties, 1);
545
546         tether->set_tethering = set_tethering;
547
548         tether->ssid_result = __connmanctl_dbus_set_property(connection,
549                         "/net/connman/technology/wifi",
550                         "net.connman.Technology",
551                         tether_set_ssid_return, tether,
552                         "TetheringIdentifier", DBUS_TYPE_STRING, &ssid);
553
554         tether->passphrase_result =__connmanctl_dbus_set_property(connection,
555                         "/net/connman/technology/wifi",
556                         "net.connman.Technology",
557                         tether_set_passphrase_return, tether,
558                         "TetheringPassphrase", DBUS_TYPE_STRING, &passphrase);
559
560         if (tether->ssid_result != -EINPROGRESS &&
561                         tether->passphrase_result != -EINPROGRESS) {
562                 g_free(tether);
563                 return -ENXIO;
564         }
565
566         return -EINPROGRESS;
567 }
568
569 static int cmd_tether(char *args[], int num, struct connman_option *options)
570 {
571         char *ssid, *passphrase;
572         int set_tethering;
573
574         if (num < 3)
575                 return -EINVAL;
576
577         passphrase = args[num - 1];
578         ssid = args[num - 2];
579
580         set_tethering = parse_boolean(args[2]);
581
582         if (strcmp(args[1], "wifi") == 0) {
583
584                 if (num > 5)
585                         return -E2BIG;
586
587                 if (num == 5 && set_tethering == -1)
588                         return -EINVAL;
589
590                 if (num == 4)
591                         set_tethering = -1;
592
593                 if (num > 3)
594                         return tether_set_ssid(ssid, passphrase, set_tethering);
595         }
596
597         if (num > 3)
598                 return -E2BIG;
599
600         if (set_tethering == -1)
601                 return -EINVAL;
602
603         if (check_dbus_name(args[1]) == false)
604                 return -EINVAL;
605
606         return tether_set(args[1], set_tethering);
607 }
608
609 static int scan_return(DBusMessageIter *iter, const char *error,
610                 void *user_data)
611 {
612         char *path = user_data;
613
614         if (!error) {
615                 char *str = strrchr(path, '/');
616                 str++;
617                 fprintf(stdout, "Scan completed for %s\n", str);
618         } else
619                 fprintf(stderr, "Error %s: %s\n", path, error);
620
621         g_free(user_data);
622
623         return 0;
624 }
625
626 static int cmd_scan(char *args[], int num, struct connman_option *options)
627 {
628         char *path;
629
630         if (num > 2)
631                 return -E2BIG;
632
633         if (num < 2)
634                 return -EINVAL;
635
636         if (check_dbus_name(args[1]) == false)
637                 return -EINVAL;
638
639         path = g_strdup_printf("/net/connman/technology/%s", args[1]);
640         return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
641                         "net.connman.Technology", "Scan",
642                         scan_return, path, NULL, NULL);
643 }
644
645 static int connect_return(DBusMessageIter *iter, const char *error,
646                 void *user_data)
647 {
648         char *path = user_data;
649
650         if (!error) {
651                 char *str = strrchr(path, '/');
652                 str++;
653                 fprintf(stdout, "Connected %s\n", str);
654         } else
655                 fprintf(stderr, "Error %s: %s\n", path, error);
656
657         g_free(user_data);
658
659         return 0;
660 }
661
662 static int cmd_connect(char *args[], int num, struct connman_option *options)
663 {
664         const char *iface = "net.connman.Service";
665         char *path;
666
667         if (num > 2)
668                 return -E2BIG;
669
670         if (num < 2)
671                 return -EINVAL;
672
673         if (check_dbus_name(args[1]) == false)
674                 return -EINVAL;
675
676         if (g_strstr_len(args[1], 5, "peer_") == args[1]) {
677                 iface = "net.connman.Peer";
678                 path = g_strdup_printf("/net/connman/peer/%s", args[1]);
679         } else
680                 path = g_strdup_printf("/net/connman/service/%s", args[1]);
681
682         return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
683                         iface, "Connect", connect_return, path, NULL, NULL);
684 }
685
686 static int disconnect_return(DBusMessageIter *iter, const char *error,
687                 void *user_data)
688 {
689         char *path = user_data;
690
691         if (!error) {
692                 char *str = strrchr(path, '/');
693                 str++;
694                 fprintf(stdout, "Disconnected %s\n", str);
695         } else
696                 fprintf(stderr, "Error %s: %s\n", path, error);
697
698         g_free(user_data);
699
700         return 0;
701 }
702
703 static int cmd_disconnect(char *args[], int num, struct connman_option *options)
704 {
705         const char *iface = "net.connman.Service";
706         char *path;
707
708         if (num > 2)
709                 return -E2BIG;
710
711         if (num < 2)
712                 return -EINVAL;
713
714         if (check_dbus_name(args[1]) == false)
715                 return -EINVAL;
716
717         if (g_strstr_len(args[1], 5, "peer_") == args[1]) {
718                 iface = "net.connman.Peer";
719                 path = g_strdup_printf("/net/connman/peer/%s", args[1]);
720         } else
721                 path = g_strdup_printf("/net/connman/service/%s", args[1]);
722
723         return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
724                                         path, iface, "Disconnect",
725                                         disconnect_return, path, NULL, NULL);
726 }
727
728 static int config_return(DBusMessageIter *iter, const char *error,
729                 void *user_data)
730 {
731         char *service_name = user_data;
732
733         if (error)
734                 fprintf(stderr, "Error %s: %s\n", service_name, error);
735
736         g_free(user_data);
737
738         return 0;
739 }
740
741 struct config_append {
742         char **opts;
743         int values;
744 };
745
746 static void config_append_ipv4(DBusMessageIter *iter,
747                 void *user_data)
748 {
749         struct config_append *append = user_data;
750         char **opts = append->opts;
751         int i = 0;
752
753         if (!opts)
754                 return;
755
756         while (opts[i] && ipv4[i]) {
757                 __connmanctl_dbus_append_dict_entry(iter, ipv4[i],
758                                 DBUS_TYPE_STRING, &opts[i]);
759                 i++;
760         }
761
762         append->values = i;
763 }
764
765 static void config_append_ipv6(DBusMessageIter *iter, void *user_data)
766 {
767         struct config_append *append = user_data;
768         char **opts = append->opts;
769
770         if (!opts)
771                 return;
772
773         append->values = 1;
774
775         if (g_strcmp0(opts[0], "auto") == 0) {
776                 char *str;
777
778                 switch (parse_boolean(opts[1])) {
779                 case 0:
780                         append->values = 2;
781
782                         str = "disabled";
783                         __connmanctl_dbus_append_dict_entry(iter, "Privacy",
784                                         DBUS_TYPE_STRING, &str);
785                         break;
786
787                 case 1:
788                         append->values = 2;
789
790                         str = "enabled";
791                         __connmanctl_dbus_append_dict_entry(iter, "Privacy",
792                                         DBUS_TYPE_STRING, &str);
793                         break;
794
795                 default:
796                         if (opts[1]) {
797                                 append->values = 2;
798
799                                 if (g_strcmp0(opts[1], "prefered") != 0 &&
800                                                 g_strcmp0(opts[1],
801                                                         "preferred") != 0) {
802                                         fprintf(stderr, "Error %s: %s\n",
803                                                         opts[1],
804                                                         strerror(EINVAL));
805                                         return;
806                                 }
807
808                                 str = "prefered";
809                                 __connmanctl_dbus_append_dict_entry(iter,
810                                                 "Privacy", DBUS_TYPE_STRING,
811                                                 &str);
812                         }
813                         break;
814                 }
815         } else if (g_strcmp0(opts[0], "manual") == 0) {
816                 int i = 1;
817
818                 while (opts[i] && ipv6[i]) {
819                         if (i == 2) {
820                                 int value = atoi(opts[i]);
821                                 __connmanctl_dbus_append_dict_entry(iter,
822                                                 ipv6[i], DBUS_TYPE_BYTE,
823                                                 &value);
824                         } else {
825                                 __connmanctl_dbus_append_dict_entry(iter,
826                                                 ipv6[i], DBUS_TYPE_STRING,
827                                                 &opts[i]);
828                         }
829                         i++;
830                 }
831
832                 append->values = i;
833
834         } else if (g_strcmp0(opts[0], "off") != 0) {
835                 fprintf(stderr, "Error %s: %s\n", opts[0], strerror(EINVAL));
836
837                 return;
838         }
839
840         __connmanctl_dbus_append_dict_entry(iter, "Method", DBUS_TYPE_STRING,
841                                 &opts[0]);
842 }
843
844 static void config_append_str(DBusMessageIter *iter, void *user_data)
845 {
846         struct config_append *append = user_data;
847         char **opts = append->opts;
848         int i = 0;
849
850         if (!opts)
851                 return;
852
853         while (opts[i]) {
854                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
855                                 &opts[i]);
856                 i++;
857         }
858
859         append->values = i;
860 }
861
862 static void append_servers(DBusMessageIter *iter, void *user_data)
863 {
864         struct config_append *append = user_data;
865         char **opts = append->opts;
866         int i = 1;
867
868         if (!opts)
869                 return;
870
871         while (opts[i] && g_strcmp0(opts[i], "--excludes") != 0) {
872                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
873                                 &opts[i]);
874                 i++;
875         }
876
877         append->values = i;
878 }
879
880 static void append_excludes(DBusMessageIter *iter, void *user_data)
881 {
882         struct config_append *append = user_data;
883         char **opts = append->opts;
884         int i = append->values;
885
886         if (!opts || !opts[i] ||
887                         g_strcmp0(opts[i], "--excludes") != 0)
888                 return;
889
890         i++;
891         while (opts[i]) {
892                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
893                                 &opts[i]);
894                 i++;
895         }
896
897         append->values = i;
898 }
899
900 static void config_append_proxy(DBusMessageIter *iter, void *user_data)
901 {
902         struct config_append *append = user_data;
903         char **opts = append->opts;
904
905         if (!opts)
906                 return;
907
908         if (g_strcmp0(opts[0], "manual") == 0) {
909                 __connmanctl_dbus_append_dict_string_array(iter, "Servers",
910                                 append_servers, append);
911
912                 __connmanctl_dbus_append_dict_string_array(iter, "Excludes",
913                                 append_excludes, append);
914
915         } else if (g_strcmp0(opts[0], "auto") == 0) {
916                 if (opts[1]) {
917                         __connmanctl_dbus_append_dict_entry(iter, "URL",
918                                         DBUS_TYPE_STRING, &opts[1]);
919                         append->values++;
920                 }
921
922         } else if (g_strcmp0(opts[0], "direct") != 0)
923                 return;
924
925         __connmanctl_dbus_append_dict_entry(iter, "Method",DBUS_TYPE_STRING,
926                         &opts[0]);
927
928         append->values++;
929 }
930
931 static int cmd_config(char *args[], int num, struct connman_option *options)
932 {
933         int result = 0, res = 0, index = 2, oldindex = 0;
934         int c;
935         char *service_name, *path;
936         char **opt_start;
937         dbus_bool_t val;
938         struct config_append append;
939
940         service_name = args[1];
941         if (!service_name)
942                 return -EINVAL;
943
944         if (check_dbus_name(service_name) == false)
945                 return -EINVAL;
946
947         while (index < num && args[index]) {
948                 c = parse_args(args[index], options);
949                 opt_start = &args[index + 1];
950                 append.opts = opt_start;
951                 append.values = 0;
952
953                 res = 0;
954
955                 oldindex = index;
956                 path = g_strdup_printf("/net/connman/service/%s", service_name);
957
958                 switch (c) {
959                 case 'a':
960                         switch (parse_boolean(*opt_start)) {
961                         case 1:
962                                 val = TRUE;
963                                 break;
964                         case 0:
965                                 val = FALSE;
966                                 break;
967                         default:
968                                 res = -EINVAL;
969                                 break;
970                         }
971
972                         index++;
973
974                         if (res == 0) {
975                                 res = __connmanctl_dbus_set_property(connection,
976                                                 path, "net.connman.Service",
977                                                 config_return,
978                                                 g_strdup(service_name),
979                                                 "AutoConnect",
980                                                 DBUS_TYPE_BOOLEAN, &val);
981                         }
982                         break;
983                 case 'i':
984                         res = __connmanctl_dbus_set_property_dict(connection,
985                                         path, "net.connman.Service",
986                                         config_return, g_strdup(service_name),
987                                         "IPv4.Configuration", DBUS_TYPE_STRING,
988                                         config_append_ipv4, &append);
989                         index += append.values;
990                         break;
991
992                 case 'v':
993                         res = __connmanctl_dbus_set_property_dict(connection,
994                                         path, "net.connman.Service",
995                                         config_return, g_strdup(service_name),
996                                         "IPv6.Configuration", DBUS_TYPE_STRING,
997                                         config_append_ipv6, &append);
998                         index += append.values;
999                         break;
1000
1001                 case 'n':
1002                         res = __connmanctl_dbus_set_property_array(connection,
1003                                         path, "net.connman.Service",
1004                                         config_return, g_strdup(service_name),
1005                                         "Nameservers.Configuration",
1006                                         DBUS_TYPE_STRING, config_append_str,
1007                                         &append);
1008                         index += append.values;
1009                         break;
1010
1011                 case 't':
1012                         res = __connmanctl_dbus_set_property_array(connection,
1013                                         path, "net.connman.Service",
1014                                         config_return, g_strdup(service_name),
1015                                         "Timeservers.Configuration",
1016                                         DBUS_TYPE_STRING, config_append_str,
1017                                         &append);
1018                         index += append.values;
1019                         break;
1020
1021                 case 'd':
1022                         res = __connmanctl_dbus_set_property_array(connection,
1023                                         path, "net.connman.Service",
1024                                         config_return, g_strdup(service_name),
1025                                         "Domains.Configuration",
1026                                         DBUS_TYPE_STRING, config_append_str,
1027                                         &append);
1028                         index += append.values;
1029                         break;
1030
1031                 case 'x':
1032                         res = __connmanctl_dbus_set_property_dict(connection,
1033                                         path, "net.connman.Service",
1034                                         config_return, g_strdup(service_name),
1035                                         "Proxy.Configuration",
1036                                         DBUS_TYPE_STRING, config_append_proxy,
1037                                         &append);
1038                         index += append.values;
1039                         break;
1040                 case 'r':
1041                         res = __connmanctl_dbus_method_call(connection,
1042                                         CONNMAN_SERVICE, path,
1043                                         "net.connman.Service", "Remove",
1044                                         config_return, g_strdup(service_name),
1045                                         NULL, NULL);
1046                         break;
1047                 default:
1048                         res = -EINVAL;
1049                         break;
1050                 }
1051
1052                 g_free(path);
1053
1054                 if (res < 0) {
1055                         if (res == -EINPROGRESS)
1056                                 result = -EINPROGRESS;
1057                         else
1058                                 printf("Error '%s': %s\n", args[oldindex],
1059                                                 strerror(-res));
1060                 } else
1061                         index += res;
1062
1063                 index++;
1064         }
1065
1066         return result;
1067 }
1068
1069 static DBusHandlerResult monitor_changed(DBusConnection *connection,
1070                 DBusMessage *message, void *user_data)
1071 {
1072         DBusMessageIter iter;
1073         const char *interface, *path;
1074
1075         interface = dbus_message_get_interface(message);
1076         if (!interface)
1077                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1078
1079         if (strncmp(interface, "net.connman.", 12) != 0)
1080                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1081
1082         if (!strcmp(interface, "net.connman.Agent") ||
1083                         !strcmp(interface, "net.connman.vpn.Agent") ||
1084                         !strcmp(interface, "net.connman.Session") ||
1085                         !strcmp(interface, "net.connman.Notification"))
1086                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1087
1088         interface = strrchr(interface, '.');
1089         if (interface && *interface != '\0')
1090                 interface++;
1091
1092         path = strrchr(dbus_message_get_path(message), '/');
1093         if (path && *path != '\0')
1094                 path++;
1095
1096         __connmanctl_save_rl();
1097
1098         if (dbus_message_is_signal(message, "net.connman.Manager",
1099                                         "ServicesChanged")) {
1100
1101                 fprintf(stdout, "%-12s %-20s = {\n", interface,
1102                                 "ServicesChanged");
1103                 dbus_message_iter_init(message, &iter);
1104                 __connmanctl_services_list(&iter);
1105                 fprintf(stdout, "\n}\n");
1106
1107                 __connmanctl_redraw_rl();
1108
1109                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1110         } else if (dbus_message_is_signal(message, "net.connman.Manager",
1111                                                         "PeersChanged")) {
1112                 fprintf(stdout, "%-12s %-20s = {\n", interface,
1113                                                         "PeersChanged");
1114                 dbus_message_iter_init(message, &iter);
1115                 __connmanctl_peers_list(&iter);
1116                 fprintf(stdout, "\n}\n");
1117
1118                 __connmanctl_redraw_rl();
1119
1120                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1121         } else if (dbus_message_is_signal(message, "net.connman.vpn.Manager",
1122                                         "ConnectionAdded") ||
1123                         dbus_message_is_signal(message,
1124                                         "net.connman.vpn.Manager",
1125                                         "ConnectionRemoved")) {
1126                 interface = "vpn.Manager";
1127                 path = dbus_message_get_member(message);
1128
1129         } else if (dbus_message_is_signal(message, "net.connman.Manager",
1130                                         "TechnologyAdded") ||
1131                         dbus_message_is_signal(message, "net.connman.Manager",
1132                                         "TechnologyRemoved"))
1133                 path = dbus_message_get_member(message);
1134
1135         fprintf(stdout, "%-12s %-20s ", interface, path);
1136         dbus_message_iter_init(message, &iter);
1137
1138         __connmanctl_dbus_print(&iter, "", " = ", " = ");
1139         fprintf(stdout, "\n");
1140
1141         __connmanctl_redraw_rl();
1142
1143         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1144 }
1145
1146 static struct {
1147         char *interface;
1148         bool enabled;
1149 } monitor[] = {
1150         { "Service", false },
1151         { "Technology", false },
1152         { "Manager", false },
1153         { "vpn.Manager", false },
1154         { "vpn.Connection", false },
1155         { NULL, },
1156 };
1157
1158 static void monitor_add(char *interface)
1159 {
1160         bool add_filter = true, found = false;
1161         int i;
1162         char *rule;
1163         DBusError err;
1164
1165         for (i = 0; monitor[i].interface; i++) {
1166                 if (monitor[i].enabled == true)
1167                         add_filter = false;
1168
1169                 if (g_strcmp0(interface, monitor[i].interface) == 0) {
1170                         if (monitor[i].enabled == true)
1171                                 return;
1172
1173                         monitor[i].enabled = true;
1174                         found = true;
1175                 }
1176         }
1177
1178         if (found == false)
1179                 return;
1180
1181         if (add_filter == true)
1182                 dbus_connection_add_filter(connection, monitor_changed,
1183                                 NULL, NULL);
1184
1185         dbus_error_init(&err);
1186         rule  = g_strdup_printf("type='signal',interface='net.connman.%s'",
1187                         interface);
1188         dbus_bus_add_match(connection, rule, &err);
1189         g_free(rule);
1190
1191         if (dbus_error_is_set(&err))
1192                 fprintf(stderr, "Error: %s\n", err.message);
1193 }
1194
1195 static void monitor_del(char *interface)
1196 {
1197         bool del_filter = true, found = false;
1198         int i;
1199         char *rule;
1200
1201
1202         for (i = 0; monitor[i].interface; i++) {
1203                 if (g_strcmp0(interface, monitor[i].interface) == 0) {
1204                         if (monitor[i].enabled == false)
1205                                 return;
1206
1207                         monitor[i].enabled = false;
1208                         found = true;
1209                 }
1210
1211                 if (monitor[i].enabled == true)
1212                         del_filter = false;
1213         }
1214
1215         if (found == false)
1216                 return;
1217
1218         rule  = g_strdup_printf("type='signal',interface='net.connman.%s'",
1219                         interface);
1220         dbus_bus_remove_match(connection, rule, NULL);
1221         g_free(rule);
1222
1223         if (del_filter == true)
1224                 dbus_connection_remove_filter(connection, monitor_changed,
1225                                 NULL);
1226 }
1227
1228 static int cmd_monitor(char *args[], int num, struct connman_option *options)
1229 {
1230         bool add = true;
1231         int c;
1232
1233         if (num > 3)
1234                 return -E2BIG;
1235
1236         if (num == 3) {
1237                 switch (parse_boolean(args[2])) {
1238                 case 0:
1239                         add = false;
1240                         break;
1241
1242                 default:
1243                         break;
1244                 }
1245         }
1246
1247         c = parse_args(args[1], options);
1248         switch (c) {
1249         case -1:
1250                 monitor_add("Service");
1251                 monitor_add("Technology");
1252                 monitor_add("Manager");
1253                 monitor_add("vpn.Manager");
1254                 monitor_add("vpn.Connection");
1255                 break;
1256
1257         case 's':
1258                 if (add == true)
1259                         monitor_add("Service");
1260                 else
1261                         monitor_del("Service");
1262                 break;
1263
1264         case 'c':
1265                 if (add == true)
1266                         monitor_add("Technology");
1267                 else
1268                         monitor_del("Technology");
1269                 break;
1270
1271         case 'm':
1272                 if (add == true)
1273                         monitor_add("Manager");
1274                 else
1275                         monitor_del("Manager");
1276                 break;
1277
1278         case 'M':
1279                 if (add == true)
1280                         monitor_add("vpn.Manager");
1281                 else
1282                         monitor_del("vpn.Manager");
1283                 break;
1284
1285         case 'C':
1286                 if (add == true)
1287                         monitor_add("vpn.Connection");
1288                 else
1289                         monitor_del("vpn.Connection");
1290                 break;
1291
1292         default:
1293                 switch(parse_boolean(args[1])) {
1294                 case 0:
1295                         monitor_del("Service");
1296                         monitor_del("Technology");
1297                         monitor_del("Manager");
1298                         monitor_del("vpn.Manager");
1299                         monitor_del("vpn.Connection");
1300                         break;
1301
1302                 case 1:
1303                         monitor_add("Service");
1304                         monitor_add("Technology");
1305                         monitor_add("Manager");
1306                         monitor_add("vpn.Manager");
1307                         monitor_add("vpn.Connection");
1308                         break;
1309
1310                 default:
1311                         return -EINVAL;
1312                 }
1313         }
1314
1315         if (add == true)
1316                 return -EINPROGRESS;
1317
1318         return 0;
1319 }
1320
1321 static int cmd_agent(char *args[], int num, struct connman_option *options)
1322 {
1323         if (!__connmanctl_is_interactive()) {
1324                 fprintf(stderr, "Error: Not supported in non-interactive "
1325                                 "mode\n");
1326                 return 0;
1327         }
1328
1329         if (num > 2)
1330                 return -E2BIG;
1331
1332         if (num < 2)
1333                 return -EINVAL;
1334
1335         switch(parse_boolean(args[1])) {
1336         case 0:
1337                 __connmanctl_agent_unregister(connection);
1338                 break;
1339
1340         case 1:
1341                 if (__connmanctl_agent_register(connection) == -EINPROGRESS)
1342                         return -EINPROGRESS;
1343
1344                 break;
1345
1346         default:
1347                 return -EINVAL;
1348                 break;
1349         }
1350
1351         return 0;
1352 }
1353
1354 static int vpnconnections_properties(DBusMessageIter *iter, const char *error,
1355                 void *user_data)
1356 {
1357         char *path = user_data;
1358         char *str;
1359         DBusMessageIter dict;
1360
1361         if (!error) {
1362                 fprintf(stdout, "%s\n", path);
1363
1364                 dbus_message_iter_recurse(iter, &dict);
1365                 __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
1366
1367                 fprintf(stdout, "\n");
1368
1369         } else {
1370                 str = strrchr(path, '/');
1371                 if (str)
1372                         str++;
1373                 else
1374                         str = path;
1375
1376                 fprintf(stderr, "Error %s: %s\n", str, error);
1377         }
1378
1379         g_free(user_data);
1380
1381         return 0;
1382 }
1383
1384 static int vpnconnections_list(DBusMessageIter *iter, const char *error,
1385                 void *user_data)
1386 {
1387         if (!error)
1388                 __connmanctl_vpnconnections_list(iter);
1389         else
1390                 fprintf(stderr, "Error: %s\n", error);
1391
1392         return 0;
1393 }
1394
1395 static int cmd_vpnconnections(char *args[], int num,
1396                 struct connman_option *options)
1397 {
1398         char *vpnconnection_name, *path;
1399
1400         if (num > 2)
1401                 return -E2BIG;
1402
1403         vpnconnection_name = args[1];
1404
1405         if (!vpnconnection_name)
1406                 return __connmanctl_dbus_method_call(connection,
1407                                 VPN_SERVICE, VPN_PATH,
1408                                 "net.connman.vpn.Manager", "GetConnections",
1409                                 vpnconnections_list, NULL,
1410                                 NULL, NULL);
1411
1412         if (check_dbus_name(vpnconnection_name) == false)
1413                 return -EINVAL;
1414
1415         path = g_strdup_printf("/net/connman/vpn/connection/%s",
1416                         vpnconnection_name);
1417         return __connmanctl_dbus_method_call(connection, VPN_SERVICE, path,
1418                         "net.connman.vpn.Connection", "GetProperties",
1419                         vpnconnections_properties, path, NULL, NULL);
1420
1421 }
1422
1423 static int cmd_vpnagent(char *args[], int num, struct connman_option *options)
1424 {
1425         if (!__connmanctl_is_interactive()) {
1426                 fprintf(stderr, "Error: Not supported in non-interactive "
1427                                 "mode\n");
1428                 return 0;
1429         }
1430
1431         if (num > 2)
1432                 return -E2BIG;
1433
1434         if (num < 2)
1435                 return -EINVAL;
1436
1437         switch(parse_boolean(args[1])) {
1438         case 0:
1439                 __connmanctl_vpn_agent_unregister(connection);
1440                 break;
1441
1442         case 1:
1443                 if (__connmanctl_vpn_agent_register(connection) ==
1444                                 -EINPROGRESS)
1445                         return -EINPROGRESS;
1446
1447                 break;
1448
1449         default:
1450                 return -EINVAL;
1451                 break;
1452         }
1453
1454         return 0;
1455 }
1456
1457 static DBusMessage *session_release(DBusConnection *connection,
1458                 DBusMessage *message, void *user_data)
1459 {
1460         __connmanctl_save_rl();
1461
1462         fprintf(stdout, "Session %s released\n", session_path);
1463
1464         __connmanctl_redraw_rl();
1465
1466         g_free(session_path);
1467         session_path = NULL;
1468         session_connected = false;
1469
1470         return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
1471 }
1472
1473 static DBusMessage *session_update(DBusConnection *connection,
1474                 DBusMessage *message, void *user_data)
1475 {
1476         DBusMessageIter iter, dict;
1477
1478         __connmanctl_save_rl();
1479
1480         fprintf(stdout, "Session      Update               = {\n");
1481
1482         dbus_message_iter_init(message, &iter);
1483         dbus_message_iter_recurse(&iter, &dict);
1484
1485         __connmanctl_dbus_print(&dict, "", " = ", "\n");
1486         fprintf(stdout, "\n}\n");
1487
1488         dbus_message_iter_recurse(&iter, &dict);
1489
1490         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
1491                 DBusMessageIter entry, variant;
1492                 char *field, *state;
1493
1494                 dbus_message_iter_recurse(&dict, &entry);
1495
1496                 dbus_message_iter_get_basic(&entry, &field);
1497
1498                 if (dbus_message_iter_get_arg_type(&entry)
1499                                 == DBUS_TYPE_STRING
1500                                 && !strcmp(field, "State")) {
1501
1502                         dbus_message_iter_next(&entry);
1503                         dbus_message_iter_recurse(&entry, &variant);
1504                         if (dbus_message_iter_get_arg_type(&variant)
1505                                         != DBUS_TYPE_STRING)
1506                                 break;
1507
1508                         dbus_message_iter_get_basic(&variant, &state);
1509
1510                         if (!session_connected && (!strcmp(state, "connected")
1511                                         || !strcmp(state, "online"))) {
1512
1513                                 fprintf(stdout, "Session %s connected\n",
1514                                         session_path);
1515                                 session_connected = true;
1516
1517                                 break;
1518                         }
1519
1520                         if (!strcmp(state, "disconnected") &&
1521                                         session_connected) {
1522
1523                                 fprintf(stdout, "Session %s disconnected\n",
1524                                         session_path);
1525                                 session_connected = false;
1526                         }
1527                         break;
1528                 }
1529
1530                 dbus_message_iter_next(&dict);
1531         }
1532
1533         __connmanctl_redraw_rl();
1534
1535         return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
1536 }
1537
1538 static const GDBusMethodTable notification_methods[] = {
1539         { GDBUS_METHOD("Release", NULL, NULL, session_release) },
1540         { GDBUS_METHOD("Update", GDBUS_ARGS({"settings", "a{sv}"}),
1541                                 NULL, session_update) },
1542         { },
1543 };
1544
1545 static int session_notify_add(const char *path)
1546 {
1547         if (session_notify_path)
1548                 return 0;
1549
1550         if (!g_dbus_register_interface(connection, path,
1551                                         "net.connman.Notification",
1552                                         notification_methods, NULL, NULL,
1553                                         NULL, NULL)) {
1554                 fprintf(stderr, "Error: Failed to register VPN Agent "
1555                                 "callbacks\n");
1556                 return -EIO;
1557         }
1558
1559         session_notify_path = g_strdup(path);
1560
1561         return 0;
1562 }
1563
1564 static void session_notify_remove(void)
1565 {
1566         if (!session_notify_path)
1567                 return;
1568
1569         g_dbus_unregister_interface(connection, session_notify_path,
1570                         "net.connman.Notification");
1571
1572         g_free(session_notify_path);
1573         session_notify_path = NULL;
1574 }
1575
1576 static int session_connect_cb(DBusMessageIter *iter, const char *error,
1577                 void *user_data)
1578 {
1579         if (error) {
1580                 fprintf(stderr, "Error: %s", error);
1581                 return 0;
1582         }
1583
1584         return -EINPROGRESS;
1585 }
1586
1587
1588 static int session_connect(void)
1589 {
1590         return __connmanctl_dbus_method_call(connection, "net.connman",
1591                         session_path, "net.connman.Session", "Connect",
1592                         session_connect_cb, NULL, NULL, NULL);
1593 }
1594
1595 static int session_disconnect_cb(DBusMessageIter *iter, const char *error,
1596                 void *user_data)
1597 {
1598         if (error)
1599                 fprintf(stderr, "Error: %s", error);
1600
1601         return 0;
1602 }
1603
1604 static int session_disconnect(void)
1605 {
1606         return __connmanctl_dbus_method_call(connection, "net.connman",
1607                         session_path, "net.connman.Session", "Disconnect",
1608                         session_disconnect_cb, NULL, NULL, NULL);
1609 }
1610
1611 static int session_create_cb(DBusMessageIter *iter, const char *error,
1612                 void *user_data)
1613 {
1614         gboolean connect = GPOINTER_TO_INT(user_data);
1615         char *str;
1616
1617         if (error) {
1618                 fprintf(stderr, "Error creating session: %s", error);
1619                 session_notify_remove();
1620                 return 0;
1621         }
1622
1623         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH) {
1624                 fprintf(stderr, "Error creating session: No session path\n");
1625                 return -EINVAL;
1626         }
1627
1628         g_free(session_path);
1629
1630         dbus_message_iter_get_basic(iter, &str);
1631         session_path = g_strdup(str);
1632
1633         fprintf(stdout, "Session %s created\n", session_path);
1634
1635         if (connect)
1636                 return session_connect();
1637
1638         return -EINPROGRESS;
1639 }
1640
1641 static void session_create_append(DBusMessageIter *iter, void *user_data)
1642 {
1643         const char *notify_path = user_data;
1644
1645         __connmanctl_dbus_append_dict(iter, NULL, NULL);
1646
1647         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
1648                         &notify_path);
1649 }
1650
1651 static int session_create(gboolean connect)
1652 {
1653         int res;
1654         char *notify_path;
1655
1656         notify_path = g_strdup_printf("/net/connman/connmanctl%d", getpid());
1657         session_notify_add(notify_path);
1658
1659         res = __connmanctl_dbus_method_call(connection, "net.connman", "/",
1660                         "net.connman.Manager", "CreateSession",
1661                         session_create_cb, GINT_TO_POINTER(connect),
1662                         session_create_append, notify_path);
1663
1664         g_free(notify_path);
1665
1666         if (res < 0 && res != -EINPROGRESS)
1667                 session_notify_remove();
1668
1669         return res;
1670 }
1671
1672 static int session_destroy_cb(DBusMessageIter *iter, const char *error,
1673                 void *user_data)
1674 {
1675         if (error) {
1676                 fprintf(stderr, "Error destroying session: %s", error);
1677                 return 0;
1678         }
1679
1680         fprintf(stdout, "Session %s ended\n", session_path);
1681
1682         g_free(session_path);
1683         session_path = NULL;
1684         session_connected = false;
1685
1686         return 0;
1687 }
1688
1689 static void session_destroy_append(DBusMessageIter *iter, void *user_data)
1690 {
1691         const char *path = user_data;
1692
1693         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
1694 }
1695
1696 static int session_destroy(void)
1697 {
1698         return __connmanctl_dbus_method_call(connection, "net.connman", "/",
1699                         "net.connman.Manager", "DestroySession",
1700                         session_destroy_cb, NULL,
1701                         session_destroy_append, session_path);
1702 }
1703
1704 static int session_config_return(DBusMessageIter *iter, const char *error,
1705                 void *user_data)
1706 {
1707         char *property_name = user_data;
1708
1709         if (error)
1710                 fprintf(stderr, "Error setting session %s: %s\n",
1711                                 property_name, error);
1712
1713         return 0;
1714 }
1715
1716 static void session_config_append_array(DBusMessageIter *iter,
1717                 void *user_data)
1718 {
1719         struct config_append *append = user_data;
1720         char **opts = append->opts;
1721         int i = 1;
1722
1723         if (!opts)
1724                 return;
1725
1726         while (opts[i] && strncmp(opts[i], "--", 2) != 0) {
1727                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
1728                                 &opts[i]);
1729                 i++;
1730         }
1731
1732         append->values = i;
1733 }
1734
1735 static int session_config(char *args[], int num,
1736                 struct connman_option *options)
1737 {
1738         int index = 0, res = 0;
1739         struct config_append append;
1740         char c;
1741
1742         while (index < num && args[index]) {
1743                 append.opts = &args[index];
1744                 append.values = 0;
1745
1746                 c = parse_args(args[index], options);
1747
1748                 switch (c) {
1749                 case 'b':
1750                         res = __connmanctl_dbus_session_change_array(connection,
1751                                         session_path, session_config_return,
1752                                         "AllowedBearers", "AllowedBearers",
1753                                         session_config_append_array, &append);
1754                         break;
1755                 case 't':
1756                         if (!args[index + 1]) {
1757                                 res = -EINVAL;
1758                                 break;
1759                         }
1760
1761                         res = __connmanctl_dbus_session_change(connection,
1762                                         session_path, session_config_return,
1763                                         "ConnectionType", "ConnectionType",
1764                                         DBUS_TYPE_STRING, &args[index + 1]);
1765                         append.values = 2;
1766                         break;
1767
1768                 default:
1769                         res = -EINVAL;
1770                 }
1771
1772                 if (res < 0 && res != -EINPROGRESS) {
1773                         printf("Error '%s': %s\n", args[index],
1774                                         strerror(-res));
1775                         return 0;
1776                 }
1777
1778                 index += append.values;
1779         }
1780
1781         return 0;
1782 }
1783
1784 static int cmd_session(char *args[], int num, struct connman_option *options)
1785 {
1786         char *command;
1787
1788         if (num < 2)
1789                 return -EINVAL;
1790
1791         command = args[1];
1792
1793         switch(parse_boolean(command)) {
1794         case 0:
1795                 if (!session_path)
1796                         return -EALREADY;
1797                 return session_destroy();
1798
1799         case 1:
1800                 if (session_path)
1801                         return -EALREADY;
1802                 return session_create(FALSE);
1803
1804         default:
1805                 if (!strcmp(command, "connect")) {
1806                         if (!session_path)
1807                                 return session_create(TRUE);
1808
1809                         return session_connect();
1810
1811                 } else if (!strcmp(command, "disconnect")) {
1812
1813                         if (!session_path) {
1814                                 fprintf(stdout, "Session does not exist\n");
1815                                 return 0;
1816                         }
1817
1818                         return session_disconnect();
1819                 } else if (!strcmp(command, "config")) {
1820                         if (!session_path) {
1821                                 fprintf(stdout, "Session does not exist\n");
1822                                 return 0;
1823                         }
1824
1825                         if (num == 2)
1826                                 return -EINVAL;
1827
1828                         return session_config(&args[2], num - 2, options);
1829                 }
1830
1831         }
1832
1833         return -EINVAL;
1834 }
1835
1836 static int cmd_exit(char *args[], int num, struct connman_option *options)
1837 {
1838         return 1;
1839 }
1840
1841 static char *lookup_service(const char *text, int state)
1842 {
1843         static int len = 0;
1844         static GHashTableIter iter;
1845         gpointer key, value;
1846
1847         if (state == 0) {
1848                 g_hash_table_iter_init(&iter, service_hash);
1849                 len = strlen(text);
1850         }
1851
1852         while (g_hash_table_iter_next(&iter, &key, &value)) {
1853                 const char *service = key;
1854                 if (strncmp(text, service, len) == 0)
1855                         return strdup(service);
1856         }
1857
1858         return NULL;
1859 }
1860
1861 static char *lookup_service_arg(const char *text, int state)
1862 {
1863         if (__connmanctl_input_calc_level() > 1) {
1864                 __connmanctl_input_lookup_end();
1865                 return NULL;
1866         }
1867
1868         return lookup_service(text, state);
1869 }
1870
1871 static char *lookup_peer(const char *text, int state)
1872 {
1873         static GHashTableIter iter;
1874         gpointer key, value;
1875         static int len = 0;
1876
1877         if (state == 0) {
1878                 g_hash_table_iter_init(&iter, peer_hash);
1879                 len = strlen(text);
1880         }
1881
1882         while (g_hash_table_iter_next(&iter, &key, &value)) {
1883                 const char *peer = key;
1884                 if (strncmp(text, peer, len) == 0)
1885                         return strdup(peer);
1886         }
1887
1888         return NULL;
1889 }
1890
1891 static char *lookup_peer_arg(const char *text, int state)
1892 {
1893         if (__connmanctl_input_calc_level() > 1) {
1894                 __connmanctl_input_lookup_end();
1895                 return NULL;
1896         }
1897
1898         return lookup_peer(text, state);
1899 }
1900
1901 static char *lookup_technology(const char *text, int state)
1902 {
1903         static int len = 0;
1904         static GHashTableIter iter;
1905         gpointer key, value;
1906
1907         if (state == 0) {
1908                 g_hash_table_iter_init(&iter, technology_hash);
1909                 len = strlen(text);
1910         }
1911
1912         while (g_hash_table_iter_next(&iter, &key, &value)) {
1913                 const char *technology = key;
1914                 if (strncmp(text, technology, len) == 0)
1915                         return strdup(technology);
1916         }
1917
1918         return NULL;
1919 }
1920
1921 static char *lookup_technology_arg(const char *text, int state)
1922 {
1923         if (__connmanctl_input_calc_level() > 1) {
1924                 __connmanctl_input_lookup_end();
1925                 return NULL;
1926         }
1927
1928         return lookup_technology(text, state);
1929 }
1930
1931 static char *lookup_technology_offline(const char *text, int state)
1932 {
1933         static int len = 0;
1934         static bool end = false;
1935         char *str;
1936
1937         if (__connmanctl_input_calc_level() > 1) {
1938                 __connmanctl_input_lookup_end();
1939                 return NULL;
1940         }
1941
1942         if (state == 0) {
1943                 len = strlen(text);
1944                 end = false;
1945         }
1946
1947         if (end)
1948                 return NULL;
1949
1950         str = lookup_technology(text, state);
1951         if (str)
1952                 return str;
1953
1954         end = true;
1955
1956         if (strncmp(text, "offline", len) == 0)
1957                 return strdup("offline");
1958
1959         return NULL;
1960 }
1961
1962 static char *lookup_on_off(const char *text, int state)
1963 {
1964         char *onoff[] = { "on", "off", NULL };
1965         static int idx = 0;
1966         static int len = 0;
1967
1968         char *str;
1969
1970         if (!state) {
1971                 idx = 0;
1972                 len = strlen(text);
1973         }
1974
1975         while (onoff[idx]) {
1976                 str = onoff[idx];
1977                 idx++;
1978
1979                 if (!strncmp(text, str, len))
1980                         return strdup(str);
1981         }
1982
1983         return NULL;
1984 }
1985
1986 static char *lookup_tether(const char *text, int state)
1987 {
1988         int level;
1989
1990         level = __connmanctl_input_calc_level();
1991         if (level < 2)
1992                 return lookup_technology(text, state);
1993
1994         if (level == 2)
1995                 return lookup_on_off(text, state);
1996
1997         __connmanctl_input_lookup_end();
1998
1999         return NULL;
2000 }
2001
2002 static char *lookup_agent(const char *text, int state)
2003 {
2004         if (__connmanctl_input_calc_level() > 1) {
2005                 __connmanctl_input_lookup_end();
2006                 return NULL;
2007         }
2008
2009         return lookup_on_off(text, state);
2010 }
2011
2012 static struct connman_option service_options[] = {
2013         {"properties", 'p', "[<service>]      (obsolete)"},
2014         { NULL, }
2015 };
2016
2017 static struct connman_option config_options[] = {
2018         {"nameservers", 'n', "<dns1> [<dns2>] [<dns3>]"},
2019         {"timeservers", 't', "<ntp1> [<ntp2>] [...]"},
2020         {"domains", 'd', "<domain1> [<domain2>] [...]"},
2021         {"ipv6", 'v', "off|auto [enable|disable|preferred]|\n"
2022                       "\t\t\tmanual <address> <prefixlength> <gateway>"},
2023         {"proxy", 'x', "direct|auto <URL>|manual <URL1> [<URL2>] [...]\n"
2024                        "\t\t\t[exclude <exclude1> [<exclude2>] [...]]"},
2025         {"autoconnect", 'a', "yes|no"},
2026         {"ipv4", 'i', "off|dhcp|manual <address> <netmask> <gateway>"},
2027         {"remove", 'r', "                 Remove service"},
2028         { NULL, }
2029 };
2030
2031 static struct connman_option monitor_options[] = {
2032         {"services", 's', "[off]            Monitor only services"},
2033         {"tech", 'c', "[off]            Monitor only technologies"},
2034         {"manager", 'm', "[off]            Monitor only manager interface"},
2035         {"vpnmanager", 'M', "[off]            Monitor only VPN manager "
2036          "interface"},
2037         {"vpnconnection", 'C', "[off]            Monitor only VPN "
2038          "connections" },
2039         { NULL, }
2040 };
2041
2042 static struct connman_option session_options[] = {
2043         {"bearers", 'b', "<technology1> [<technology2> [...]]"},
2044         {"type", 't', "local|internet|any"},
2045         { NULL, }
2046 };
2047
2048 static char *lookup_options(struct connman_option *options, const char *text,
2049                 int state)
2050 {
2051         static int idx = 0;
2052         static int len = 0;
2053         const char *str;
2054
2055         if (state == 0) {
2056                 idx = 0;
2057                 len = strlen(text);
2058         }
2059
2060         while (options[idx].name) {
2061                 str = options[idx].name;
2062                 idx++;
2063
2064                 if (str && strncmp(text, str, len) == 0)
2065                         return strdup(str);
2066         }
2067
2068         return NULL;
2069 }
2070
2071 static char *lookup_monitor(const char *text, int state)
2072 {
2073         int level;
2074
2075         level = __connmanctl_input_calc_level();
2076
2077         if (level < 2)
2078                 return lookup_options(monitor_options, text, state);
2079
2080         if (level == 2)
2081                 return lookup_on_off(text, state);
2082
2083         __connmanctl_input_lookup_end();
2084         return NULL;
2085 }
2086
2087 static char *lookup_config(const char *text, int state)
2088 {
2089         if (__connmanctl_input_calc_level() < 2)
2090                 return lookup_service(text, state);
2091
2092         return lookup_options(config_options, text, state);
2093 }
2094
2095 static char *lookup_session(const char *text, int state)
2096 {
2097         return lookup_options(session_options, text, state);
2098 }
2099
2100 static int peer_service_cb(DBusMessageIter *iter, const char *error,
2101                                                         void *user_data)
2102 {
2103         bool registration = GPOINTER_TO_INT(user_data);
2104
2105         if (error)
2106                 fprintf(stderr, "Error %s peer service: %s\n",
2107                         registration ? "registering" : "unregistering", error);
2108         else
2109                 fprintf(stdout, "Peer service %s\n",
2110                         registration ? "registered" : "unregistered");
2111
2112         return 0;
2113 }
2114
2115 struct _peer_service {
2116         unsigned char *bjr_query;
2117         int bjr_query_len;
2118         unsigned char *bjr_response;
2119         int bjr_response_len;
2120         unsigned char *wfd_ies;
2121         int wfd_ies_len;
2122         char *upnp_service;
2123         int version;
2124         int master;
2125 };
2126
2127 static void append_dict_entry_fixed_array(DBusMessageIter *iter,
2128                         const char *property, void *value, int length)
2129 {
2130         DBusMessageIter dict_entry, variant, array;
2131
2132         dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY,
2133                                                         NULL, &dict_entry);
2134         dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING,
2135                                                                 &property);
2136         dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT,
2137                         DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING,
2138                         &variant);
2139         dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
2140                                         DBUS_TYPE_BYTE_AS_STRING, &array);
2141         dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
2142                                                         value, length);
2143         dbus_message_iter_close_container(&variant, &array);
2144         dbus_message_iter_close_container(&dict_entry, &variant);
2145         dbus_message_iter_close_container(iter, &dict_entry);
2146 }
2147
2148 static void append_peer_service_dict(DBusMessageIter *iter, void *user_data)
2149 {
2150         struct _peer_service *service = user_data;
2151
2152         if (service->bjr_query && service->bjr_response) {
2153                 append_dict_entry_fixed_array(iter, "BonjourQuery",
2154                         &service->bjr_query, service->bjr_query_len);
2155                 append_dict_entry_fixed_array(iter, "BonjourResponse",
2156                         &service->bjr_response, service->bjr_response_len);
2157         } else if (service->upnp_service && service->version) {
2158                 __connmanctl_dbus_append_dict_entry(iter, "UpnpVersion",
2159                                         DBUS_TYPE_INT32, &service->version);
2160                 __connmanctl_dbus_append_dict_entry(iter, "UpnpService",
2161                                 DBUS_TYPE_STRING, &service->upnp_service);
2162         } else if (service->wfd_ies) {
2163                 append_dict_entry_fixed_array(iter, "WiFiDisplayIEs",
2164                                 &service->wfd_ies, service->wfd_ies_len);
2165         }
2166 }
2167
2168 static void peer_service_append(DBusMessageIter *iter, void *user_data)
2169 {
2170         struct _peer_service *service = user_data;
2171         dbus_bool_t master;
2172
2173         __connmanctl_dbus_append_dict(iter, append_peer_service_dict, service);
2174
2175         if (service->master < 0)
2176                 return;
2177
2178         master = service->master == 1 ? TRUE : FALSE;
2179         dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &master);
2180 }
2181
2182 static struct _peer_service *fill_in_peer_service(unsigned char *bjr_query,
2183                                 int bjr_query_len, unsigned char *bjr_response,
2184                                 int bjr_response_len, char *upnp_service,
2185                                 int version, unsigned char *wfd_ies,
2186                                 int wfd_ies_len)
2187 {
2188         struct _peer_service *service;
2189
2190         service = dbus_malloc0(sizeof(*service));
2191
2192         if (bjr_query_len && bjr_response_len) {
2193                 service->bjr_query = dbus_malloc0(bjr_query_len);
2194                 memcpy(service->bjr_query, bjr_query, bjr_query_len);
2195                 service->bjr_query_len = bjr_query_len;
2196
2197                 service->bjr_response = dbus_malloc0(bjr_response_len);
2198                 memcpy(service->bjr_response, bjr_response, bjr_response_len);
2199                 service->bjr_response_len = bjr_response_len;
2200         } else if (upnp_service && version) {
2201                 service->upnp_service = strdup(upnp_service);
2202                 service->version = version;
2203         } else if (wfd_ies && wfd_ies_len) {
2204                 service->wfd_ies = dbus_malloc0(wfd_ies_len);
2205                 memcpy(service->wfd_ies, wfd_ies, wfd_ies_len);
2206                 service->wfd_ies_len = wfd_ies_len;
2207         } else {
2208                 dbus_free(service);
2209                 service = NULL;
2210         }
2211
2212         return service;
2213 }
2214
2215 static void free_peer_service(struct _peer_service *service)
2216 {
2217         dbus_free(service->bjr_query);
2218         dbus_free(service->bjr_response);
2219         dbus_free(service->wfd_ies);
2220         free(service->upnp_service);
2221         dbus_free(service);
2222 }
2223
2224 static int peer_service_register(unsigned char *bjr_query, int bjr_query_len,
2225                         unsigned char *bjr_response, int bjr_response_len,
2226                         char *upnp_service, int version,
2227                         unsigned char *wfd_ies, int wfd_ies_len, int master)
2228 {
2229         struct _peer_service *service;
2230         bool registration = true;
2231         int ret;
2232
2233         service = fill_in_peer_service(bjr_query, bjr_query_len, bjr_response,
2234                                 bjr_response_len, upnp_service, version,
2235                                 wfd_ies, wfd_ies_len);
2236         if (!service)
2237                 return -EINVAL;
2238
2239         service->master = master;
2240
2241         ret = __connmanctl_dbus_method_call(connection, "net.connman", "/",
2242                         "net.connman.Manager", "RegisterPeerService",
2243                         peer_service_cb, GINT_TO_POINTER(registration),
2244                         peer_service_append, service);
2245
2246         free_peer_service(service);
2247
2248         return ret;
2249 }
2250
2251 static int peer_service_unregister(unsigned char *bjr_query, int bjr_query_len,
2252                         unsigned char *bjr_response, int bjr_response_len,
2253                         char *upnp_service, int version,
2254                         unsigned char *wfd_ies, int wfd_ies_len)
2255 {
2256         struct _peer_service *service;
2257         bool registration = false;
2258         int ret;
2259
2260         service = fill_in_peer_service(bjr_query, bjr_query_len, bjr_response,
2261                                 bjr_response_len, upnp_service, version,
2262                                 wfd_ies, wfd_ies_len);
2263         if (!service)
2264                 return -EINVAL;
2265
2266         service->master = -1;
2267
2268         ret = __connmanctl_dbus_method_call(connection, "net.connman", "/",
2269                         "net.connman.Manager", "UnregisterPeerService",
2270                         peer_service_cb, GINT_TO_POINTER(registration),
2271                         peer_service_append, service);
2272
2273         free_peer_service(service);
2274
2275         return ret;
2276 }
2277
2278 static int parse_spec_array(char *command, unsigned char spec[1024])
2279 {
2280         int length, pos, end;
2281         char b[3] = {};
2282         char *e;
2283
2284         end = strlen(command);
2285         for (e = NULL, length = pos = 0; command[pos] != '\0'; length++) {
2286                 if (pos+2 > end)
2287                         return -EINVAL;
2288
2289                 b[0] = command[pos];
2290                 b[1] = command[pos+1];
2291
2292                 spec[length] = strtol(b, &e, 16);
2293                 if (e && *e != '\0')
2294                         return -EINVAL;
2295
2296                 pos += 2;
2297         }
2298
2299         return length;
2300 }
2301
2302 static int cmd_peer_service(char *args[], int num,
2303                                 struct connman_option *options)
2304 {
2305         unsigned char bjr_query[1024] = {};
2306         unsigned char bjr_response[1024] = {};
2307         unsigned char wfd_ies[1024] = {};
2308         char *upnp_service = NULL;
2309         int bjr_query_len = 0, bjr_response_len = 0;
2310         int version = 0, master = 0, wfd_ies_len = 0;
2311         int limit;
2312
2313         if (num < 4)
2314                 return -EINVAL;
2315
2316         if (!strcmp(args[2], "wfd_ies")) {
2317                 wfd_ies_len = parse_spec_array(args[3], wfd_ies);
2318                 if (wfd_ies_len == -EINVAL)
2319                         return -EINVAL;
2320                 limit = 5;
2321                 goto master;
2322         }
2323
2324         if (num < 6)
2325                 return -EINVAL;
2326
2327         limit = 7;
2328         if (!strcmp(args[2], "bjr_query")) {
2329                 if (strcmp(args[4], "bjr_response"))
2330                         return -EINVAL;
2331                 bjr_query_len = parse_spec_array(args[3], bjr_query);
2332                 bjr_response_len = parse_spec_array(args[5], bjr_response);
2333
2334                 if (bjr_query_len == -EINVAL || bjr_response_len == -EINVAL)
2335                         return -EINVAL;
2336         } else if (!strcmp(args[2], "upnp_service")) {
2337                 char *e = NULL;
2338
2339                 if (strcmp(args[4], "upnp_version"))
2340                         return -EINVAL;
2341                 upnp_service = args[3];
2342                 version = strtol(args[5], &e, 10);
2343                 if (*e != '\0')
2344                         return -EINVAL;
2345         }
2346
2347 master:
2348         if (num == limit) {
2349                 master = parse_boolean(args[6]);
2350                 if (master < 0)
2351                         return -EINVAL;
2352         }
2353
2354         if (!strcmp(args[1], "register")) {
2355                 return peer_service_register(bjr_query, bjr_query_len,
2356                                 bjr_response, bjr_response_len, upnp_service,
2357                                 version, wfd_ies, wfd_ies_len, master);
2358         } else if (!strcmp(args[1], "unregister")) {
2359                 return peer_service_unregister(bjr_query, bjr_query_len,
2360                                 bjr_response, bjr_response_len, upnp_service,
2361                                 version, wfd_ies, wfd_ies_len);
2362         }
2363
2364         return -EINVAL;
2365 }
2366
2367 static const struct {
2368         const char *cmd;
2369         const char *argument;
2370         struct connman_option *options;
2371         int (*func) (char *args[], int num, struct connman_option *options);
2372         const char *desc;
2373         __connmanctl_lookup_cb cb;
2374 } cmd_table[] = {
2375         { "state",        NULL,           NULL,            cmd_state,
2376           "Shows if the system is online or offline", NULL },
2377         { "technologies", NULL,           NULL,            cmd_technologies,
2378           "Display technologies", NULL },
2379         { "enable",       "<technology>|offline", NULL,    cmd_enable,
2380           "Enables given technology or offline mode",
2381           lookup_technology_offline },
2382         { "disable",      "<technology>|offline", NULL,    cmd_disable,
2383           "Disables given technology or offline mode",
2384           lookup_technology_offline },
2385         { "tether", "<technology> on|off\n"
2386                     "            wifi [on|off] <ssid> <passphrase> ",
2387                                           NULL,            cmd_tether,
2388           "Enable, disable tethering, set SSID and passphrase for wifi",
2389           lookup_tether },
2390         { "services",     "[<service>]",  service_options, cmd_services,
2391           "Display services", lookup_service_arg },
2392         { "peers",        "[peer]",       NULL,            cmd_peers,
2393           "Display peers", lookup_peer_arg },
2394         { "scan",         "<technology>", NULL,            cmd_scan,
2395           "Scans for new services for given technology",
2396           lookup_technology_arg },
2397         { "connect",      "<service/peer>", NULL,          cmd_connect,
2398           "Connect a given service or peer", lookup_service_arg },
2399         { "disconnect",   "<service/peer>", NULL,          cmd_disconnect,
2400           "Disconnect a given service or peer", lookup_service_arg },
2401         { "config",       "<service>",    config_options,  cmd_config,
2402           "Set service configuration options", lookup_config },
2403         { "monitor",      "[off]",        monitor_options, cmd_monitor,
2404           "Monitor signals from interfaces", lookup_monitor },
2405         { "agent", "on|off",              NULL,            cmd_agent,
2406           "Agent mode", lookup_agent },
2407         {"vpnconnections", "[<connection>]", NULL,         cmd_vpnconnections,
2408          "Display VPN connections", NULL },
2409         { "vpnagent",     "on|off",     NULL,            cmd_vpnagent,
2410           "VPN Agent mode", lookup_agent },
2411         { "session",      "on|off|connect|disconnect|config", session_options,
2412           cmd_session, "Enable or disable a session", lookup_session },
2413         { "peer_service", "register|unregister <specs> <master>\n"
2414                           "Where specs are:\n"
2415                           "\tbjr_query <query> bjr_response <response>\n"
2416                           "\tupnp_service <service> upnp_version <version>\n"
2417                           "\twfd_ies <ies>\n", NULL,
2418           cmd_peer_service, "(Un)Register a Peer Service", NULL },
2419         { "help",         NULL,           NULL,            cmd_help,
2420           "Show help", NULL },
2421         { "exit",         NULL,           NULL,            cmd_exit,
2422           "Exit", NULL },
2423         { "quit",         NULL,           NULL,            cmd_exit,
2424           "Quit", NULL },
2425         {  NULL, },
2426 };
2427
2428 static int cmd_help(char *args[], int num, struct connman_option *options)
2429 {
2430         bool interactive = __connmanctl_is_interactive();
2431         int i, j;
2432
2433         if (interactive == false)
2434                 fprintf(stdout, "Usage: connmanctl [[command] [args]]\n");
2435
2436         for (i = 0; cmd_table[i].cmd; i++) {
2437                 const char *cmd = cmd_table[i].cmd;
2438                 const char *argument = cmd_table[i].argument;
2439                 const char *desc = cmd_table[i].desc;
2440
2441                 printf("%-16s%-22s%s\n", cmd? cmd: "",
2442                                 argument? argument: "",
2443                                 desc? desc: "");
2444
2445                 if (cmd_table[i].options) {
2446                         for (j = 0; cmd_table[i].options[j].name;
2447                              j++) {
2448                                 const char *options_desc =
2449                                         cmd_table[i].options[j].desc ?
2450                                         cmd_table[i].options[j].desc: "";
2451
2452                                 printf("   --%-16s%s\n",
2453                                                 cmd_table[i].options[j].name,
2454                                                 options_desc);
2455                         }
2456                 }
2457         }
2458
2459         if (interactive == false)
2460                 fprintf(stdout, "\nNote: arguments and output are considered "
2461                                 "EXPERIMENTAL for now.\n");
2462
2463         return 0;
2464 }
2465
2466 __connmanctl_lookup_cb __connmanctl_get_lookup_func(const char *text)
2467 {
2468         int i, cmdlen, textlen;
2469
2470         if (!text)
2471                 return NULL;
2472
2473         textlen = strlen(text);
2474
2475         for (i = 0; cmd_table[i].cmd; i++) {
2476                 cmdlen = strlen(cmd_table[i].cmd);
2477
2478                 if (textlen > cmdlen && text[cmdlen] != ' ')
2479                         continue;
2480
2481                 if (strncmp(cmd_table[i].cmd, text, cmdlen) == 0)
2482                         return cmd_table[i].cb;
2483         }
2484
2485         return NULL;
2486 }
2487
2488 int __connmanctl_commands(DBusConnection *dbus_conn, char *argv[], int argc)
2489 {
2490         int i, result;
2491
2492         connection = dbus_conn;
2493
2494         for (i = 0; cmd_table[i].cmd; i++) {
2495                 if (g_strcmp0(cmd_table[i].cmd, argv[0]) == 0 &&
2496                                 cmd_table[i].func) {
2497                         result = cmd_table[i].func(argv, argc,
2498                                         cmd_table[i].options);
2499                         if (result < 0 && result != -EINPROGRESS)
2500                                 fprintf(stderr, "Error '%s': %s\n", argv[0],
2501                                                 strerror(-result));
2502                         return result;
2503                 }
2504         }
2505
2506         fprintf(stderr, "Error '%s': Unknown command\n", argv[0]);
2507         return -EINVAL;
2508 }
2509
2510 char *__connmanctl_lookup_command(const char *text, int state)
2511 {
2512         static int i = 0;
2513         static int len = 0;
2514
2515         if (state == 0) {
2516                 i = 0;
2517                 len = strlen(text);
2518         }
2519
2520         while (cmd_table[i].cmd) {
2521                 const char *command = cmd_table[i].cmd;
2522
2523                 i++;
2524
2525                 if (strncmp(text, command, len) == 0)
2526                         return strdup(command);
2527         }
2528
2529         return NULL;
2530 }
2531
2532 static char *get_path(char *full_path)
2533 {
2534         char *path;
2535
2536         path = strrchr(full_path, '/');
2537         if (path && *path != '\0')
2538                 path++;
2539         else
2540                 path = full_path;
2541
2542         return path;
2543 }
2544
2545 static void add_service_id(const char *path)
2546 {
2547         g_hash_table_replace(service_hash, g_strdup(path),
2548                         GINT_TO_POINTER(TRUE));
2549 }
2550
2551 static void remove_service_id(const char *path)
2552 {
2553         g_hash_table_remove(service_hash, path);
2554 }
2555
2556 static void services_added(DBusMessageIter *iter)
2557 {
2558         DBusMessageIter array;
2559         char *path = NULL;
2560
2561         while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRUCT) {
2562
2563                 dbus_message_iter_recurse(iter, &array);
2564                 if (dbus_message_iter_get_arg_type(&array) !=
2565                                                 DBUS_TYPE_OBJECT_PATH)
2566                         return;
2567
2568                 dbus_message_iter_get_basic(&array, &path);
2569                 add_service_id(get_path(path));
2570
2571                 dbus_message_iter_next(iter);
2572         }
2573 }
2574
2575 static void update_services(DBusMessageIter *iter)
2576 {
2577         DBusMessageIter array;
2578         char *path;
2579
2580         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
2581                 return;
2582
2583         dbus_message_iter_recurse(iter, &array);
2584         services_added(&array);
2585
2586         dbus_message_iter_next(iter);
2587         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
2588                 return;
2589
2590         dbus_message_iter_recurse(iter, &array);
2591         while (dbus_message_iter_get_arg_type(&array) ==
2592                                                 DBUS_TYPE_OBJECT_PATH) {
2593                 dbus_message_iter_get_basic(&array, &path);
2594                 remove_service_id(get_path(path));
2595
2596                 dbus_message_iter_next(&array);
2597         }
2598 }
2599
2600 static int populate_service_hash(DBusMessageIter *iter, const char *error,
2601                                 void *user_data)
2602 {
2603         if (error) {
2604                 fprintf(stderr, "Error getting services: %s", error);
2605                 return 0;
2606         }
2607
2608         update_services(iter);
2609         return 0;
2610 }
2611
2612 static void add_peer_id(const char *path)
2613 {
2614         g_hash_table_replace(peer_hash, g_strdup(path), GINT_TO_POINTER(TRUE));
2615 }
2616
2617 static void remove_peer_id(const char *path)
2618 {
2619         g_hash_table_remove(peer_hash, path);
2620 }
2621
2622 static void peers_added(DBusMessageIter *iter)
2623 {
2624         DBusMessageIter array;
2625         char *path = NULL;
2626
2627         while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRUCT) {
2628
2629                 dbus_message_iter_recurse(iter, &array);
2630                 if (dbus_message_iter_get_arg_type(&array) !=
2631                                                 DBUS_TYPE_OBJECT_PATH)
2632                         return;
2633
2634                 dbus_message_iter_get_basic(&array, &path);
2635                 add_peer_id(get_path(path));
2636
2637                 dbus_message_iter_next(iter);
2638         }
2639 }
2640
2641 static void update_peers(DBusMessageIter *iter)
2642 {
2643         DBusMessageIter array;
2644         char *path;
2645
2646         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
2647                 return;
2648
2649         dbus_message_iter_recurse(iter, &array);
2650         peers_added(&array);
2651
2652         dbus_message_iter_next(iter);
2653         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
2654                 return;
2655
2656         dbus_message_iter_recurse(iter, &array);
2657         while (dbus_message_iter_get_arg_type(&array) ==
2658                                                 DBUS_TYPE_OBJECT_PATH) {
2659                 dbus_message_iter_get_basic(&array, &path);
2660                 remove_peer_id(get_path(path));
2661
2662                 dbus_message_iter_next(&array);
2663         }
2664 }
2665
2666 static int populate_peer_hash(DBusMessageIter *iter,
2667                                         const char *error, void *user_data)
2668 {
2669         if (error) {
2670                 fprintf(stderr, "Error getting peers: %s", error);
2671                 return 0;
2672         }
2673
2674         update_peers(iter);
2675         return 0;
2676 }
2677
2678 static void add_technology_id(const char *path)
2679 {
2680         g_hash_table_replace(technology_hash, g_strdup(path),
2681                         GINT_TO_POINTER(TRUE));
2682 }
2683
2684 static void remove_technology_id(const char *path)
2685 {
2686         g_hash_table_remove(technology_hash, path);
2687 }
2688
2689 static void remove_technology(DBusMessageIter *iter)
2690 {
2691         char *path = NULL;
2692
2693         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH)
2694                 return;
2695
2696         dbus_message_iter_get_basic(iter, &path);
2697         remove_technology_id(get_path(path));
2698 }
2699
2700 static void add_technology(DBusMessageIter *iter)
2701 {
2702         char *path = NULL;
2703
2704         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH)
2705                 return;
2706
2707         dbus_message_iter_get_basic(iter, &path);
2708         add_technology_id(get_path(path));
2709 }
2710
2711 static void update_technologies(DBusMessageIter *iter)
2712 {
2713         DBusMessageIter array;
2714
2715         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
2716                 return;
2717
2718         dbus_message_iter_recurse(iter, &array);
2719
2720         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
2721                 DBusMessageIter object_path;
2722
2723                 dbus_message_iter_recurse(&array, &object_path);
2724
2725                 add_technology(&object_path);
2726
2727                 dbus_message_iter_next(&array);
2728         }
2729 }
2730
2731 static int populate_technology_hash(DBusMessageIter *iter, const char *error,
2732                                 void *user_data)
2733 {
2734         if (error) {
2735                 fprintf(stderr, "Error getting technologies: %s", error);
2736                 return 0;
2737         }
2738
2739         update_technologies(iter);
2740
2741         return 0;
2742 }
2743
2744 static DBusHandlerResult monitor_completions_changed(
2745                 DBusConnection *connection,
2746                 DBusMessage *message, void *user_data)
2747 {
2748         bool *enabled = user_data;
2749         DBusMessageIter iter;
2750         DBusHandlerResult handled;
2751
2752         if (*enabled)
2753                 handled = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2754         else
2755                 handled = DBUS_HANDLER_RESULT_HANDLED;
2756
2757         if (dbus_message_is_signal(message, "net.connman.Manager",
2758                                         "ServicesChanged")) {
2759                 dbus_message_iter_init(message, &iter);
2760                 update_services(&iter);
2761                 return handled;
2762         }
2763
2764         if (dbus_message_is_signal(message, "net.connman.Manager",
2765                                                 "PeersChanged")) {
2766                 dbus_message_iter_init(message, &iter);
2767                 update_peers(&iter);
2768                 return handled;
2769         }
2770
2771         if (dbus_message_is_signal(message, "net.connman.Manager",
2772                                         "TechnologyAdded")) {
2773                 dbus_message_iter_init(message, &iter);
2774                 add_technology(&iter);
2775                 return handled;
2776         }
2777
2778         if (dbus_message_is_signal(message, "net.connman.Manager",
2779                                         "TechnologyRemoved")) {
2780                 dbus_message_iter_init(message, &iter);
2781                 remove_technology(&iter);
2782                 return handled;
2783         }
2784
2785         if (!g_strcmp0(dbus_message_get_interface(message),
2786                                         "net.connman.Manager"))
2787                 return handled;
2788
2789         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2790 }
2791
2792 void __connmanctl_monitor_completions(DBusConnection *dbus_conn)
2793 {
2794         bool *manager_enabled = NULL;
2795         DBusError err;
2796         int i;
2797
2798         for (i = 0; monitor[i].interface; i++) {
2799                 if (!strcmp(monitor[i].interface, "Manager")) {
2800                         manager_enabled = &monitor[i].enabled;
2801                         break;
2802                 }
2803         }
2804
2805         if (!dbus_conn) {
2806                 g_hash_table_destroy(service_hash);
2807                 g_hash_table_destroy(technology_hash);
2808
2809                 dbus_bus_remove_match(connection,
2810                         "type='signal',interface='net.connman.Manager'", NULL);
2811                 dbus_connection_remove_filter(connection,
2812                                         monitor_completions_changed,
2813                                         manager_enabled);
2814                 return;
2815         }
2816
2817         connection = dbus_conn;
2818
2819         service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
2820                                                                 g_free, NULL);
2821
2822         peer_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
2823                                                                 g_free, NULL);
2824
2825         technology_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
2826                                                                 g_free, NULL);
2827
2828         __connmanctl_dbus_method_call(connection,
2829                                 CONNMAN_SERVICE, CONNMAN_PATH,
2830                                 "net.connman.Manager", "GetServices",
2831                                 populate_service_hash, NULL, NULL, NULL);
2832
2833         __connmanctl_dbus_method_call(connection,
2834                                 CONNMAN_SERVICE, CONNMAN_PATH,
2835                                 "net.connman.Manager", "GetPeers",
2836                                 populate_peer_hash, NULL, NULL, NULL);
2837
2838         __connmanctl_dbus_method_call(connection,
2839                                 CONNMAN_SERVICE, CONNMAN_PATH,
2840                                 "net.connman.Manager", "GetTechnologies",
2841                                 populate_technology_hash, NULL, NULL, NULL);
2842
2843         dbus_connection_add_filter(connection,
2844                                 monitor_completions_changed, manager_enabled,
2845                         NULL);
2846
2847         dbus_error_init(&err);
2848         dbus_bus_add_match(connection,
2849                         "type='signal',interface='net.connman.Manager'", &err);
2850
2851         if (dbus_error_is_set(&err))
2852                 fprintf(stderr, "Error: %s\n", err.message);
2853 }