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