Use new D-Bus helpers wherever possible
[platform/upstream/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 <string.h>
27
28 #include <glib.h>
29 #include <gdbus.h>
30
31 #include "connman.h"
32
33 #define PROFILE_DEFAULT_IDENT  "default"
34
35 struct connman_profile {
36         char *ident;
37         char *path;
38         char *name;
39         connman_bool_t offlinemode;
40 };
41
42 static GHashTable *profile_hash = NULL;
43 static struct connman_profile *default_profile = NULL;
44
45 static DBusConnection *connection = NULL;
46
47 static void append_path(gpointer key, gpointer value, gpointer user_data)
48 {
49         struct connman_profile *profile = value;
50         DBusMessageIter *iter = user_data;
51
52         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
53                                                         &profile->path);
54 }
55
56 void __connman_profile_list(DBusMessageIter *iter)
57 {
58         DBG("");
59
60         g_hash_table_foreach(profile_hash, append_path, iter);
61 }
62
63 static void profiles_changed(void)
64 {
65         DBusMessage *signal;
66         DBusMessageIter iter;
67
68         DBG("");
69
70         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
71                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
72         if (signal == NULL)
73                 return;
74
75         dbus_message_iter_init_append(signal, &iter);
76         connman_dbus_property_append_variable_array(&iter, "Profiles",
77                                 DBUS_TYPE_OBJECT_PATH, __connman_profile_list);
78
79         g_dbus_send_message(connection, signal);
80 }
81
82 static void name_changed(struct connman_profile *profile)
83 {
84         DBusMessage *signal;
85         DBusMessageIter iter;
86
87         DBG("profile %p", profile);
88
89         signal = dbus_message_new_signal(profile->path,
90                                 CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
91         if (signal == NULL)
92                 return;
93
94         dbus_message_iter_init_append(signal, &iter);
95         connman_dbus_property_append_variant(&iter, "Name",
96                                         DBUS_TYPE_STRING, &profile->name);
97
98         g_dbus_send_message(connection, signal);
99 }
100
101 static void offlinemode_changed(struct connman_profile *profile)
102 {
103         DBusMessage *signal;
104         DBusMessageIter iter;
105
106         DBG("profile %p", profile);
107
108         signal = dbus_message_new_signal(profile->path,
109                                 CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
110         if (signal == NULL)
111                 return;
112
113         dbus_message_iter_init_append(signal, &iter);
114         connman_dbus_property_append_variant(&iter, "OfflineMode",
115                                 DBUS_TYPE_BOOLEAN, &profile->offlinemode);
116
117         g_dbus_send_message(connection, signal);
118 }
119
120 connman_bool_t __connman_profile_get_offlinemode(void)
121 {
122         if (default_profile == NULL)
123                 return FALSE;
124
125         DBG("offlinemode %d", default_profile->offlinemode);
126
127         return default_profile->offlinemode;
128 }
129
130 int __connman_profile_set_offlinemode(connman_bool_t offlinemode)
131 {
132         DBG("offlinemode %d", offlinemode);
133
134         if (default_profile == NULL)
135                 return -EINVAL;
136
137         if (default_profile->offlinemode == offlinemode)
138                 return -EALREADY;
139
140         default_profile->offlinemode = offlinemode;
141         offlinemode_changed(default_profile);
142
143         __connman_device_set_offlinemode(offlinemode);
144
145         return 0;
146 }
147
148 int __connman_profile_save_default(void)
149 {
150         DBG("");
151
152         if (default_profile != NULL)
153                 __connman_storage_save_profile(default_profile);
154
155         return 0;
156 }
157
158 const char *__connman_profile_active_ident(void)
159 {
160         DBG("");
161
162         return PROFILE_DEFAULT_IDENT;
163 }
164
165 const char *__connman_profile_active_path(void)
166 {
167         DBG("");
168
169         if (default_profile == NULL)
170                 return NULL;
171
172         return default_profile->path;
173 }
174
175 static guint changed_timeout = 0;
176
177 static gboolean services_changed(gpointer user_data)
178 {
179         struct connman_profile *profile = default_profile;
180         DBusMessage *signal;
181         DBusMessageIter iter;
182
183         changed_timeout = 0;
184
185         if (profile == NULL)
186                 return FALSE;
187
188         signal = dbus_message_new_signal(profile->path,
189                                 CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
190         if (signal == NULL)
191                 return FALSE;
192
193         dbus_message_iter_init_append(signal, &iter);
194         if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) == 0)
195                 connman_dbus_property_append_variable_array(&iter, "Services",
196                                 DBUS_TYPE_OBJECT_PATH, __connman_service_list);
197         else
198                 connman_dbus_property_append_variable_array(&iter, "Services",
199                                                 DBUS_TYPE_OBJECT_PATH, NULL);
200
201         g_dbus_send_message(connection, signal);
202
203         if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) != 0)
204                 return FALSE;
205
206         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
207                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
208         if (signal == NULL)
209                 return FALSE;
210
211         dbus_message_iter_init_append(signal, &iter);
212         connman_dbus_property_append_variable_array(&iter, "Services",
213                                 DBUS_TYPE_OBJECT_PATH, __connman_service_list);
214
215         g_dbus_send_message(connection, signal);
216
217         return FALSE;
218 }
219
220 void __connman_profile_changed(gboolean delayed)
221 {
222         DBG("");
223
224         if (changed_timeout > 0) {
225                 g_source_remove(changed_timeout);
226                 changed_timeout = 0;
227         }
228
229         if (__connman_connection_update_gateway() == TRUE) {
230                 services_changed(NULL);
231                 return;
232         }
233
234         if (delayed == FALSE) {
235                 services_changed(NULL);
236                 return;
237         }
238
239         changed_timeout = g_timeout_add_seconds(1, services_changed, NULL);
240 }
241
242 int __connman_profile_add_network(struct connman_network *network)
243 {
244         struct connman_service *service;
245
246         DBG("network %p", network);
247
248         service = __connman_service_create_from_network(network);
249         if (service == NULL)
250                 return -EINVAL;
251
252         return 0;
253 }
254
255 int __connman_profile_update_network(struct connman_network *network)
256 {
257         DBG("network %p", network);
258
259         __connman_service_update_from_network(network);
260
261         return 0;
262 }
263
264 int __connman_profile_remove_network(struct connman_network *network)
265 {
266         DBG("network %p", network);
267
268         __connman_service_remove_from_network(network);
269
270         return 0;
271 }
272
273 static DBusMessage *get_properties(DBusConnection *conn,
274                                         DBusMessage *msg, void *data)
275 {
276         struct connman_profile *profile = data;
277         DBusMessage *reply;
278         DBusMessageIter array, dict;
279
280         DBG("conn %p", conn);
281
282         reply = dbus_message_new_method_return(msg);
283         if (reply == NULL)
284                 return NULL;
285
286         dbus_message_iter_init_append(reply, &array);
287
288         connman_dbus_dict_open(&array, &dict);
289
290         if (profile->name != NULL)
291                 connman_dbus_dict_append_variant(&dict, "Name",
292                                         DBUS_TYPE_STRING, &profile->name);
293
294         connman_dbus_dict_append_variant(&dict, "OfflineMode",
295                                 DBUS_TYPE_BOOLEAN, &profile->offlinemode);
296
297         connman_dbus_dict_append_variable_array(&dict, "Services",
298                                 DBUS_TYPE_OBJECT_PATH, __connman_service_list);
299
300         connman_dbus_dict_close(&array, &dict);
301
302         return reply;
303 }
304
305 static DBusMessage *set_property(DBusConnection *conn,
306                                         DBusMessage *msg, void *data)
307 {
308         struct connman_profile *profile = data;
309         DBusMessageIter iter, value;
310         const char *name;
311         int type;
312
313         DBG("conn %p", conn);
314
315         if (dbus_message_iter_init(msg, &iter) == FALSE)
316                 return __connman_error_invalid_arguments(msg);
317
318         dbus_message_iter_get_basic(&iter, &name);
319         dbus_message_iter_next(&iter);
320         dbus_message_iter_recurse(&iter, &value);
321
322         if (__connman_security_check_privilege(msg,
323                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
324                 return __connman_error_permission_denied(msg);
325
326         type = dbus_message_iter_get_arg_type(&value);
327
328         if (g_str_equal(name, "Name") == TRUE) {
329                 const char *name;
330
331                 if (type != DBUS_TYPE_STRING)
332                         return __connman_error_invalid_arguments(msg);
333
334                 dbus_message_iter_get_basic(&value, &name);
335
336                 g_free(profile->name);
337                 profile->name = g_strdup(name);
338
339                 if (profile->name != NULL)
340                         name_changed(profile);
341
342                 __connman_storage_save_profile(profile);
343         } else if (g_str_equal(name, "OfflineMode") == TRUE) {
344                 connman_bool_t offlinemode;
345
346                 if (type != DBUS_TYPE_BOOLEAN)
347                         return __connman_error_invalid_arguments(msg);
348
349                 dbus_message_iter_get_basic(&value, &offlinemode);
350
351                 if (profile->offlinemode == offlinemode)
352                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
353
354                 profile->offlinemode = offlinemode;
355                 offlinemode_changed(profile);
356
357                 __connman_storage_save_profile(profile);
358         } else
359                 return __connman_error_invalid_property(msg);
360
361         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
362 }
363
364 static GDBusMethodTable profile_methods[] = {
365         { "GetProperties", "",   "a{sv}", get_properties },
366         { "SetProperty",   "sv", "",      set_property   },
367         { },
368 };
369
370 static GDBusSignalTable profile_signals[] = {
371         { "PropertyChanged", "sv" },
372         { },
373 };
374
375 static void free_profile(struct connman_profile *profile)
376 {
377         g_free(profile->name);
378         g_free(profile->path);
379         g_free(profile->ident);
380         g_free(profile);
381 }
382
383 static void unregister_profile(gpointer data)
384 {
385         struct connman_profile *profile = data;
386
387         DBG("profile %p", profile);
388
389         connman_info("Removing profile %s", profile->ident);
390
391         g_dbus_unregister_interface(connection, profile->path,
392                                                 CONNMAN_PROFILE_INTERFACE);
393
394         if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) == 0)
395                 default_profile = NULL;
396
397         free_profile(profile);
398 }
399
400 static int create_profile(const char *ident, const char *name,
401                                                         const char **path)
402 {
403         struct connman_profile *profile;
404
405         DBG("ident %s name %s", ident, name);
406
407         profile = g_try_new0(struct connman_profile, 1);
408         if (profile == NULL)
409                 return -ENOMEM;
410
411         profile->ident = g_strdup(ident);
412         profile->path = g_strdup_printf("/profile/%s", ident);
413
414         if (profile->ident == NULL || profile->path == NULL) {
415                 free_profile(profile);
416                 return -ENOMEM;
417         }
418
419         if (g_hash_table_lookup(profile_hash, profile->path) != NULL) {
420                 free_profile(profile);
421                 return -EEXIST;
422         }
423
424         profile->name = g_strdup(name);
425
426         __connman_storage_load_profile(profile);
427
428         g_hash_table_insert(profile_hash, g_strdup(profile->path), profile);
429
430         connman_info("Adding profile %s", ident);
431
432         if (g_strcmp0(ident, PROFILE_DEFAULT_IDENT) == 0)
433                 default_profile = profile;
434
435         g_dbus_register_interface(connection, profile->path,
436                                         CONNMAN_PROFILE_INTERFACE,
437                                         profile_methods, profile_signals,
438                                                         NULL, profile, NULL);
439
440         if (path != NULL)
441                 *path = profile->path;
442
443         DBG("profile %p path %s", profile, profile->path);
444
445         return 0;
446 }
447
448 int __connman_profile_create(const char *name, const char **path)
449 {
450         struct connman_profile *profile;
451         int err;
452
453         DBG("name %s", name);
454
455         if (connman_dbus_validate_ident(name) == FALSE)
456                 return -EINVAL;
457
458         err = create_profile(name, NULL, path);
459         if (err < 0)
460                 return err;
461
462         profile = g_hash_table_lookup(profile_hash, *path);
463         if (profile == NULL)
464                 return -EIO;
465
466         __connman_storage_save_profile(profile);
467
468         profiles_changed();
469
470         return 0;
471 }
472
473 int __connman_profile_remove(const char *path)
474 {
475         struct connman_profile *profile;
476
477         DBG("path %s", path);
478
479         if (default_profile != NULL &&
480                                 g_strcmp0(path, default_profile->path) == 0)
481                 return -EINVAL;
482
483         profile = g_hash_table_lookup(profile_hash, path);
484         if (profile == NULL)
485                 return -ENXIO;
486
487         __connman_storage_delete_profile(profile->ident);
488
489         g_hash_table_remove(profile_hash, path);
490
491         profiles_changed();
492
493         return 0;
494 }
495
496 static int profile_init(void)
497 {
498         GDir *dir;
499         const gchar *file;
500
501         DBG("");
502
503         dir = g_dir_open(STORAGEDIR, 0, NULL);
504         if (dir != NULL) {
505                 while ((file = g_dir_read_name(dir)) != NULL) {
506                         GString *str;
507                         gchar *ident;
508
509                         if (g_str_has_suffix(file, ".profile") == FALSE)
510                                 continue;
511
512                         ident = g_strrstr(file, ".profile");
513                         if (ident == NULL)
514                                 continue;
515
516                         str = g_string_new_len(file, ident - file);
517                         if (str == NULL)
518                                 continue;
519
520                         ident = g_string_free(str, FALSE);
521
522                         if (connman_dbus_validate_ident(ident) == TRUE)
523                                 create_profile(ident, NULL, NULL);
524
525                         g_free(ident);
526                 }
527
528                 g_dir_close(dir);
529         }
530
531         if (g_hash_table_size(profile_hash) == 0)
532                 create_profile(PROFILE_DEFAULT_IDENT, "Default", NULL);
533
534         profiles_changed();
535
536         return 0;
537 }
538
539 static int profile_load(struct connman_profile *profile)
540 {
541         GKeyFile *keyfile;
542         GError *error = NULL;
543         connman_bool_t offlinemode;
544         char *name;
545
546         DBG("profile %p", profile);
547
548         keyfile = __connman_storage_open_profile(profile->ident);
549         if (keyfile == NULL)
550                 return -EIO;
551
552         name = g_key_file_get_string(keyfile, "global", "Name", NULL);
553         if (name != NULL) {
554                 g_free(profile->name);
555                 profile->name = name;
556         }
557
558         offlinemode = g_key_file_get_boolean(keyfile, "global",
559                                                 "OfflineMode", &error);
560         if (error == NULL)
561                 profile->offlinemode = offlinemode;
562         g_clear_error(&error);
563
564         __connman_storage_close_profile(profile->ident, keyfile, FALSE);
565
566         return 0;
567 }
568
569 static int profile_save(struct connman_profile *profile)
570 {
571         GKeyFile *keyfile;
572
573         DBG("profile %p", profile);
574
575         keyfile = __connman_storage_open_profile(profile->ident);
576         if (keyfile == NULL)
577                 return -EIO;
578
579         if (profile->name != NULL)
580                 g_key_file_set_string(keyfile, "global",
581                                                 "Name", profile->name);
582
583         g_key_file_set_boolean(keyfile, "global",
584                                         "OfflineMode", profile->offlinemode);
585
586         __connman_storage_close_profile(profile->ident, keyfile, TRUE);
587
588         return 0;
589 }
590
591 static struct connman_storage profile_storage = {
592         .name           = "profile",
593         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
594         .profile_init   = profile_init,
595         .profile_load   = profile_load,
596         .profile_save   = profile_save,
597 };
598
599 int __connman_profile_init(void)
600 {
601         DBG("");
602
603         connection = connman_dbus_get_connection();
604         if (connection == NULL)
605                 return -1;
606
607         if (connman_storage_register(&profile_storage) < 0)
608                 connman_error("Failed to register profile storage");
609
610         profile_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
611                                                 g_free, unregister_profile);
612
613         return 0;
614 }
615
616 void __connman_profile_cleanup(void)
617 {
618         DBG("");
619
620         if (connection == NULL)
621                 return;
622
623         g_hash_table_destroy(profile_hash);
624         profile_hash = NULL;
625
626         connman_storage_unregister(&profile_storage);
627
628         dbus_connection_unref(connection);
629 }