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