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