client: Add boolean parsing helper function
[platform/upstream/connman.git] / client / services.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012  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 <stdint.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <errno.h>
33
34 #include <glib.h>
35
36 #include "services.h"
37 #include "dbus.h"
38
39 int parse_boolean(char *arg)
40 {
41         if (arg == NULL)
42                 return -1;
43
44         if (strcasecmp(arg, "no") == 0 ||
45                         strcasecmp(arg, "false") == 0 ||
46                         strcasecmp(arg, "off" ) == 0 ||
47                         strcasecmp(arg, "disable" ) == 0 ||
48                         strcasecmp(arg, "n") == 0 ||
49                         strcasecmp(arg, "f") == 0 ||
50                         strcasecmp(arg, "0") == 0)
51                 return 0;
52
53         if (strcasecmp(arg, "yes") == 0 ||
54                         strcasecmp(arg, "true") == 0 ||
55                         strcasecmp(arg, "on") == 0 ||
56                         strcasecmp(arg, "enable" ) == 0 ||
57                         strcasecmp(arg, "y") == 0 ||
58                         strcasecmp(arg, "t") == 0 ||
59                         strcasecmp(arg, "1") == 0)
60                 return 1;
61
62         return -1;
63 }
64
65 static void append_property_array(DBusMessageIter *iter, char *property,
66                                                 char **data, int num_args)
67 {
68         DBusMessageIter value, array;
69         int i;
70
71         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &property);
72
73         dbus_array_open(iter, &value);
74         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
75                                          DBUS_TYPE_STRING_AS_STRING, &array);
76
77         for (i = 0; i < num_args; i++) {
78                 dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
79                                                 &data[i]);
80                 printf("Added: %s\n", data[i]);
81         }
82         dbus_message_iter_close_container(&value, &array);
83         dbus_message_iter_close_container(iter, &value);
84 }
85
86 static void append_property_dict(DBusMessageIter *iter, char *property,
87                                         char **keys, char **data, int num_args)
88 {
89         DBusMessageIter value, dict, entry, dict_key;
90         int i;
91         unsigned char prefix;
92
93         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &property);
94
95         /* Top most level is a{sv} */
96         dbus_dict_open_variant(iter, &value);
97
98         dbus_dict_open(&value, &dict);
99
100         for (i = 0; i < num_args; i++) {
101                 dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
102                                                         NULL, &entry);
103                 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
104                                                         &keys[i]);
105
106                 if (strcmp(property, "IPv6.Configuration") == 0 &&
107                                            g_strcmp0(keys[i], "PrefixLength")) {
108                         if (data[i] == NULL) {
109                                 fprintf(stderr, "No values entered!\n");
110                                 exit(EXIT_FAILURE);
111                         }
112                         prefix = atoi(data[i]);
113
114                         dbus_message_iter_open_container(&entry,
115                                                 DBUS_TYPE_VARIANT,
116                                                 DBUS_TYPE_BYTE_AS_STRING,
117                                                 &dict_key);
118                         dbus_message_iter_append_basic(&dict_key,
119                                                        DBUS_TYPE_BYTE, &prefix);
120                 } else {
121                         dbus_message_iter_open_container(&entry,
122                                                  DBUS_TYPE_VARIANT,
123                                                  DBUS_TYPE_STRING_AS_STRING,
124                                                  &dict_key);
125                         dbus_message_iter_append_basic(&dict_key,
126                                                         DBUS_TYPE_STRING,
127                                                         &data[i]);
128                 }
129                 dbus_message_iter_close_container(&entry, &dict_key);
130                 dbus_message_iter_close_container(&dict, &entry);
131         }
132         /* Close {sv}, then close a{sv} */
133         dbus_dict_close(&value, &dict);
134         dbus_dict_close(iter, &value);
135 }
136
137 void iterate_array(DBusMessageIter *iter)
138 {
139         DBusMessageIter array_item;
140         dbus_bool_t key_bool;
141         char *key_str;
142
143         dbus_message_iter_recurse(iter, &array_item);
144         /* Make sure the entry is not NULL! */
145         printf("[ ");
146         while (dbus_message_iter_get_arg_type(&array_item) !=
147                                         DBUS_TYPE_INVALID) {
148                 if (dbus_message_iter_get_arg_type(&array_item) ==
149                                         DBUS_TYPE_STRING) {
150                         dbus_message_iter_get_basic(&array_item,
151                                                 &key_str);
152                         printf("%s ", key_str);
153                 } else if (dbus_message_iter_get_arg_type(&array_item) ==
154                                         DBUS_TYPE_BOOLEAN) {
155                         dbus_message_iter_get_basic(&array_item, &key_bool);
156                         printf("%s ", key_bool == TRUE ? "True"
157                                                 : "False");
158                 }
159                 dbus_message_iter_next(&array_item);
160         }
161         if (dbus_message_iter_get_arg_type(&array_item) ==
162                                         DBUS_TYPE_INVALID)
163                 printf("] ");
164 }
165
166 void iterate_dict(DBusMessageIter *dict, char *string, uint16_t key_int)
167 {
168         DBusMessageIter dict_entry, sub_dict_entry;
169
170         printf("{ ");
171         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
172                 dbus_message_iter_recurse(dict, &dict_entry);
173                 dbus_message_iter_get_basic(&dict_entry, &string);
174                 printf("%s=", string);
175                 dbus_message_iter_next(&dict_entry);
176                 while (dbus_message_iter_get_arg_type(&dict_entry)
177                                                         != DBUS_TYPE_INVALID) {
178                         dbus_message_iter_recurse(&dict_entry, &sub_dict_entry);
179                         if (dbus_message_iter_get_arg_type(&sub_dict_entry)
180                                                         == DBUS_TYPE_UINT16) {
181                                 dbus_message_iter_get_basic(&sub_dict_entry,
182                                                                 &key_int);
183                                 printf("%d ", key_int);
184                         } else if (dbus_message_iter_get_arg_type(&sub_dict_entry)
185                                                         == DBUS_TYPE_STRING) {
186                                 dbus_message_iter_get_basic(&sub_dict_entry,
187                                                                 &string);
188                                 printf("%s ", string);
189                         } else if (dbus_message_iter_get_arg_type(&sub_dict_entry)
190                                                         == DBUS_TYPE_ARRAY) {
191                                 iterate_array(&sub_dict_entry);
192                         }
193                         dbus_message_iter_next(&dict_entry);
194                 }
195                 dbus_message_iter_next(dict);
196         }
197         printf("}");
198 }
199
200 /* Get dictionary info about the current service and store it */
201 static void extract_service_properties(DBusMessageIter *dict,
202                                 struct service_data *service)
203 {
204         DBusMessageIter entry, value, array_item;
205         char *key;
206         char *key_str;
207         uint16_t key_uint16;
208         dbus_bool_t key_bool;
209
210         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
211                 dbus_message_iter_recurse(dict, &entry);
212                 dbus_message_iter_get_basic(&entry, &key);
213                 printf("\n  %s = ", key);
214                 if (strcmp(key, "Name") == 0 && strlen(key) < 5)
215                         service->name = key;
216
217                 dbus_message_iter_next(&entry);
218                 dbus_message_iter_recurse(&entry, &value);
219                 /* Check if entry is a dictionary itself */
220                 if (strcmp(key, "Ethernet") == 0 ||
221                         /* if just strcmp, the .Configuration names don't match
222                          * and they are iterated with iterate_array instead*/
223                                 strncmp(key, "IPv4", 4) == 0 ||
224                                 strncmp(key, "IPv6", 4) == 0 ||
225                                 strncmp(key, "Proxy", 5) == 0 ||
226                                 strcmp(key, "Provider") == 0) {
227                         dbus_message_iter_recurse(&value, &array_item);
228                         iterate_dict(&array_item, key_str, key_uint16);
229                 } else
230                 switch (dbus_message_iter_get_arg_type(&value)) {
231                 case DBUS_TYPE_ARRAY:
232                         iterate_array(&value);
233                         break;
234                 case DBUS_TYPE_BOOLEAN:
235                         dbus_message_iter_get_basic(&value, &key_bool);
236                         printf("%s", key_bool == TRUE ? "True" : "False");
237                         break;
238                 case DBUS_TYPE_BYTE:
239                         dbus_message_iter_get_basic(&value, &key_uint16);
240                         printf("%d", key_uint16);
241                         break;
242                 case DBUS_TYPE_STRING:
243                         dbus_message_iter_get_basic(&value, &key_str);
244                         printf("%s", key_str);
245                         break;
246                 }
247                 dbus_message_iter_next(dict);
248         }
249         printf("\n\n");
250 }
251
252 static void match_service_name(DBusMessage *message, char *service_name,
253                                                 struct service_data *service)
254 {
255         DBusMessageIter iter, array;
256
257         dbus_message_iter_init(message, &iter);
258         dbus_message_iter_recurse(&iter, &array);
259
260         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
261                 DBusMessageIter entry, dict;
262                 char *path;
263
264                 dbus_message_iter_recurse(&array, &entry);
265                 dbus_message_iter_get_basic(&entry, &path);
266
267                 service->path = strip_service_path(path);
268                 dbus_message_iter_next(&entry);
269                 dbus_message_iter_recurse(&entry, &dict);
270                 extract_service_name(&dict, service);
271                 if (g_strcmp0(service_name, service->name) == 0) {
272                         printf("    Matched %s with %s\n\n", service->name,
273                                                                 service->path);
274                         break;
275                 }
276                 dbus_message_iter_next(&array);
277         }
278 }
279
280 void extract_service_name(DBusMessageIter *dict, struct service_data *service)
281 {
282         DBusMessageIter dict_entry, value;
283         const char *key;
284         const char *state;
285
286         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
287                 dbus_message_iter_recurse(dict, &dict_entry);
288                 dbus_message_iter_get_basic(&dict_entry, &key);
289                 if (strcmp(key, "Name") == 0) {
290                         dbus_message_iter_next(&dict_entry);
291                         dbus_message_iter_recurse(&dict_entry, &value);
292                         dbus_message_iter_get_basic(&value, &service->name);
293                 }
294                 if (strcmp(key, "AutoConnect") == 0) {
295                         dbus_message_iter_next(&dict_entry);
296                         dbus_message_iter_recurse(&dict_entry, &value);
297                         dbus_message_iter_get_basic(&value, &service->autoconn);
298                 }
299                 if (strcmp(key, "State") == 0) {
300                         dbus_message_iter_next(&dict_entry);
301                         dbus_message_iter_recurse(&dict_entry, &value);
302                         dbus_message_iter_get_basic(&value, &state);
303                         if (strcmp(state, "ready") == 0) {
304                                 service->connected = TRUE;
305                                 service->online = FALSE;
306                         } else if (strcmp(state, "online") == 0) {
307                                 service->connected = FALSE;
308                                 service->online = TRUE;
309                         } else {
310                                 service->connected = FALSE;
311                                 service->online = FALSE;
312                         }
313                 }
314                 if (strcmp(key, "Favorite") == 0) {
315                         dbus_message_iter_next(&dict_entry);
316                         dbus_message_iter_recurse(&dict_entry, &value);
317                         dbus_message_iter_get_basic(&value, &service->favorite);
318                 }
319                 dbus_message_iter_next(dict);
320         }
321 }
322
323 /* Show detailed information about a service */
324 void extract_services(DBusMessage *message, char *service_name)
325 {
326         DBusMessageIter iter, array;
327
328         dbus_message_iter_init(message, &iter);
329         dbus_message_iter_recurse(&iter, &array);
330
331         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
332                 DBusMessageIter entry, dict;
333                 struct service_data service;
334                 char *path;
335
336                 dbus_message_iter_recurse(&array, &entry);
337                 dbus_message_iter_get_basic(&entry, &path);
338
339                 service.path = strip_service_path(path);
340                 if (g_strcmp0(service.path, service_name) == 0) {
341                         printf("[ %s ]\n", service.path);
342                         dbus_message_iter_next(&entry);
343                         dbus_message_iter_recurse(&entry, &dict);
344                         extract_service_properties(&dict, &service);
345                 }
346                 dbus_message_iter_next(&array);
347         }
348 }
349
350 /* Support both string names and path names for connecting to services */
351 char *strip_service_path(char *service)
352 {
353         char *service_name;
354         service_name = strrchr(service, '/');
355         if (service_name == NULL)
356                 return service;
357         else
358                 return service_name + 1;
359 }
360
361 /* Show a simple list of service names only */
362 void get_services(DBusMessage *message)
363 {
364         DBusMessageIter iter, array;
365
366         dbus_message_iter_init(message, &iter);
367         dbus_message_iter_recurse(&iter, &array);
368
369         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
370                 DBusMessageIter entry, dict;
371                 struct service_data service;
372                 char *path;
373
374                 dbus_message_iter_recurse(&array, &entry);
375                 dbus_message_iter_get_basic(&entry, &path);
376
377                 service.path = strip_service_path(path);
378                 dbus_message_iter_next(&entry);
379                 dbus_message_iter_recurse(&entry, &dict);
380                 extract_service_name(&dict, &service);
381                 printf("%-1s%-1s%-1s %-20s { %s }\n",
382                         service.favorite ? "*" : "",
383                         service.autoconn ? "A" : "",
384                         service.online ? "O" : (service.connected ? "R" : ""),
385                         service.name, service.path);
386                 dbus_message_iter_next(&array);
387         }
388 }
389
390 const char *find_service(DBusConnection *connection, DBusMessage *message,
391                           char *service_name, struct service_data *service)
392 {
393         DBusMessageIter iter, array, entry;
394         char *path;
395
396         service_name = strip_service_path(service_name);
397         match_service_name(message, service_name, service);
398         /* Service name did not match, so check if entry is a path */
399         if (g_strcmp0(service_name, service->name)) {
400                 dbus_message_iter_init(message, &iter);
401                 dbus_message_iter_recurse(&iter, &array);
402
403                 while (dbus_message_iter_get_arg_type(&array) ==
404                                                         DBUS_TYPE_STRUCT) {
405                         dbus_message_iter_recurse(&array, &entry);
406                         dbus_message_iter_get_basic(&entry, &path);
407
408                         service->path = strip_service_path(path);
409                         if (g_strcmp0(service->path, service_name) == 0)
410                                 return service->path;
411                         dbus_message_iter_next(&array);
412                 }
413                 fprintf(stderr, "'%s' is not a valid service name or path.\n",
414                                                                 service_name);
415                 fprintf(stderr, "Use the 'services' command to find available "
416                                                         "services.\n");
417                 return NULL;
418         } else
419                 return service->path;
420 }
421
422 int set_proxy_manual(DBusConnection *connection, DBusMessage *message,
423                                 char *name, char **servers, char **excludes,
424                                 int num_servers, int num_excludes)
425 {
426         DBusMessage *message_send;
427         DBusMessageIter iter, value, dict, entry, data;
428         struct service_data service;
429         char *path;
430         const char *path_name;
431         char *property = "Proxy.Configuration";
432         char *method = "Method";
433         char *manual = "manual";
434
435         path_name = find_service(connection, message, name, &service);
436         if (path_name == NULL)
437                 return -ENXIO;
438
439         path = g_strdup_printf("/net/connman/service/%s", path_name);
440         message_send = dbus_message_new_method_call("net.connman", path,
441                                                         "net.connman.Service",
442                                                         "SetProperty");
443
444         if (message_send == NULL) {
445                 g_free(path);
446                 return -ENOMEM;
447         }
448
449         dbus_message_iter_init_append(message_send, &iter);
450         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
451         dbus_dict_open_variant(&iter, &value);
452         dbus_dict_open(&value, &dict);
453         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
454                                                         &entry);
455         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &method);
456         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
457                                                 DBUS_TYPE_STRING_AS_STRING,
458                                                 &data);
459         dbus_message_iter_append_basic(&data, DBUS_TYPE_STRING, &manual);
460         dbus_message_iter_close_container(&entry, &data);
461         dbus_message_iter_close_container(&dict, &entry);
462         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
463                                                         &entry);
464         append_property_array(&entry, "Servers", servers, num_servers);
465         dbus_message_iter_close_container(&dict, &entry);
466
467         if (num_excludes != 0) {
468                 dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
469                                                         NULL, &entry);
470                 append_property_array(&entry, "Excludes", excludes,
471                                                         num_excludes);
472                 dbus_message_iter_close_container(&dict, &entry);
473         }
474
475         dbus_message_iter_close_container(&value, &dict);
476         dbus_message_iter_close_container(&iter, &value);
477         dbus_connection_send(connection, message_send, NULL);
478         dbus_connection_flush(connection);
479         dbus_message_unref(message_send);
480
481         g_free(path);
482
483         return 0;
484 }
485
486 int set_service_property(DBusConnection *connection, DBusMessage *message,
487                                 char *name, char *property, char **keys,
488                                 void *data, int num_args)
489 {
490         DBusMessage *message_send;
491         DBusMessageIter iter;
492         struct service_data service;
493         char *path;
494         const char *path_name;
495
496         path_name = find_service(connection, message, name, &service);
497         if (path_name == NULL)
498                 return -ENXIO;
499
500         path = g_strdup_printf("/net/connman/service/%s", path_name);
501         message_send = dbus_message_new_method_call("net.connman", path,
502                                                         "net.connman.Service",
503                                                         "SetProperty");
504
505         if (message_send == NULL) {
506                 g_free(path);
507                 return -ENOMEM;
508         }
509
510         dbus_message_iter_init_append(message_send, &iter);
511
512         if (strcmp(property, "AutoConnect") == 0)
513                 dbus_property_append_basic(&iter, (const char *) property,
514                                                 DBUS_TYPE_BOOLEAN, data);
515         else if ((strcmp(property, "Domains.Configuration") == 0)
516                         || (strcmp(property, "Timeservers.Configuration") == 0)
517                         || (strcmp(property, "Nameservers.Configuration") == 0))
518                 append_property_array(&iter, property, data, num_args);
519         else if ((strcmp(property, "IPv4.Configuration") == 0)
520                         || (strcmp(property, "IPv6.Configuration") == 0)
521                         || (strcmp(property, "Proxy.Configuration") == 0))
522                 append_property_dict(&iter, property, keys, data, num_args);
523
524         dbus_connection_send(connection, message_send, NULL);
525         dbus_connection_flush(connection);
526         dbus_message_unref(message_send);
527         g_free(path);
528
529         return 0;
530 }
531
532 int remove_service(DBusConnection *connection, DBusMessage *message,
533                                                                 char *name)
534 {
535         struct service_data service;
536         DBusMessage *message_send;
537         const char *path_name;
538         char *path;
539
540         path_name = find_service(connection, message, name, &service);
541         if (path_name == NULL)
542                 return -ENXIO;
543
544         if (service.favorite == FALSE)
545                 return 0;
546
547         path = g_strdup_printf("/net/connman/service/%s", path_name);
548         message_send = dbus_message_new_method_call(CONNMAN_SERVICE, path,
549                                                 CONNMAN_SERVICE_INTERFACE,
550                                                 "Remove");
551         if (message_send == NULL) {
552                 g_free(path);
553                 return -ENOMEM;
554         }
555
556         dbus_connection_send(connection, message_send, NULL);
557         dbus_message_unref(message_send);
558         g_free(path);
559
560         return 0;
561 }