Fix memory leak in profiles and rename hash variable
[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 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->ident);
431         g_free(profile);
432 }
433
434 static void unregister_profile(gpointer data)
435 {
436         struct connman_profile *profile = data;
437
438         DBG("profile %p", profile);
439
440         connman_info("Removing profile %s", profile->ident);
441
442         g_dbus_unregister_interface(connection, profile->path,
443                                                 CONNMAN_PROFILE_INTERFACE);
444
445         if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) == 0)
446                 default_profile = NULL;
447
448         free_profile(profile);
449 }
450
451 static int create_profile(const char *ident, const char *name,
452                                                         const char **path)
453 {
454         struct connman_profile *profile;
455
456         DBG("ident %s name %s", ident, name);
457
458         profile = g_try_new0(struct connman_profile, 1);
459         if (profile == NULL)
460                 return -ENOMEM;
461
462         profile->ident = g_strdup(ident);
463         profile->path = g_strdup_printf("/profile/%s", ident);
464
465         if (profile->ident == NULL || profile->path == NULL) {
466                 free_profile(profile);
467                 return -ENOMEM;
468         }
469
470         if (g_hash_table_lookup(profile_hash, profile->path) != NULL) {
471                 free_profile(profile);
472                 return -EEXIST;
473         }
474
475         profile->name = g_strdup(name);
476
477         __connman_storage_load_profile(profile);
478
479         g_hash_table_insert(profile_hash, g_strdup(profile->path), profile);
480
481         connman_info("Adding profile %s", ident);
482
483         if (g_strcmp0(ident, PROFILE_DEFAULT_IDENT) == 0)
484                 default_profile = profile;
485
486         g_dbus_register_interface(connection, profile->path,
487                                         CONNMAN_PROFILE_INTERFACE,
488                                         profile_methods, profile_signals,
489                                                         NULL, profile, NULL);
490
491         if (path != NULL)
492                 *path = profile->path;
493
494         DBG("profile %p path %s", profile, profile->path);
495
496         return 0;
497 }
498
499 static gboolean validate_ident(const char *ident)
500 {
501         unsigned int i;
502
503         for (i = 0; i < strlen(ident); i++) {
504                 if (ident[i] >= '0' && ident[i] <= '9')
505                         continue;
506                 if (ident[i] >= 'a' && ident[i] <= 'z')
507                         continue;
508                 if (ident[i] >= 'A' && ident[i] <= 'Z')
509                         continue;
510                 return FALSE;
511         }
512
513         return TRUE;
514 }
515
516 int __connman_profile_create(const char *name, const char **path)
517 {
518         struct connman_profile *profile;
519         int err;
520
521         DBG("name %s", name);
522
523         if (validate_ident(name) == FALSE)
524                 return -EINVAL;
525
526         err = create_profile(name, NULL, path);
527         if (err < 0)
528                 return err;
529
530         profile = g_hash_table_lookup(profile_hash, *path);
531         if (profile == NULL)
532                 return -EIO;
533
534         __connman_storage_save_profile(profile);
535
536         profiles_changed();
537
538         return 0;
539 }
540
541 int __connman_profile_remove(const char *path)
542 {
543         struct connman_profile *profile;
544
545         DBG("path %s", path);
546
547         if (default_profile != NULL &&
548                                 g_strcmp0(path, default_profile->path) == 0)
549                 return -EINVAL;
550
551         profile = g_hash_table_lookup(profile_hash, path);
552         if (profile == NULL)
553                 return -ENXIO;
554
555         __connman_storage_delete(profile->ident);
556
557         g_hash_table_remove(profile_hash, path);
558
559         profiles_changed();
560
561         return 0;
562 }
563
564 static int profile_init(void)
565 {
566         GDir *dir;
567         const gchar *file;
568
569         DBG("");
570
571         dir = g_dir_open(STORAGEDIR, 0, NULL);
572         if (dir != NULL) {
573                 while ((file = g_dir_read_name(dir)) != NULL) {
574                         GString *str;
575                         gchar *ident;
576
577                         if (g_str_has_suffix(file, ".profile") == FALSE)
578                                 continue;
579
580                         ident = g_strrstr(file, ".profile");
581                         if (ident == NULL)
582                                 continue;
583
584                         str = g_string_new_len(file, ident - file);
585                         if (str == NULL)
586                                 continue;
587
588                         ident = g_string_free(str, FALSE);
589
590                         if (validate_ident(ident) == TRUE)
591                                 create_profile(ident, NULL, NULL);
592
593                         g_free(ident);
594                 }
595
596                 g_dir_close(dir);
597         }
598
599         if (g_hash_table_size(profile_hash) == 0)
600                 create_profile(PROFILE_DEFAULT_IDENT, "Default", NULL);
601
602         profiles_changed();
603
604         return 0;
605 }
606
607 static int profile_load(struct connman_profile *profile)
608 {
609         GKeyFile *keyfile;
610         GError *error = NULL;
611         connman_bool_t offlinemode;
612         char *name;
613
614         DBG("profile %p", profile);
615
616         keyfile = __connman_storage_open(profile->ident);
617         if (keyfile == NULL)
618                 return -EIO;
619
620         name = g_key_file_get_string(keyfile, "global", "Name", NULL);
621         if (name != NULL) {
622                 g_free(profile->name);
623                 profile->name = name;
624         }
625
626         offlinemode = g_key_file_get_boolean(keyfile, "global",
627                                                 "OfflineMode", &error);
628         if (error == NULL)
629                 profile->offlinemode = offlinemode;
630         g_clear_error(&error);
631
632         __connman_storage_close(profile->ident, keyfile, FALSE);
633
634         return 0;
635 }
636
637 static int profile_save(struct connman_profile *profile)
638 {
639         GKeyFile *keyfile;
640
641         DBG("profile %p", profile);
642
643         keyfile = __connman_storage_open(profile->ident);
644         if (keyfile == NULL)
645                 return -EIO;
646
647         if (profile->name != NULL)
648                 g_key_file_set_string(keyfile, "global",
649                                                 "Name", profile->name);
650
651         g_key_file_set_boolean(keyfile, "global",
652                                         "OfflineMode", profile->offlinemode);
653
654         __connman_storage_close(profile->ident, keyfile, TRUE);
655
656         return 0;
657 }
658
659 static struct connman_storage profile_storage = {
660         .name           = "profile",
661         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
662         .profile_init   = profile_init,
663         .profile_load   = profile_load,
664         .profile_save   = profile_save,
665 };
666
667 int __connman_profile_init(void)
668 {
669         DBG("");
670
671         connection = connman_dbus_get_connection();
672         if (connection == NULL)
673                 return -1;
674
675         if (connman_storage_register(&profile_storage) < 0)
676                 connman_error("Failed to register profile storage");
677
678         profile_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
679                                                 g_free, unregister_profile);
680
681         return 0;
682 }
683
684 void __connman_profile_cleanup(void)
685 {
686         DBG("");
687
688         if (connection == NULL)
689                 return;
690
691         g_hash_table_destroy(profile_hash);
692         profile_hash = NULL;
693
694         connman_storage_unregister(&profile_storage);
695
696         dbus_connection_unref(connection);
697 }