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