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