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