Add error checking for service methods
[framework/connectivity/connman.git] / src / profile.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  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 version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <glib.h>
27 #include <gdbus.h>
28
29 #include "connman.h"
30
31 #define PROFILE_DEFAULT  "/profile/default"
32
33 enum connman_service_type {
34         CONNMAN_SERVICE_TYPE_UNKNOWN  = 0,
35         CONNMAN_SERVICE_TYPE_ETHERNET = 1,
36         CONNMAN_SERVICE_TYPE_WIFI     = 2,
37         CONNMAN_SERVICE_TYPE_WIMAX    = 3,
38 };
39
40 enum connman_service_state {
41         CONNMAN_SERVICE_STATE_UNKNOWN = 0,
42         CONNMAN_SERVICE_STATE_IDLE    = 1,
43 };
44
45 struct connman_group {
46         GSequenceIter *iter;
47         char *id;
48         char *path;
49         char *name;
50         char *mode;
51         char *security;
52         connman_uint8_t strength;
53         connman_bool_t favorite;
54         enum connman_service_type type;
55         enum connman_service_state state;
56         struct connman_network *network;
57 };
58
59 static GSequence *groups = NULL;
60
61 static DBusConnection *connection = NULL;
62
63 static const char *type2string(enum connman_service_type type)
64 {
65         switch (type) {
66         case CONNMAN_SERVICE_TYPE_UNKNOWN:
67                 break;
68         case CONNMAN_SERVICE_TYPE_ETHERNET:
69                 return "ethernet";
70         case CONNMAN_SERVICE_TYPE_WIFI:
71                 return "wifi";
72         case CONNMAN_SERVICE_TYPE_WIMAX:
73                 return "wimax";
74         }
75
76         return NULL;
77 }
78
79 static const char *state2string(enum connman_service_state state)
80 {
81         switch (state) {
82         case CONNMAN_SERVICE_STATE_UNKNOWN:
83                 break;
84         case CONNMAN_SERVICE_STATE_IDLE:
85                 return "idle";
86         }
87
88         return NULL;
89 }
90
91 static DBusMessage *get_properties(DBusConnection *conn,
92                                         DBusMessage *msg, void *data)
93 {
94         struct connman_group *group = data;
95         DBusMessage *reply;
96         DBusMessageIter array, dict;
97         const char *str;
98
99         DBG("conn %p", conn);
100
101         reply = dbus_message_new_method_return(msg);
102         if (reply == NULL)
103                 return NULL;
104
105         dbus_message_iter_init_append(reply, &array);
106
107         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
108                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
109                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
110                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
111
112         str = type2string(group->type);
113         if (str != NULL)
114                 connman_dbus_dict_append_variant(&dict, "Type",
115                                                 DBUS_TYPE_STRING, &str);
116
117         str = state2string(group->state);
118         if (str != NULL)
119                 connman_dbus_dict_append_variant(&dict, "State",
120                                                 DBUS_TYPE_STRING, &str);
121
122         if (group->name != NULL)
123                 connman_dbus_dict_append_variant(&dict, "Name",
124                                         DBUS_TYPE_STRING, &group->name);
125
126         if (group->mode != NULL)
127                 connman_dbus_dict_append_variant(&dict, "Mode",
128                                         DBUS_TYPE_STRING, &group->mode);
129
130         if (group->security != NULL)
131                 connman_dbus_dict_append_variant(&dict, "Security",
132                                         DBUS_TYPE_STRING, &group->security);
133
134         if (group->strength > 0)
135                 connman_dbus_dict_append_variant(&dict, "Strength",
136                                         DBUS_TYPE_BYTE, &group->strength);
137
138         connman_dbus_dict_append_variant(&dict, "Favorite",
139                                         DBUS_TYPE_BOOLEAN, &group->favorite);
140
141         dbus_message_iter_close_container(&array, &dict);
142
143         return reply;
144 }
145
146 static DBusMessage *connect_service(DBusConnection *conn,
147                                         DBusMessage *msg, void *data)
148 {
149         struct connman_group *group = data;
150
151         if (group->type == CONNMAN_SERVICE_TYPE_ETHERNET)
152                 return __connman_error_not_supported(msg);
153
154         return __connman_error_not_implemented(msg);
155 }
156
157 static DBusMessage *disconnect_service(DBusConnection *conn,
158                                         DBusMessage *msg, void *data)
159 {
160         struct connman_group *group = data;
161
162         if (group->type == CONNMAN_SERVICE_TYPE_ETHERNET)
163                 return __connman_error_not_supported(msg);
164
165         return __connman_error_not_implemented(msg);
166 }
167
168 static DBusMessage *remove_service(DBusConnection *conn,
169                                         DBusMessage *msg, void *data)
170 {
171         struct connman_group *group = data;
172
173         if (group->type == CONNMAN_SERVICE_TYPE_ETHERNET)
174                 return __connman_error_not_supported(msg);
175
176         group->favorite = FALSE;
177
178         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
179 }
180
181 static DBusMessage *move_before(DBusConnection *conn,
182                                         DBusMessage *msg, void *data)
183 {
184         struct connman_group *group = data;
185
186         if (group->favorite == FALSE)
187                 return __connman_error_not_supported(msg);
188
189         return __connman_error_not_implemented(msg);
190 }
191
192 static DBusMessage *move_after(DBusConnection *conn,
193                                         DBusMessage *msg, void *data)
194 {
195         struct connman_group *group = data;
196
197         if (group->favorite == FALSE)
198                 return __connman_error_not_supported(msg);
199
200         return __connman_error_not_implemented(msg);
201 }
202
203 static GDBusMethodTable service_methods[] = {
204         { "GetProperties", "",  "a{sv}", get_properties     },
205         { "Connect",       "",  "",      connect_service    },
206         { "Disconnect",    "",  "",      disconnect_service },
207         { "Remove",        "",  "",      remove_service     },
208         { "MoveBefore",    "o", "",      move_before        },
209         { "MoveAfter",     "o", "",      move_after         },
210         { },
211 };
212
213 static GDBusSignalTable service_signals[] = {
214         { "PropertyChanged", "sv" },
215         { },
216 };
217
218 const char *__connman_profile_active(void)
219 {
220         DBG("");
221
222         return PROFILE_DEFAULT;
223 }
224
225 static void append_path(gpointer value, gpointer user_data)
226 {
227         struct connman_group *group = value;
228         DBusMessageIter *iter = user_data;
229
230         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
231                                                         &group->path);
232 }
233
234 void __connman_profile_list_services(DBusMessageIter *iter)
235 {
236         DBG("");
237
238         g_sequence_foreach(groups, append_path, iter);
239 }
240
241 static void append_services(DBusMessageIter *entry)
242 {
243         DBusMessageIter value, iter;
244         const char *key = "Services";
245
246         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
247
248         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
249                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
250                                                                 &value);
251
252         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
253                                 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
254         __connman_profile_list_services(&iter);
255         dbus_message_iter_close_container(&value, &iter);
256
257         dbus_message_iter_close_container(entry, &value);
258 }
259
260 static void emit_services_signal(void)
261 {
262         const char *path = __connman_profile_active();
263         DBusMessage *signal;
264         DBusMessageIter entry;
265
266         signal = dbus_message_new_signal(path,
267                                 CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
268         if (signal == NULL)
269                 return;
270
271         dbus_message_iter_init_append(signal, &entry);
272         append_services(&entry);
273         g_dbus_send_message(connection, signal);
274
275         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
276                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
277         if (signal == NULL)
278                 return;
279
280         dbus_message_iter_init_append(signal, &entry);
281         append_services(&entry);
282         g_dbus_send_message(connection, signal);
283 }
284
285 static void free_group(gpointer data)
286 {
287         struct connman_group *group = data;
288
289         DBG("group %p", group);
290
291         g_dbus_unregister_interface(connection, group->path,
292                                                 CONNMAN_SERVICE_INTERFACE);
293
294         g_free(group->security);
295         g_free(group->mode);
296         g_free(group->name);
297         g_free(group->path);
298         g_free(group->id);
299         g_free(group);
300 }
301
302 static gint compare_group(gconstpointer a, gconstpointer b, gpointer user_data)
303 {
304         struct connman_group *group_a = (void *) a;
305         struct connman_group *group_b = (void *) b;
306
307         if (group_a->favorite == TRUE && group_b->favorite == FALSE)
308                 return -1;
309
310         if (group_a->favorite == FALSE && group_b->favorite == TRUE)
311                 return 1;
312
313         return (gint) group_b->strength - (gint) group_a->strength;
314 }
315
316 static gint search_group(gconstpointer a, gconstpointer b, gpointer user_data)
317 {
318         struct connman_group *group = (void *) a;
319
320         return g_strcmp0(group->id, user_data);
321 }
322
323 static struct connman_group *lookup_group(const char *name)
324 {
325         GSequenceIter *iter;
326         struct connman_group *group;
327
328         DBG("name %s", name);
329
330         if (name == NULL)
331                 return NULL;
332
333         iter = g_sequence_search(groups, NULL, search_group, (char *) name);
334         if (g_sequence_iter_is_begin(iter) == FALSE &&
335                                 g_sequence_iter_is_end(iter) == FALSE) {
336                 group = g_sequence_get(iter);
337                 if (group != NULL)
338                         goto done;
339         }
340
341         group = g_try_new0(struct connman_group, 1);
342         if (group == NULL)
343                 return NULL;
344
345         group->id = g_strdup(name);
346
347         group->type = CONNMAN_SERVICE_TYPE_UNKNOWN;
348         group->path = g_strdup_printf("%s/%s", PROFILE_DEFAULT, name);
349
350         group->favorite = FALSE;
351
352         group->state = CONNMAN_SERVICE_STATE_IDLE;
353
354         group->iter = g_sequence_insert_sorted(groups, group,
355                                                 compare_group, NULL);
356
357         g_dbus_register_interface(connection, group->path,
358                                         CONNMAN_SERVICE_INTERFACE,
359                                         service_methods, service_signals,
360                                                         NULL, group, NULL);
361
362 done:
363         DBG("group %p", group);
364
365         return group;
366 }
367
368 static enum connman_service_type convert_device_type(struct connman_device *device)
369 {
370         enum connman_device_type type = connman_device_get_type(device);
371
372         switch (type) {
373         case CONNMAN_DEVICE_TYPE_UNKNOWN:
374         case CONNMAN_DEVICE_TYPE_VENDOR:
375         case CONNMAN_DEVICE_TYPE_WIFI:
376         case CONNMAN_DEVICE_TYPE_WIMAX:
377         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
378         case CONNMAN_DEVICE_TYPE_GPS:
379         case CONNMAN_DEVICE_TYPE_HSO:
380         case CONNMAN_DEVICE_TYPE_NOZOMI:
381         case CONNMAN_DEVICE_TYPE_HUAWEI:
382         case CONNMAN_DEVICE_TYPE_NOVATEL:
383                 break;
384         case CONNMAN_DEVICE_TYPE_ETHERNET:
385                 return CONNMAN_SERVICE_TYPE_ETHERNET;
386         }
387
388         return CONNMAN_SERVICE_TYPE_UNKNOWN;
389 }
390
391 int __connman_profile_add_device(struct connman_device *device)
392 {
393         struct connman_group *group;
394         char *name;
395
396         DBG("device %p", device);
397
398         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
399                                         connman_device_get_index(device));
400         group = lookup_group(name);
401         g_free(name);
402
403         if (group == NULL)
404                 return -EINVAL;
405
406         group->type = convert_device_type(device);
407
408         g_sequence_sort_changed(group->iter, compare_group, NULL);
409         emit_services_signal();
410
411         return 0;
412 }
413
414 int __connman_profile_remove_device(struct connman_device *device)
415 {
416         struct connman_group *group;
417         char *name;
418
419         DBG("device %p", device);
420
421         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
422                                         connman_device_get_index(device));
423         group = lookup_group(name);
424         g_free(name);
425
426         if (group == NULL)
427                 return -EINVAL;
428
429         group->type = CONNMAN_SERVICE_TYPE_UNKNOWN;
430
431         g_sequence_sort_changed(group->iter, compare_group, NULL);
432         emit_services_signal();
433
434         return 0;
435 }
436
437 static enum connman_service_type convert_network_type(struct connman_network *network)
438 {
439         enum connman_network_type type = connman_network_get_type(network);
440
441         switch (type) {
442         case CONNMAN_NETWORK_TYPE_UNKNOWN:
443         case CONNMAN_NETWORK_TYPE_VENDOR:
444         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
445         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
446         case CONNMAN_NETWORK_TYPE_HSO:
447                 break;
448         case CONNMAN_NETWORK_TYPE_WIFI:
449                 return CONNMAN_SERVICE_TYPE_WIFI;
450         case CONNMAN_NETWORK_TYPE_WIMAX:
451                 return CONNMAN_SERVICE_TYPE_WIMAX;
452         }
453
454         return CONNMAN_SERVICE_TYPE_UNKNOWN;
455 }
456
457 int __connman_profile_add_network(struct connman_network *network)
458 {
459         struct connman_group *group;
460         char *name;
461
462         DBG("network %p", network);
463
464         if (__connman_network_get_group(network) == NULL)
465                 return -EINVAL;
466
467         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
468                                         __connman_network_get_group(network));
469         group = lookup_group(name);
470         g_free(name);
471
472         if (group == NULL)
473                 return -EINVAL;
474
475         group->type = convert_network_type(network);
476
477         g_free(group->name);
478         group->name = g_strdup(connman_network_get_string(network, "Name"));
479
480         group->strength = connman_network_get_uint8(network, "Strength");
481
482         if (group->network == NULL) {
483                 group->network = network;
484
485                 group->mode = g_strdup(connman_network_get_string(network,
486                                                                 "WiFi.Mode"));
487                 group->security = g_strdup(connman_network_get_string(network,
488                                                         "WiFi.Security"));
489         }
490
491         g_sequence_sort_changed(group->iter, compare_group, NULL);
492         emit_services_signal();
493
494         return 0;
495 }
496
497 int __connman_profile_remove_network(struct connman_network *network)
498 {
499         struct connman_group *group;
500         char *name;
501
502         DBG("network %p", network);
503
504         if (__connman_network_get_group(network) == NULL)
505                 return -EINVAL;
506
507         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
508                                         __connman_network_get_group(network));
509         group = lookup_group(name);
510         g_free(name);
511
512         if (group == NULL)
513                 return -EINVAL;
514
515         if (group->network == network) {
516                 g_free(group->security);
517                 group->security = NULL;
518
519                 g_free(group->mode);
520                 group->mode = NULL;
521
522                 group->network = NULL;
523         }
524
525         group->type = CONNMAN_SERVICE_TYPE_UNKNOWN;
526
527         g_sequence_sort_changed(group->iter, compare_group, NULL);
528         emit_services_signal();
529
530         return 0;
531 }
532
533 void __connman_profile_list(DBusMessageIter *iter)
534 {
535         const char *path = __connman_profile_active();
536
537         DBG("");
538
539         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
540 }
541
542 static DBusMessage *profile_properties(DBusConnection *conn,
543                                         DBusMessage *msg, void *data)
544 {
545         const char *name = "Default";
546         DBusMessage *reply;
547         DBusMessageIter array, dict, entry;
548
549         DBG("conn %p", conn);
550
551         reply = dbus_message_new_method_return(msg);
552         if (reply == NULL)
553                 return NULL;
554
555         dbus_message_iter_init_append(reply, &array);
556
557         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
558                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
559                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
560                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
561
562         connman_dbus_dict_append_variant(&dict, "Name",
563                                                 DBUS_TYPE_STRING, &name);
564
565         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
566                                                                 NULL, &entry);
567         append_services(&entry);
568         dbus_message_iter_close_container(&dict, &entry);
569
570         dbus_message_iter_close_container(&array, &dict);
571
572         return reply;
573 }
574
575 static GDBusMethodTable profile_methods[] = {
576         { "GetProperties", "", "a{sv}", profile_properties },
577         { },
578 };
579
580 static GDBusSignalTable profile_signals[] = {
581         { "PropertyChanged", "sv" },
582         { },
583 };
584
585 int __connman_profile_init(DBusConnection *conn)
586 {
587         DBG("conn %p", conn);
588
589         connection = dbus_connection_ref(conn);
590         if (connection == NULL)
591                 return -1;
592
593         groups = g_sequence_new(free_group);
594
595         g_dbus_register_interface(connection, PROFILE_DEFAULT,
596                                         CONNMAN_PROFILE_INTERFACE,
597                                         profile_methods, profile_signals,
598                                                         NULL, NULL, NULL);
599
600         return 0;
601 }
602
603 void __connman_profile_cleanup(void)
604 {
605         DBG("conn %p", connection);
606
607         g_dbus_unregister_interface(connection, PROFILE_DEFAULT,
608                                                 CONNMAN_PROFILE_INTERFACE);
609
610         g_sequence_free(groups);
611         groups = NULL;
612
613         if (connection == NULL)
614                 return;
615
616         dbus_connection_unref(connection);
617 }