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