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