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