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