client: Quick fix for parsing IPv6.Configuration correctly
[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         struct service_data service;
462         char *path;
463         const char *path_name;
464         char *property = "Proxy.Configuration";
465         char *method = "Method";
466         char *manual = "manual";
467
468         path_name = find_service(connection, message, name, &service);
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         dbus_connection_send(connection, message_send, NULL);
511         dbus_connection_flush(connection);
512         dbus_message_unref(message_send);
513
514         g_free(path);
515
516         return num_servers + num_excludes;
517 }
518
519 int set_service_property(DBusConnection *connection, DBusMessage *message,
520                                 char *name, char *property, char **keys,
521                                 void *data, int num_args)
522 {
523         int num_props = 1;
524         DBusMessage *message_send;
525         DBusMessageIter iter;
526         struct service_data service;
527         char *path;
528         const char *path_name;
529
530         path_name = find_service(connection, message, name, &service);
531         if (path_name == NULL)
532                 return -ENXIO;
533
534         path = g_strdup_printf("/net/connman/service/%s", path_name);
535         message_send = dbus_message_new_method_call("net.connman", path,
536                                                         "net.connman.Service",
537                                                         "SetProperty");
538
539         if (message_send == NULL) {
540                 g_free(path);
541                 return -ENOMEM;
542         }
543
544         dbus_message_iter_init_append(message_send, &iter);
545
546         if (strcmp(property, "AutoConnect") == 0)
547                 dbus_property_append_basic(&iter, (const char *) property,
548                                                 DBUS_TYPE_BOOLEAN, data);
549         else if ((strcmp(property, "Domains.Configuration") == 0)
550                         || (strcmp(property, "Timeservers.Configuration") == 0)
551                         || (strcmp(property, "Nameservers.Configuration") == 0))
552                 num_props = append_property_array(&iter, property, data,
553                                 num_args);
554         else if ((strcmp(property, "IPv4.Configuration") == 0)
555                         || (strcmp(property, "IPv6.Configuration") == 0)
556                         || (strcmp(property, "Proxy.Configuration") == 0))
557                 num_props = append_property_dict(&iter, property, keys, data,
558                                 num_args);
559
560         dbus_connection_send(connection, message_send, NULL);
561         dbus_connection_flush(connection);
562         dbus_message_unref(message_send);
563         g_free(path);
564
565         return num_props;
566 }
567
568 int remove_service(DBusConnection *connection, DBusMessage *message,
569                                                                 char *name)
570 {
571         struct service_data service;
572         DBusMessage *message_send;
573         const char *path_name;
574         char *path;
575
576         path_name = find_service(connection, message, name, &service);
577         if (path_name == NULL)
578                 return -ENXIO;
579
580         if (service.favorite == FALSE)
581                 return 0;
582
583         path = g_strdup_printf("/net/connman/service/%s", path_name);
584         message_send = dbus_message_new_method_call(CONNMAN_SERVICE, path,
585                                                 CONNMAN_SERVICE_INTERFACE,
586                                                 "Remove");
587         if (message_send == NULL) {
588                 g_free(path);
589                 return -ENOMEM;
590         }
591
592         dbus_connection_send(connection, message_send, NULL);
593         dbus_message_unref(message_send);
594         g_free(path);
595
596         return 0;
597 }