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