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