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