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