Fix service state signal emission and error handling
[connman] / src / service.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 <gdbus.h>
27
28 #include "connman.h"
29
30 static DBusConnection *connection = NULL;
31
32 static GSequence *service_list = NULL;
33 static GHashTable *service_hash = NULL;
34
35 struct connman_service {
36         gint refcount;
37         char *identifier;
38         char *path;
39         enum connman_service_type type;
40         enum connman_service_mode mode;
41         enum connman_service_security security;
42         enum connman_service_state state;
43         connman_uint8_t strength;
44         connman_bool_t favorite;
45         unsigned int order;
46         char *name;
47         char *passphrase;
48         char *profile;
49         struct connman_device *device;
50         struct connman_network *network;
51 };
52
53 static void append_path(gpointer value, gpointer user_data)
54 {
55         struct connman_service *service = value;
56         DBusMessageIter *iter = user_data;
57
58         if (service->path == NULL)
59                 return;
60
61         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
62                                                         &service->path);
63 }
64
65 void __connman_service_list(DBusMessageIter *iter)
66 {
67         DBG("");
68
69         g_sequence_foreach(service_list, append_path, iter);
70 }
71
72 static const char *type2string(enum connman_service_type type)
73 {
74         switch (type) {
75         case CONNMAN_SERVICE_TYPE_UNKNOWN:
76                 break;
77         case CONNMAN_SERVICE_TYPE_ETHERNET:
78                 return "ethernet";
79         case CONNMAN_SERVICE_TYPE_WIFI:
80                 return "wifi";
81         case CONNMAN_SERVICE_TYPE_WIMAX:
82                 return "wimax";
83         }
84
85         return NULL;
86 }
87
88 static const char *mode2string(enum connman_service_mode mode)
89 {
90         switch (mode) {
91         case CONNMAN_SERVICE_MODE_UNKNOWN:
92                 break;
93         case CONNMAN_SERVICE_MODE_MANAGED:
94                 return "managed";
95         case CONNMAN_SERVICE_MODE_ADHOC:
96                 return "adhoc";
97         }
98
99         return NULL;
100 }
101
102 static const char *security2string(enum connman_service_security security)
103 {
104         switch (security) {
105         case CONNMAN_SERVICE_SECURITY_UNKNOWN:
106                 break;
107         case CONNMAN_SERVICE_SECURITY_NONE:
108                 return "none";
109         case CONNMAN_SERVICE_SECURITY_WEP:
110                 return "wep";
111         case CONNMAN_SERVICE_SECURITY_WPA:
112                 return "wpa";
113         case CONNMAN_SERVICE_SECURITY_WPA2:
114                 return "wpa2";
115         }
116
117         return NULL;
118 }
119
120 static const char *state2string(enum connman_service_state state)
121 {
122         switch (state) {
123         case CONNMAN_SERVICE_STATE_UNKNOWN:
124                 break;
125         case CONNMAN_SERVICE_STATE_IDLE:
126                 return "idle";
127         case CONNMAN_SERVICE_STATE_CARRIER:
128                 return "carrier";
129         case CONNMAN_SERVICE_STATE_ASSOCIATION:
130                 return "association";
131         case CONNMAN_SERVICE_STATE_CONFIGURATION:
132                 return "configuration";
133         case CONNMAN_SERVICE_STATE_READY:
134                 return "ready";
135         case CONNMAN_SERVICE_STATE_DISCONNECT:
136                 return "disconnect";
137         case CONNMAN_SERVICE_STATE_FAILURE:
138                 return "failure";
139         }
140
141         return NULL;
142 }
143
144 static void state_changed(struct connman_service *service)
145 {
146         DBusMessage *signal;
147         DBusMessageIter entry, value;
148         const char *str, *key = "State";
149
150         if (service->path == NULL)
151                 return;
152
153         str = state2string(service->state);
154         if (str == NULL)
155                 return;
156
157         signal = dbus_message_new_signal(service->path,
158                                 CONNMAN_SERVICE_INTERFACE, "PropertyChanged");
159         if (signal == NULL)
160                 return;
161
162         dbus_message_iter_init_append(signal, &entry);
163
164         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
165
166         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
167                                         DBUS_TYPE_STRING_AS_STRING, &value);
168         dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
169         dbus_message_iter_close_container(&entry, &value);
170
171         g_dbus_send_message(connection, signal);
172 }
173
174 static DBusMessage *get_properties(DBusConnection *conn,
175                                         DBusMessage *msg, void *data)
176 {
177         struct connman_service *service = data;
178         DBusMessage *reply;
179         DBusMessageIter array, dict;
180         const char *str;
181
182         DBG("conn %p", conn);
183
184         reply = dbus_message_new_method_return(msg);
185         if (reply == NULL)
186                 return NULL;
187
188         dbus_message_iter_init_append(reply, &array);
189
190         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
191                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
192                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
193                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
194
195         str = type2string(service->type);
196         if (str != NULL)
197                 connman_dbus_dict_append_variant(&dict, "Type",
198                                                 DBUS_TYPE_STRING, &str);
199
200         str = mode2string(service->mode);
201         if (str != NULL)
202                 connman_dbus_dict_append_variant(&dict, "Mode",
203                                                 DBUS_TYPE_STRING, &str);
204
205         str = security2string(service->security);
206         if (str != NULL)
207                 connman_dbus_dict_append_variant(&dict, "Security",
208                                                 DBUS_TYPE_STRING, &str);
209
210         str = state2string(service->state);
211         if (str != NULL)
212                 connman_dbus_dict_append_variant(&dict, "State",
213                                                 DBUS_TYPE_STRING, &str);
214
215         if (service->strength > 0)
216                 connman_dbus_dict_append_variant(&dict, "Strength",
217                                         DBUS_TYPE_BYTE, &service->strength);
218
219         connman_dbus_dict_append_variant(&dict, "Favorite",
220                                         DBUS_TYPE_BOOLEAN, &service->favorite);
221
222         if (service->name != NULL)
223                 connman_dbus_dict_append_variant(&dict, "Name",
224                                         DBUS_TYPE_STRING, &service->name);
225
226         if (service->passphrase != NULL &&
227                         __connman_security_check_privilege(msg,
228                                 CONNMAN_SECURITY_PRIVILEGE_SECRET) == 0)
229                 connman_dbus_dict_append_variant(&dict, "Passphrase",
230                                 DBUS_TYPE_STRING, &service->passphrase);
231
232         dbus_message_iter_close_container(&array, &dict);
233
234         return reply;
235 }
236
237 static DBusMessage *set_property(DBusConnection *conn,
238                                         DBusMessage *msg, void *data)
239 {
240         struct connman_service *service = data;
241         DBusMessageIter iter, value;
242         const char *name;
243         int type;
244
245         DBG("conn %p", conn);
246
247         if (dbus_message_iter_init(msg, &iter) == FALSE)
248                 return __connman_error_invalid_arguments(msg);
249
250         dbus_message_iter_get_basic(&iter, &name);
251         dbus_message_iter_next(&iter);
252         dbus_message_iter_recurse(&iter, &value);
253
254         if (__connman_security_check_privilege(msg,
255                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
256                 return __connman_error_permission_denied(msg);
257
258         type = dbus_message_iter_get_arg_type(&value);
259
260         if (g_str_equal(name, "Passphrase") == TRUE) {
261                 const char *passphrase;
262
263                 if (type != DBUS_TYPE_STRING)
264                         return __connman_error_invalid_arguments(msg);
265
266                 if (__connman_security_check_privilege(msg,
267                                         CONNMAN_SECURITY_PRIVILEGE_SECRET) < 0)
268                         return __connman_error_permission_denied(msg);
269
270                 dbus_message_iter_get_basic(&value, &passphrase);
271
272                 g_free(service->passphrase);
273                 service->passphrase = g_strdup(passphrase);
274
275                 if (service->network != NULL)
276                         connman_network_set_string(service->network,
277                                 "WiFi.Passphrase", service->passphrase);
278
279                 __connman_storage_save_service(service);
280         }
281
282         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
283 }
284
285 static DBusMessage *connect_service(DBusConnection *conn,
286                                         DBusMessage *msg, void *data)
287 {
288         struct connman_service *service = data;
289
290         if (service->network != NULL) {
291                 int err;
292
293                 connman_network_set_string(service->network,
294                                 "WiFi.Passphrase", service->passphrase);
295
296                 err = connman_network_connect(service->network);
297                 if (err < 0 && err != -EINPROGRESS)
298                         return __connman_error_failed(msg, -err);
299
300                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
301         } else if (service->device != NULL) {
302                 if (service->favorite == FALSE)
303                         return __connman_error_no_carrier(msg);
304
305                 if (__connman_device_connect(service->device) < 0)
306                         return __connman_error_failed(msg, EINVAL);
307
308                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
309         }
310
311         return __connman_error_not_supported(msg);
312 }
313
314 static DBusMessage *disconnect_service(DBusConnection *conn,
315                                         DBusMessage *msg, void *data)
316 {
317         struct connman_service *service = data;
318
319         if (service->network != NULL) {
320                 int err;
321
322                 err = __connman_network_disconnect(service->network);
323                 if (err < 0 && err != -EINPROGRESS)
324                         return __connman_error_failed(msg, -err);
325
326                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
327         } else if (service->device != NULL) {
328                 int err;
329
330                 if (service->favorite == FALSE)
331                         return __connman_error_no_carrier(msg);
332
333                 err = __connman_device_disconnect(service->device);
334                 if (err < 0)
335                         return __connman_error_failed(msg, -err);
336
337                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
338         }
339
340         return __connman_error_not_supported(msg);
341 }
342
343 static DBusMessage *remove_service(DBusConnection *conn,
344                                         DBusMessage *msg, void *data)
345 {
346         struct connman_service *service = data;
347
348         if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET)
349                 return __connman_error_not_supported(msg);
350
351         if (service->network != NULL) {
352                 int err;
353
354                 err = __connman_network_disconnect(service->network);
355                 if (err < 0 && err != -EINPROGRESS)
356                         return __connman_error_failed(msg, -err);
357         }
358
359         connman_service_set_favorite(service, FALSE);
360
361         __connman_storage_save_service(service);
362
363         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
364 }
365
366 static DBusMessage *move_before(DBusConnection *conn,
367                                         DBusMessage *msg, void *data)
368 {
369         struct connman_service *service = data;
370
371         if (service->favorite == FALSE)
372                 return __connman_error_not_supported(msg);
373
374         return __connman_error_not_implemented(msg);
375 }
376
377 static DBusMessage *move_after(DBusConnection *conn,
378                                         DBusMessage *msg, void *data)
379 {
380         struct connman_service *service = data;
381
382         if (service->favorite == FALSE)
383                 return __connman_error_not_supported(msg);
384
385         return __connman_error_not_implemented(msg);
386 }
387
388 static GDBusMethodTable service_methods[] = {
389         { "GetProperties", "",   "a{sv}", get_properties     },
390         { "SetProperty",   "sv", "",      set_property       },
391         { "Connect",       "",   "",      connect_service    },
392         { "Disconnect",    "",   "",      disconnect_service },
393         { "Remove",        "",   "",      remove_service     },
394         { "MoveBefore",    "o",  "",      move_before        },
395         { "MoveAfter",     "o",  "",      move_after         },
396         { },
397 };
398
399 static GDBusSignalTable service_signals[] = {
400         { "PropertyChanged", "sv" },
401         { },
402 };
403
404 static void service_free(gpointer data)
405 {
406         struct connman_service *service = data;
407         char *path = service->path;
408
409         DBG("service %p", service);
410
411         g_hash_table_remove(service_hash, service->identifier);
412
413         service->path = NULL;
414
415         if (path != NULL) {
416                 __connman_profile_changed();
417
418                 g_dbus_unregister_interface(connection, path,
419                                                 CONNMAN_SERVICE_INTERFACE);
420                 g_free(path);
421         }
422
423         if (service->network != NULL)
424                 connman_network_unref(service->network);
425
426         g_free(service->profile);
427         g_free(service->name);
428         g_free(service->passphrase);
429         g_free(service->identifier);
430         g_free(service);
431 }
432
433 /**
434  * connman_service_put:
435  * @service: service structure
436  *
437  * Release service if no longer needed
438  */
439 void connman_service_put(struct connman_service *service)
440 {
441         DBG("service %p", service);
442
443         if (g_atomic_int_dec_and_test(&service->refcount) == TRUE) {
444                 GSequenceIter *iter;
445
446                 iter = g_hash_table_lookup(service_hash, service->identifier);
447                 if (iter != NULL)
448                         g_sequence_remove(iter);
449                 else
450                         service_free(service);
451         }
452 }
453
454 static void __connman_service_initialize(struct connman_service *service)
455 {
456         DBG("service %p", service);
457
458         service->refcount = 1;
459
460         service->type     = CONNMAN_SERVICE_TYPE_UNKNOWN;
461         service->mode     = CONNMAN_SERVICE_MODE_UNKNOWN;
462         service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN;
463         service->state    = CONNMAN_SERVICE_STATE_UNKNOWN;
464
465         service->favorite = FALSE;
466
467         service->order = 0;
468 }
469
470 /**
471  * connman_service_create:
472  *
473  * Allocate a new service.
474  *
475  * Returns: a newly-allocated #connman_service structure
476  */
477 struct connman_service *connman_service_create(void)
478 {
479         struct connman_service *service;
480
481         service = g_try_new0(struct connman_service, 1);
482         if (service == NULL)
483                 return NULL;
484
485         DBG("service %p", service);
486
487         __connman_service_initialize(service);
488
489         return service;
490 }
491
492 /**
493  * connman_service_ref:
494  * @service: service structure
495  *
496  * Increase reference counter of service
497  */
498 struct connman_service *connman_service_ref(struct connman_service *service)
499 {
500         g_atomic_int_inc(&service->refcount);
501
502         return service;
503 }
504
505 /**
506  * connman_service_unref:
507  * @service: service structure
508  *
509  * Decrease reference counter of service
510  */
511 void connman_service_unref(struct connman_service *service)
512 {
513         connman_service_put(service);
514 }
515
516 static gint service_compare(gconstpointer a, gconstpointer b,
517                                                         gpointer user_data)
518 {
519         struct connman_service *service_a = (void *) a;
520         struct connman_service *service_b = (void *) b;
521
522         if (service_a->order > service_b->order)
523                 return -1;
524
525         if (service_a->order < service_b->order)
526                 return 1;
527
528         if (service_a->favorite == TRUE && service_b->favorite == FALSE)
529                 return -1;
530
531         if (service_a->favorite == FALSE && service_b->favorite == TRUE)
532                 return 1;
533
534         return (gint) service_b->strength - (gint) service_a->strength;
535 }
536
537 /**
538  * connman_service_set_favorite:
539  * @service: service structure
540  * @favorite: favorite value
541  *
542  * Change the favorite setting of service
543  */
544 int connman_service_set_favorite(struct connman_service *service,
545                                                 connman_bool_t favorite)
546 {
547         GSequenceIter *iter;
548
549         iter = g_hash_table_lookup(service_hash, service->identifier);
550         if (iter == NULL)
551                 return -ENOENT;
552
553         if (service->favorite)
554                 return -EALREADY;
555
556         service->favorite = favorite;
557
558         g_sequence_sort_changed(iter, service_compare, NULL);
559
560         __connman_profile_changed();
561
562         return 0;
563 }
564
565 int __connman_service_set_carrier(struct connman_service *service,
566                                                 connman_bool_t carrier)
567 {
568         DBG("service %p carrier %d", service, carrier);
569
570         if (service == NULL)
571                 return -EINVAL;
572
573         switch (service->type) {
574         case CONNMAN_SERVICE_TYPE_UNKNOWN:
575         case CONNMAN_SERVICE_TYPE_WIFI:
576         case CONNMAN_SERVICE_TYPE_WIMAX:
577                 return -EINVAL;
578         case CONNMAN_SERVICE_TYPE_ETHERNET:
579                 break;
580         }
581
582         if (carrier == FALSE) {
583                 service->state = CONNMAN_SERVICE_STATE_DISCONNECT;
584                 state_changed(service);
585
586                 service->state = CONNMAN_SERVICE_STATE_IDLE;
587                 state_changed(service);
588         } else {
589                 service->state = CONNMAN_SERVICE_STATE_CARRIER;
590                 state_changed(service);
591         }
592
593         return connman_service_set_favorite(service, carrier);
594 }
595
596 int __connman_service_indicate_state(struct connman_service *service,
597                                         enum connman_service_state state)
598 {
599         DBG("service %p state %d", service, state);
600
601         if (service == NULL)
602                 return -EINVAL;
603
604         if (state == CONNMAN_SERVICE_STATE_CARRIER)
605                 return __connman_service_set_carrier(service, TRUE);
606
607         if (service->state == state)
608                 return -EALREADY;
609
610         if (service->state == CONNMAN_SERVICE_STATE_IDLE &&
611                                 state == CONNMAN_SERVICE_STATE_DISCONNECT)
612                 return -EINVAL;
613
614         if (state == CONNMAN_SERVICE_STATE_IDLE &&
615                         service->state != CONNMAN_SERVICE_STATE_DISCONNECT) {
616                 service->state = CONNMAN_SERVICE_STATE_DISCONNECT;
617                 state_changed(service);
618         }
619
620         service->state = state;
621         state_changed(service);
622
623         if (state == CONNMAN_SERVICE_STATE_READY)
624                 return connman_service_set_favorite(service, TRUE);
625
626         return 0;
627 }
628
629 /**
630  * connman_service_lookup:
631  * @identifier: service identifier
632  *
633  * Look up a service by identifier (reference count will not be increased)
634  */
635 struct connman_service *connman_service_lookup(const char *identifier)
636 {
637         GSequenceIter *iter;
638
639         iter = g_hash_table_lookup(service_hash, identifier);
640         if (iter != NULL)
641                 return g_sequence_get(iter);
642
643         return NULL;
644 }
645
646 /**
647  * connman_service_get:
648  * @identifier: service identifier
649  *
650  * Look up a service by identifier or create a new one if not found
651  */
652 struct connman_service *connman_service_get(const char *identifier)
653 {
654         struct connman_service *service;
655         GSequenceIter *iter;
656
657         iter = g_hash_table_lookup(service_hash, identifier);
658         if (iter != NULL) {
659                 service = g_sequence_get(iter);
660                 if (service != NULL)
661                         g_atomic_int_inc(&service->refcount);
662                 return service;
663         }
664
665         service = g_try_new0(struct connman_service, 1);
666         if (service == NULL)
667                 return NULL;
668
669         DBG("service %p", service);
670
671         __connman_service_initialize(service);
672
673         service->identifier = g_strdup(identifier);
674
675         service->profile = g_strdup(__connman_profile_active_ident());
676
677         __connman_storage_load_service(service);
678
679         iter = g_sequence_insert_sorted(service_list, service,
680                                                 service_compare, NULL);
681
682         g_hash_table_insert(service_hash, service->identifier, iter);
683
684         return service;
685 }
686
687 static int service_register(struct connman_service *service)
688 {
689         const char *path = __connman_profile_active_path();
690
691         DBG("service %p", service);
692
693         if (service->path != NULL)
694                 return -EALREADY;
695
696         service->path = g_strdup_printf("%s/%s", path, service->identifier);
697
698         DBG("path %s", service->path);
699
700         g_dbus_register_interface(connection, service->path,
701                                         CONNMAN_SERVICE_INTERFACE,
702                                         service_methods, service_signals,
703                                                         NULL, service, NULL);
704
705         __connman_profile_changed();
706
707         return 0;
708 }
709
710 /**
711  * connman_service_lookup_from_device:
712  * @device: device structure
713  *
714  * Look up a service by device (reference count will not be increased)
715  */
716 struct connman_service *__connman_service_lookup_from_device(struct connman_device *device)
717 {
718         struct connman_service *service;
719         const char *ident;
720         char *name;
721
722         ident = __connman_device_get_ident(device);
723         if (ident == NULL)
724                 return NULL;
725
726         name = g_strdup_printf("%s_%s",
727                                 __connman_device_get_type(device), ident);
728
729         service = connman_service_lookup(name);
730
731         g_free(name);
732
733         return service;
734 }
735
736 static enum connman_service_type convert_device_type(struct connman_device *device)
737 {
738         enum connman_device_type type = connman_device_get_type(device);
739
740         switch (type) {
741         case CONNMAN_DEVICE_TYPE_UNKNOWN:
742         case CONNMAN_DEVICE_TYPE_VENDOR:
743         case CONNMAN_DEVICE_TYPE_WIFI:
744         case CONNMAN_DEVICE_TYPE_WIMAX:
745         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
746         case CONNMAN_DEVICE_TYPE_GPS:
747         case CONNMAN_DEVICE_TYPE_HSO:
748         case CONNMAN_DEVICE_TYPE_NOZOMI:
749         case CONNMAN_DEVICE_TYPE_HUAWEI:
750         case CONNMAN_DEVICE_TYPE_NOVATEL:
751                 break;
752         case CONNMAN_DEVICE_TYPE_ETHERNET:
753                 return CONNMAN_SERVICE_TYPE_ETHERNET;
754         }
755
756         return CONNMAN_SERVICE_TYPE_UNKNOWN;
757 }
758
759 /**
760  * connman_service_create_from_device:
761  * @device: device structure
762  *
763  * Look up service by device and if not found, create one
764  */
765 struct connman_service *__connman_service_create_from_device(struct connman_device *device)
766 {
767         struct connman_service *service;
768         const char *ident;
769         char *name;
770
771         ident = __connman_device_get_ident(device);
772         if (ident == NULL)
773                 return NULL;
774
775         name = g_strdup_printf("%s_%s",
776                                 __connman_device_get_type(device), ident);
777
778         service = connman_service_get(name);
779         if (service == NULL)
780                 goto done;
781
782         if (service->path != NULL) {
783                 connman_service_put(service);
784                 service = NULL;
785                 goto done;
786         }
787
788         service->type = convert_device_type(device);
789
790         service->device = device;
791
792         service_register(service);
793
794 done:
795         g_free(name);
796
797         return service;
798 }
799
800 /**
801  * connman_service_lookup_from_network:
802  * @network: network structure
803  *
804  * Look up a service by network (reference count will not be increased)
805  */
806 struct connman_service *__connman_service_lookup_from_network(struct connman_network *network)
807 {
808         struct connman_service *service;
809         const char *ident, *group;
810         char *name;
811
812         ident = __connman_network_get_ident(network);
813         if (ident == NULL)
814                 return NULL;
815
816         group = __connman_network_get_group(network);
817         if (group == NULL)
818                 return NULL;
819
820         name = g_strdup_printf("%s_%s_%s",
821                         __connman_network_get_type(network), ident, group);
822
823         service = connman_service_lookup(name);
824
825         g_free(name);
826
827         return service;
828 }
829
830 static enum connman_service_type convert_network_type(struct connman_network *network)
831 {
832         enum connman_network_type type = connman_network_get_type(network);
833
834         switch (type) {
835         case CONNMAN_NETWORK_TYPE_UNKNOWN:
836         case CONNMAN_NETWORK_TYPE_VENDOR:
837         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
838         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
839         case CONNMAN_NETWORK_TYPE_HSO:
840                 break;
841         case CONNMAN_NETWORK_TYPE_WIFI:
842                 return CONNMAN_SERVICE_TYPE_WIFI;
843         case CONNMAN_NETWORK_TYPE_WIMAX:
844                 return CONNMAN_SERVICE_TYPE_WIMAX;
845         }
846
847         return CONNMAN_SERVICE_TYPE_UNKNOWN;
848 }
849
850 static enum connman_service_mode convert_wifi_mode(const char *mode)
851 {
852         if (mode == NULL)
853                 return CONNMAN_SERVICE_MODE_UNKNOWN;
854         else if (g_str_equal(mode, "managed") == TRUE)
855                 return CONNMAN_SERVICE_MODE_MANAGED;
856         else if (g_str_equal(mode, "adhoc") == TRUE)
857                 return CONNMAN_SERVICE_MODE_ADHOC;
858         else
859                 return CONNMAN_SERVICE_MODE_UNKNOWN;
860 }
861
862 static enum connman_service_mode convert_wifi_security(const char *security)
863 {
864         if (security == NULL)
865                 return CONNMAN_SERVICE_SECURITY_UNKNOWN;
866         else if (g_str_equal(security, "none") == TRUE)
867                 return CONNMAN_SERVICE_SECURITY_NONE;
868         else if (g_str_equal(security, "wep") == TRUE)
869                 return CONNMAN_SERVICE_SECURITY_WEP;
870         else if (g_str_equal(security, "wpa") == TRUE)
871                 return CONNMAN_SERVICE_SECURITY_WPA;
872         else if (g_str_equal(security, "wpa2") == TRUE)
873                 return CONNMAN_SERVICE_SECURITY_WPA2;
874         else
875                 return CONNMAN_SERVICE_SECURITY_UNKNOWN;
876 }
877
878 static void update_from_network(struct connman_service *service,
879                                         struct connman_network *network)
880 {
881         connman_uint8_t strength = service->strength;
882         GSequenceIter *iter;
883         const char *str;
884
885         str = connman_network_get_string(network, "Name");
886         if (str != NULL) {
887                 g_free(service->name);
888                 service->name = g_strdup(str);
889         }
890
891         service->strength = connman_network_get_uint8(network, "Strength");
892
893         str = connman_network_get_string(network, "WiFi.Mode");
894         service->mode = convert_wifi_mode(str);
895
896         str = connman_network_get_string(network, "WiFi.Security");
897         service->security = convert_wifi_security(str);
898
899         if (service->strength > strength && service->network != NULL) {
900                 connman_network_unref(service->network);
901                 service->network = NULL;
902         }
903
904         if (service->network == NULL) {
905                 service->network = connman_network_ref(network);
906
907                 str = connman_network_get_string(network, "WiFi.Passphrase");
908                 if (str != NULL) {
909                         g_free(service->passphrase);
910                         service->passphrase = g_strdup(str);
911                 }
912         }
913
914         iter = g_hash_table_lookup(service_hash, service->identifier);
915         if (iter != NULL)
916                 g_sequence_sort_changed(iter, service_compare, NULL);
917 }
918
919 /**
920  * connman_service_create_from_network:
921  * @network: network structure
922  *
923  * Look up service by network and if not found, create one
924  */
925 struct connman_service *__connman_service_create_from_network(struct connman_network *network)
926 {
927         struct connman_service *service;
928         const char *ident, *group;
929         char *name;
930
931         ident = __connman_network_get_ident(network);
932         if (ident == NULL)
933                 return NULL;
934
935         group = __connman_network_get_group(network);
936         if (group == NULL)
937                 return NULL;
938
939         name = g_strdup_printf("%s_%s_%s",
940                         __connman_network_get_type(network), ident, group);
941
942         service = connman_service_get(name);
943         if (service == NULL)
944                 goto done;
945
946         if (service->path != NULL) {
947                 update_from_network(service, network);
948
949                 __connman_profile_changed();
950
951                 connman_service_put(service);
952                 service = NULL;
953                 goto done;
954         }
955
956         service->type = convert_network_type(network);
957
958         update_from_network(service, network);
959
960         service_register(service);
961
962 done:
963         g_free(name);
964
965         return service;
966 }
967
968 static int service_load(struct connman_service *service)
969 {
970         GKeyFile *keyfile;
971         gchar *pathname, *data = NULL;
972         gsize length;
973         char *str;
974
975         DBG("service %p", service);
976
977         if (service->profile == NULL)
978                 return -EINVAL;
979
980         pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, service->profile);
981         if (pathname == NULL)
982                 return -ENOMEM;
983
984         keyfile = g_key_file_new();
985
986         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) {
987                 g_free(pathname);
988                 return -ENOENT;
989         }
990
991         g_free(pathname);
992
993         if (g_key_file_load_from_data(keyfile, data, length,
994                                                         0, NULL) == FALSE) {
995                 g_free(data);
996                 return -EILSEQ;
997         }
998
999         g_free(data);
1000
1001         str = g_key_file_get_string(keyfile,
1002                                 service->identifier, "Passphrase", NULL);
1003         if (str != NULL) {
1004                 g_free(service->passphrase);
1005                 service->passphrase = str;
1006         }
1007
1008         g_key_file_free(keyfile);
1009
1010         return 0;
1011 }
1012
1013 static int service_save(struct connman_service *service)
1014 {
1015         GKeyFile *keyfile;
1016         gchar *pathname, *data = NULL;
1017         gsize length;
1018
1019         DBG("service %p", service);
1020
1021         if (service->profile == NULL)
1022                 return -EINVAL;
1023
1024         pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, service->profile);
1025         if (pathname == NULL)
1026                 return -ENOMEM;
1027
1028         keyfile = g_key_file_new();
1029
1030         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE)
1031                 goto update;
1032
1033         if (length > 0) {
1034                 if (g_key_file_load_from_data(keyfile, data, length,
1035                                                         0, NULL) == FALSE)
1036                         goto done;
1037         }
1038
1039         g_free(data);
1040
1041 update:
1042         if (service->passphrase != NULL)
1043                 g_key_file_set_string(keyfile, service->identifier,
1044                                         "Passphrase", service->passphrase);
1045
1046         data = g_key_file_to_data(keyfile, &length, NULL);
1047
1048         if (g_file_set_contents(pathname, data, length, NULL) == FALSE)
1049                 connman_error("Failed to store service information");
1050
1051 done:
1052         g_free(data);
1053
1054         g_key_file_free(keyfile);
1055
1056         g_free(pathname);
1057
1058         return 0;
1059 }
1060
1061 static struct connman_storage service_storage = {
1062         .name           = "service",
1063         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
1064         .service_load   = service_load,
1065         .service_save   = service_save,
1066 };
1067
1068 int __connman_service_init(void)
1069 {
1070         DBG("");
1071
1072         connection = connman_dbus_get_connection();
1073
1074         if (connman_storage_register(&service_storage) < 0)
1075                 connman_error("Failed to register service storage");
1076
1077         service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1078                                                                 NULL, NULL);
1079
1080         service_list = g_sequence_new(service_free);
1081
1082         return 0;
1083 }
1084
1085 void __connman_service_cleanup(void)
1086 {
1087         DBG("");
1088
1089         g_sequence_free(service_list);
1090         service_list = NULL;
1091
1092         g_hash_table_destroy(service_hash);
1093         service_hash = NULL;
1094
1095         connman_storage_unregister(&service_storage);
1096
1097         dbus_connection_unref(connection);
1098 }