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