Add function to get index value from DHCP structure
[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 <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_network(struct connman_network *network)
289 {
290         struct connman_service *service;
291
292         DBG("network %p", network);
293
294         service = __connman_service_create_from_network(network);
295         if (service == NULL)
296                 return -EINVAL;
297
298         return 0;
299 }
300
301 int __connman_profile_update_network(struct connman_network *network)
302 {
303         DBG("network %p", network);
304
305         __connman_service_update_from_network(network);
306
307         return 0;
308 }
309
310 int __connman_profile_remove_network(struct connman_network *network)
311 {
312         DBG("network %p", network);
313
314         __connman_service_remove_from_network(network);
315
316         return 0;
317 }
318
319 static DBusMessage *get_properties(DBusConnection *conn,
320                                         DBusMessage *msg, void *data)
321 {
322         struct connman_profile *profile = data;
323         DBusMessage *reply;
324         DBusMessageIter array, dict, entry;
325
326         DBG("conn %p", conn);
327
328         reply = dbus_message_new_method_return(msg);
329         if (reply == NULL)
330                 return NULL;
331
332         dbus_message_iter_init_append(reply, &array);
333
334         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
335                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
336                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
337                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
338
339         if (profile->name != NULL)
340                 connman_dbus_dict_append_variant(&dict, "Name",
341                                         DBUS_TYPE_STRING, &profile->name);
342
343         connman_dbus_dict_append_variant(&dict, "OfflineMode",
344                                 DBUS_TYPE_BOOLEAN, &profile->offlinemode);
345
346         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
347                                                                 NULL, &entry);
348         append_services(profile, &entry);
349         dbus_message_iter_close_container(&dict, &entry);
350
351         dbus_message_iter_close_container(&array, &dict);
352
353         return reply;
354 }
355
356 static DBusMessage *set_property(DBusConnection *conn,
357                                         DBusMessage *msg, void *data)
358 {
359         struct connman_profile *profile = data;
360         DBusMessageIter iter, value;
361         const char *name;
362         int type;
363
364         DBG("conn %p", conn);
365
366         if (dbus_message_iter_init(msg, &iter) == FALSE)
367                 return __connman_error_invalid_arguments(msg);
368
369         dbus_message_iter_get_basic(&iter, &name);
370         dbus_message_iter_next(&iter);
371         dbus_message_iter_recurse(&iter, &value);
372
373         if (__connman_security_check_privilege(msg,
374                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
375                 return __connman_error_permission_denied(msg);
376
377         type = dbus_message_iter_get_arg_type(&value);
378
379         if (g_str_equal(name, "Name") == TRUE) {
380                 const char *name;
381
382                 if (type != DBUS_TYPE_STRING)
383                         return __connman_error_invalid_arguments(msg);
384
385                 dbus_message_iter_get_basic(&value, &name);
386
387                 g_free(profile->name);
388                 profile->name = g_strdup(name);
389
390                 if (profile->name != NULL)
391                         name_changed(profile);
392
393                 __connman_storage_save_profile(profile);
394         } else if (g_str_equal(name, "OfflineMode") == TRUE) {
395                 connman_bool_t offlinemode;
396
397                 if (type != DBUS_TYPE_BOOLEAN)
398                         return __connman_error_invalid_arguments(msg);
399
400                 dbus_message_iter_get_basic(&value, &offlinemode);
401
402                 if (profile->offlinemode == offlinemode)
403                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
404
405                 profile->offlinemode = offlinemode;
406                 offlinemode_changed(profile);
407
408                 __connman_storage_save_profile(profile);
409         } else
410                 return __connman_error_invalid_property(msg);
411
412         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
413 }
414
415 static GDBusMethodTable profile_methods[] = {
416         { "GetProperties", "",   "a{sv}", get_properties },
417         { "SetProperty",   "sv", "",      set_property   },
418         { },
419 };
420
421 static GDBusSignalTable profile_signals[] = {
422         { "PropertyChanged", "sv" },
423         { },
424 };
425
426 static void free_profile(struct connman_profile *profile)
427 {
428         g_free(profile->name);
429         g_free(profile->path);
430         g_free(profile);
431 }
432
433 static void unregister_profile(gpointer data)
434 {
435         struct connman_profile *profile = data;
436
437         DBG("profile %p", profile);
438
439         connman_info("Removing profile %s", profile->ident);
440
441         g_dbus_unregister_interface(connection, profile->path,
442                                                 CONNMAN_PROFILE_INTERFACE);
443
444         if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) == 0)
445                 default_profile = NULL;
446
447         free_profile(profile);
448 }
449
450 static int create_profile(const char *ident, const char *name,
451                                                         const char **path)
452 {
453         struct connman_profile *profile;
454
455         DBG("ident %s name %s", ident, name);
456
457         profile = g_try_new0(struct connman_profile, 1);
458         if (profile == NULL)
459                 return -ENOMEM;
460
461         profile->ident = g_strdup(ident);
462         profile->path = g_strdup_printf("/profile/%s", ident);
463
464         if (profile->ident == NULL || profile->path == NULL) {
465                 free_profile(profile);
466                 return -ENOMEM;
467         }
468
469         if (g_hash_table_lookup(profiles, profile->path) != NULL) {
470                 free_profile(profile);
471                 return -EEXIST;
472         }
473
474         profile->name = g_strdup(name);
475
476         __connman_storage_load_profile(profile);
477
478         g_hash_table_insert(profiles, g_strdup(profile->path), profile);
479
480         connman_info("Adding profile %s", ident);
481
482         if (g_strcmp0(ident, PROFILE_DEFAULT_IDENT) == 0)
483                 default_profile = profile;
484
485         g_dbus_register_interface(connection, profile->path,
486                                         CONNMAN_PROFILE_INTERFACE,
487                                         profile_methods, profile_signals,
488                                                         NULL, profile, NULL);
489
490         if (path != NULL)
491                 *path = profile->path;
492
493         DBG("profile %p path %s", profile, profile->path);
494
495         return 0;
496 }
497
498 static gboolean validate_ident(const char *ident)
499 {
500         unsigned int i;
501
502         for (i = 0; i < strlen(ident); i++) {
503                 if (ident[i] >= '0' && ident[i] <= '9')
504                         continue;
505                 if (ident[i] >= 'a' && ident[i] <= 'z')
506                         continue;
507                 if (ident[i] >= 'A' && ident[i] <= 'Z')
508                         continue;
509                 return FALSE;
510         }
511
512         return TRUE;
513 }
514
515 int __connman_profile_create(const char *name, const char **path)
516 {
517         struct connman_profile *profile;
518         int err;
519
520         DBG("name %s", name);
521
522         if (validate_ident(name) == FALSE)
523                 return -EINVAL;
524
525         err = create_profile(name, NULL, path);
526         if (err < 0)
527                 return err;
528
529         profile = g_hash_table_lookup(profiles, *path);
530         if (profile == NULL)
531                 return -EIO;
532
533         __connman_storage_save_profile(profile);
534
535         profiles_changed();
536
537         return 0;
538 }
539
540 int __connman_profile_remove(const char *path)
541 {
542         struct connman_profile *profile;
543
544         DBG("path %s", path);
545
546         if (default_profile != NULL &&
547                                 g_strcmp0(path, default_profile->path) == 0)
548                 return -EINVAL;
549
550         profile = g_hash_table_lookup(profiles, path);
551         if (profile == NULL)
552                 return -ENXIO;
553
554         __connman_storage_delete(profile->ident);
555
556         g_hash_table_remove(profiles, path);
557
558         profiles_changed();
559
560         return 0;
561 }
562
563 static int profile_init(void)
564 {
565         GDir *dir;
566         const gchar *file;
567
568         DBG("");
569
570         dir = g_dir_open(STORAGEDIR, 0, NULL);
571         if (dir != NULL) {
572                 while ((file = g_dir_read_name(dir)) != NULL) {
573                         GString *str;
574                         gchar *ident;
575
576                         if (g_str_has_suffix(file, ".profile") == FALSE)
577                                 continue;
578
579                         ident = g_strrstr(file, ".profile");
580                         if (ident == NULL)
581                                 continue;
582
583                         str = g_string_new_len(file, ident - file);
584                         if (str == NULL)
585                                 continue;
586
587                         ident = g_string_free(str, FALSE);
588
589                         if (validate_ident(ident) == TRUE)
590                                 create_profile(ident, NULL, NULL);
591
592                         g_free(ident);
593                 }
594
595                 g_dir_close(dir);
596         }
597
598         if (g_hash_table_size(profiles) == 0)
599                 create_profile(PROFILE_DEFAULT_IDENT, "Default", NULL);
600
601         profiles_changed();
602
603         return 0;
604 }
605
606 static int profile_load(struct connman_profile *profile)
607 {
608         GKeyFile *keyfile;
609         GError *error = NULL;
610         connman_bool_t offlinemode;
611         char *name;
612
613         DBG("profile %p", profile);
614
615         keyfile = __connman_storage_open(profile->ident);
616         if (keyfile == NULL)
617                 return -EIO;
618
619         name = g_key_file_get_string(keyfile, "global", "Name", NULL);
620         if (name != NULL) {
621                 g_free(profile->name);
622                 profile->name = name;
623         }
624
625         offlinemode = g_key_file_get_boolean(keyfile, "global",
626                                                 "OfflineMode", &error);
627         if (error == NULL)
628                 profile->offlinemode = offlinemode;
629         g_clear_error(&error);
630
631         __connman_storage_close(profile->ident, keyfile, FALSE);
632
633         return 0;
634 }
635
636 static int profile_save(struct connman_profile *profile)
637 {
638         GKeyFile *keyfile;
639
640         DBG("profile %p", profile);
641
642         keyfile = __connman_storage_open(profile->ident);
643         if (keyfile == NULL)
644                 return -EIO;
645
646         if (profile->name != NULL)
647                 g_key_file_set_string(keyfile, "global",
648                                                 "Name", profile->name);
649
650         g_key_file_set_boolean(keyfile, "global",
651                                         "OfflineMode", profile->offlinemode);
652
653         __connman_storage_close(profile->ident, keyfile, TRUE);
654
655         return 0;
656 }
657
658 static struct connman_storage profile_storage = {
659         .name           = "profile",
660         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
661         .profile_init   = profile_init,
662         .profile_load   = profile_load,
663         .profile_save   = profile_save,
664 };
665
666 int __connman_profile_init(void)
667 {
668         DBG("");
669
670         connection = connman_dbus_get_connection();
671         if (connection == NULL)
672                 return -1;
673
674         if (connman_storage_register(&profile_storage) < 0)
675                 connman_error("Failed to register profile storage");
676
677         profiles = g_hash_table_new_full(g_str_hash, g_str_equal,
678                                                 g_free, unregister_profile);
679
680         return 0;
681 }
682
683 void __connman_profile_cleanup(void)
684 {
685         DBG("");
686
687         if (connection == NULL)
688                 return;
689
690         g_hash_table_destroy(profiles);
691         profiles = NULL;
692
693         connman_storage_unregister(&profile_storage);
694
695         dbus_connection_unref(connection);
696 }