Add inet helper to retrieve current flags
[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 *profiles = 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(profiles, append_path, iter);
61 }
62
63 static void append_profiles(DBusMessageIter *entry)
64 {
65         DBusMessageIter value, iter;
66         const char *key = "Profiles";
67
68         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
69
70         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
71                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
72                                                                 &value);
73
74         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
75                                 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
76         __connman_profile_list(&iter);
77         dbus_message_iter_close_container(&value, &iter);
78
79         dbus_message_iter_close_container(entry, &value);
80 }
81
82 static void profiles_changed(void)
83 {
84         DBusMessage *signal;
85         DBusMessageIter entry;
86
87         DBG("");
88
89         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
90                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
91         if (signal == NULL)
92                 return;
93
94         dbus_message_iter_init_append(signal, &entry);
95         append_profiles(&entry);
96         g_dbus_send_message(connection, signal);
97 }
98
99 static void name_changed(struct connman_profile *profile)
100 {
101         DBusMessage *signal;
102         DBusMessageIter entry, value;
103         const char *key = "Name";
104
105         DBG("profile %p", profile);
106
107         signal = dbus_message_new_signal(profile->path,
108                                 CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
109         if (signal == NULL)
110                 return;
111
112         dbus_message_iter_init_append(signal, &entry);
113
114         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
115
116         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
117                                         DBUS_TYPE_STRING_AS_STRING, &value);
118         dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING,
119                                                         &profile->name);
120         dbus_message_iter_close_container(&entry, &value);
121
122         g_dbus_send_message(connection, signal);
123 }
124
125 static void offlinemode_changed(struct connman_profile *profile)
126 {
127         DBusMessage *signal;
128         DBusMessageIter entry, value;
129         const char *key = "OfflineMode";
130
131         DBG("profile %p", profile);
132
133         signal = dbus_message_new_signal(profile->path,
134                                 CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
135         if (signal == NULL)
136                 return;
137
138         dbus_message_iter_init_append(signal, &entry);
139
140         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
141
142         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
143                                         DBUS_TYPE_BOOLEAN_AS_STRING, &value);
144         dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN,
145                                                 &profile->offlinemode);
146         dbus_message_iter_close_container(&entry, &value);
147
148         g_dbus_send_message(connection, signal);
149 }
150
151 connman_bool_t __connman_profile_get_offlinemode(void)
152 {
153         if (default_profile == NULL)
154                 return FALSE;
155
156         DBG("offlinemode %d", default_profile->offlinemode);
157
158         return default_profile->offlinemode;
159 }
160
161 int __connman_profile_set_offlinemode(connman_bool_t offlinemode)
162 {
163         DBG("offlinemode %d", offlinemode);
164
165         if (default_profile == NULL)
166                 return -EINVAL;
167
168         if (default_profile->offlinemode == offlinemode)
169                 return -EALREADY;
170
171         default_profile->offlinemode = offlinemode;
172         offlinemode_changed(default_profile);
173
174         __connman_device_set_offlinemode(offlinemode);
175
176         return 0;
177 }
178
179 int __connman_profile_save_default(void)
180 {
181         DBG("");
182
183         if (default_profile != NULL)
184                 __connman_storage_save_profile(default_profile);
185
186         return 0;
187 }
188
189 const char *__connman_profile_active_ident(void)
190 {
191         DBG("");
192
193         return PROFILE_DEFAULT_IDENT;
194 }
195
196 const char *__connman_profile_active_path(void)
197 {
198         DBG("");
199
200         if (default_profile == NULL)
201                 return NULL;
202
203         return default_profile->path;
204 }
205
206 static void append_services(struct connman_profile *profile,
207                                                 DBusMessageIter *entry)
208 {
209         DBusMessageIter value, iter;
210         const char *key = "Services";
211
212         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
213
214         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
215                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
216                                                                 &value);
217
218         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
219                                 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
220
221         if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) == 0)
222                 __connman_service_list(&iter);
223
224         dbus_message_iter_close_container(&value, &iter);
225
226         dbus_message_iter_close_container(entry, &value);
227 }
228
229 static guint changed_timeout = 0;
230
231 static gboolean services_changed(gpointer user_data)
232 {
233         struct connman_profile *profile = default_profile;
234         DBusMessage *signal;
235         DBusMessageIter entry;
236
237         changed_timeout = 0;
238
239         if (profile == NULL)
240                 return FALSE;
241
242         signal = dbus_message_new_signal(profile->path,
243                                 CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
244         if (signal == NULL)
245                 return FALSE;
246
247         dbus_message_iter_init_append(signal, &entry);
248         append_services(profile, &entry);
249         g_dbus_send_message(connection, signal);
250
251         if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) != 0)
252                 return FALSE;
253
254         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
255                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
256         if (signal == NULL)
257                 return FALSE;
258
259         dbus_message_iter_init_append(signal, &entry);
260         append_services(profile, &entry);
261         g_dbus_send_message(connection, signal);
262
263         return FALSE;
264 }
265
266 void __connman_profile_changed(gboolean delayed)
267 {
268         DBG("");
269
270         if (changed_timeout > 0) {
271                 g_source_remove(changed_timeout);
272                 changed_timeout = 0;
273         }
274
275         if (__connman_connection_update_gateway() == TRUE) {
276                 services_changed(NULL);
277                 return;
278         }
279
280         if (delayed == FALSE) {
281                 services_changed(NULL);
282                 return;
283         }
284
285         changed_timeout = g_timeout_add_seconds(1, services_changed, NULL);
286 }
287
288 int __connman_profile_add_device(struct connman_device *device)
289 {
290         struct connman_service *service;
291
292         DBG("device %p", device);
293
294         service = __connman_service_create_from_device(device);
295         if (service == NULL)
296                 return -EINVAL;
297
298         return 0;
299 }
300
301 int __connman_profile_remove_device(struct connman_device *device)
302 {
303         DBG("device %p", device);
304
305         __connman_service_remove_from_device(device);
306
307         return 0;
308 }
309
310 int __connman_profile_add_network(struct connman_network *network)
311 {
312         struct connman_service *service;
313
314         DBG("network %p", network);
315
316         service = __connman_service_create_from_network(network);
317         if (service == NULL)
318                 return -EINVAL;
319
320         return 0;
321 }
322
323 int __connman_profile_update_network(struct connman_network *network)
324 {
325         DBG("network %p", network);
326
327         __connman_service_update_from_network(network);
328
329         return 0;
330 }
331
332 int __connman_profile_remove_network(struct connman_network *network)
333 {
334         DBG("network %p", network);
335
336         __connman_service_remove_from_network(network);
337
338         return 0;
339 }
340
341 static DBusMessage *get_properties(DBusConnection *conn,
342                                         DBusMessage *msg, void *data)
343 {
344         struct connman_profile *profile = data;
345         DBusMessage *reply;
346         DBusMessageIter array, dict, entry;
347
348         DBG("conn %p", conn);
349
350         reply = dbus_message_new_method_return(msg);
351         if (reply == NULL)
352                 return NULL;
353
354         dbus_message_iter_init_append(reply, &array);
355
356         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
357                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
358                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
359                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
360
361         if (profile->name != NULL)
362                 connman_dbus_dict_append_variant(&dict, "Name",
363                                         DBUS_TYPE_STRING, &profile->name);
364
365         connman_dbus_dict_append_variant(&dict, "OfflineMode",
366                                 DBUS_TYPE_BOOLEAN, &profile->offlinemode);
367
368         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
369                                                                 NULL, &entry);
370         append_services(profile, &entry);
371         dbus_message_iter_close_container(&dict, &entry);
372
373         dbus_message_iter_close_container(&array, &dict);
374
375         return reply;
376 }
377
378 static DBusMessage *set_property(DBusConnection *conn,
379                                         DBusMessage *msg, void *data)
380 {
381         struct connman_profile *profile = data;
382         DBusMessageIter iter, value;
383         const char *name;
384         int type;
385
386         DBG("conn %p", conn);
387
388         if (dbus_message_iter_init(msg, &iter) == FALSE)
389                 return __connman_error_invalid_arguments(msg);
390
391         dbus_message_iter_get_basic(&iter, &name);
392         dbus_message_iter_next(&iter);
393         dbus_message_iter_recurse(&iter, &value);
394
395         if (__connman_security_check_privilege(msg,
396                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
397                 return __connman_error_permission_denied(msg);
398
399         type = dbus_message_iter_get_arg_type(&value);
400
401         if (g_str_equal(name, "Name") == TRUE) {
402                 const char *name;
403
404                 if (type != DBUS_TYPE_STRING)
405                         return __connman_error_invalid_arguments(msg);
406
407                 dbus_message_iter_get_basic(&value, &name);
408
409                 g_free(profile->name);
410                 profile->name = g_strdup(name);
411
412                 if (profile->name != NULL)
413                         name_changed(profile);
414
415                 __connman_storage_save_profile(profile);
416         } else if (g_str_equal(name, "OfflineMode") == TRUE) {
417                 connman_bool_t offlinemode;
418
419                 if (type != DBUS_TYPE_BOOLEAN)
420                         return __connman_error_invalid_arguments(msg);
421
422                 dbus_message_iter_get_basic(&value, &offlinemode);
423
424                 if (profile->offlinemode == offlinemode)
425                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
426
427                 profile->offlinemode = offlinemode;
428                 offlinemode_changed(profile);
429
430                 __connman_storage_save_profile(profile);
431         } else
432                 return __connman_error_invalid_property(msg);
433
434         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
435 }
436
437 static GDBusMethodTable profile_methods[] = {
438         { "GetProperties", "",   "a{sv}", get_properties },
439         { "SetProperty",   "sv", "",      set_property   },
440         { },
441 };
442
443 static GDBusSignalTable profile_signals[] = {
444         { "PropertyChanged", "sv" },
445         { },
446 };
447
448 static void free_profile(struct connman_profile *profile)
449 {
450         g_free(profile->name);
451         g_free(profile->path);
452         g_free(profile);
453 }
454
455 static void unregister_profile(gpointer data)
456 {
457         struct connman_profile *profile = data;
458
459         DBG("profile %p", profile);
460
461         connman_info("Removing profile %s", profile->ident);
462
463         g_dbus_unregister_interface(connection, profile->path,
464                                                 CONNMAN_PROFILE_INTERFACE);
465
466         if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) == 0)
467                 default_profile = NULL;
468
469         free_profile(profile);
470 }
471
472 static int create_profile(const char *ident, const char *name,
473                                                         const char **path)
474 {
475         struct connman_profile *profile;
476
477         DBG("ident %s name %s", ident, name);
478
479         profile = g_try_new0(struct connman_profile, 1);
480         if (profile == NULL)
481                 return -ENOMEM;
482
483         profile->ident = g_strdup(ident);
484         profile->path = g_strdup_printf("/profile/%s", ident);
485
486         if (profile->ident == NULL || profile->path == NULL) {
487                 free_profile(profile);
488                 return -ENOMEM;
489         }
490
491         if (g_hash_table_lookup(profiles, profile->path) != NULL) {
492                 free_profile(profile);
493                 return -EEXIST;
494         }
495
496         profile->name = g_strdup(name);
497
498         __connman_storage_load_profile(profile);
499
500         g_hash_table_insert(profiles, g_strdup(profile->path), profile);
501
502         connman_info("Adding profile %s", ident);
503
504         if (g_strcmp0(ident, PROFILE_DEFAULT_IDENT) == 0)
505                 default_profile = profile;
506
507         g_dbus_register_interface(connection, profile->path,
508                                         CONNMAN_PROFILE_INTERFACE,
509                                         profile_methods, profile_signals,
510                                                         NULL, profile, NULL);
511
512         if (path != NULL)
513                 *path = profile->path;
514
515         DBG("profile %p path %s", profile, profile->path);
516
517         return 0;
518 }
519
520 static gboolean validate_ident(const char *ident)
521 {
522         unsigned int i;
523
524         for (i = 0; i < strlen(ident); i++) {
525                 if (ident[i] >= '0' && ident[i] <= '9')
526                         continue;
527                 if (ident[i] >= 'a' && ident[i] <= 'z')
528                         continue;
529                 if (ident[i] >= 'A' && ident[i] <= 'Z')
530                         continue;
531                 return FALSE;
532         }
533
534         return TRUE;
535 }
536
537 int __connman_profile_create(const char *name, const char **path)
538 {
539         struct connman_profile *profile;
540         int err;
541
542         DBG("name %s", name);
543
544         if (validate_ident(name) == FALSE)
545                 return -EINVAL;
546
547         err = create_profile(name, NULL, path);
548         if (err < 0)
549                 return err;
550
551         profile = g_hash_table_lookup(profiles, *path);
552         if (profile == NULL)
553                 return -EIO;
554
555         __connman_storage_save_profile(profile);
556
557         profiles_changed();
558
559         return 0;
560 }
561
562 int __connman_profile_remove(const char *path)
563 {
564         struct connman_profile *profile;
565
566         DBG("path %s", path);
567
568         if (default_profile != NULL &&
569                                 g_strcmp0(path, default_profile->path) == 0)
570                 return -EINVAL;
571
572         profile = g_hash_table_lookup(profiles, path);
573         if (profile == NULL)
574                 return -ENXIO;
575
576         __connman_storage_delete(profile->ident);
577
578         g_hash_table_remove(profiles, path);
579
580         profiles_changed();
581
582         return 0;
583 }
584
585 static int profile_init(void)
586 {
587         GDir *dir;
588         const gchar *file;
589
590         DBG("");
591
592         dir = g_dir_open(STORAGEDIR, 0, NULL);
593         if (dir != NULL) {
594                 while ((file = g_dir_read_name(dir)) != NULL) {
595                         GString *str;
596                         gchar *ident;
597
598                         if (g_str_has_suffix(file, ".profile") == FALSE)
599                                 continue;
600
601                         ident = g_strrstr(file, ".profile");
602                         if (ident == NULL)
603                                 continue;
604
605                         str = g_string_new_len(file, ident - file);
606                         if (str == NULL)
607                                 continue;
608
609                         ident = g_string_free(str, FALSE);
610
611                         if (validate_ident(ident) == TRUE)
612                                 create_profile(ident, NULL, NULL);
613
614                         g_free(ident);
615                 }
616
617                 g_dir_close(dir);
618         }
619
620         if (g_hash_table_size(profiles) == 0)
621                 create_profile(PROFILE_DEFAULT_IDENT, "Default", NULL);
622
623         profiles_changed();
624
625         return 0;
626 }
627
628 static int profile_load(struct connman_profile *profile)
629 {
630         GKeyFile *keyfile;
631         GError *error = NULL;
632         connman_bool_t offlinemode;
633         char *name;
634
635         DBG("profile %p", profile);
636
637         keyfile = __connman_storage_open(profile->ident);
638         if (keyfile == NULL)
639                 return -EIO;
640
641         name = g_key_file_get_string(keyfile, "global", "Name", NULL);
642         if (name != NULL) {
643                 g_free(profile->name);
644                 profile->name = name;
645         }
646
647         offlinemode = g_key_file_get_boolean(keyfile, "global",
648                                                 "OfflineMode", &error);
649         if (error == NULL)
650                 profile->offlinemode = offlinemode;
651         g_clear_error(&error);
652
653         __connman_storage_close(profile->ident, keyfile, FALSE);
654
655         return 0;
656 }
657
658 static int profile_save(struct connman_profile *profile)
659 {
660         GKeyFile *keyfile;
661
662         DBG("profile %p", profile);
663
664         keyfile = __connman_storage_open(profile->ident);
665         if (keyfile == NULL)
666                 return -EIO;
667
668         if (profile->name != NULL)
669                 g_key_file_set_string(keyfile, "global",
670                                                 "Name", profile->name);
671
672         g_key_file_set_boolean(keyfile, "global",
673                                         "OfflineMode", profile->offlinemode);
674
675         __connman_storage_close(profile->ident, keyfile, TRUE);
676
677         return 0;
678 }
679
680 static struct connman_storage profile_storage = {
681         .name           = "profile",
682         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
683         .profile_init   = profile_init,
684         .profile_load   = profile_load,
685         .profile_save   = profile_save,
686 };
687
688 int __connman_profile_init(void)
689 {
690         DBG("");
691
692         connection = connman_dbus_get_connection();
693         if (connection == NULL)
694                 return -1;
695
696         if (connman_storage_register(&profile_storage) < 0)
697                 connman_error("Failed to register profile storage");
698
699         profiles = g_hash_table_new_full(g_str_hash, g_str_equal,
700                                                 g_free, unregister_profile);
701
702         return 0;
703 }
704
705 void __connman_profile_cleanup(void)
706 {
707         DBG("");
708
709         if (connection == NULL)
710                 return;
711
712         g_hash_table_destroy(profiles);
713         profiles = NULL;
714
715         connman_storage_unregister(&profile_storage);
716
717         dbus_connection_unref(connection);
718 }