Have services remove method also clear the passphrase
[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_HSO:
1093         case CONNMAN_DEVICE_TYPE_NOZOMI:
1094         case CONNMAN_DEVICE_TYPE_HUAWEI:
1095         case CONNMAN_DEVICE_TYPE_NOVATEL:
1096                 break;
1097         case CONNMAN_DEVICE_TYPE_ETHERNET:
1098                 return CONNMAN_SERVICE_TYPE_ETHERNET;
1099         }
1100
1101         return CONNMAN_SERVICE_TYPE_UNKNOWN;
1102 }
1103
1104 /**
1105  * connman_service_create_from_device:
1106  * @device: device structure
1107  *
1108  * Look up service by device and if not found, create one
1109  */
1110 struct connman_service *__connman_service_create_from_device(struct connman_device *device)
1111 {
1112         struct connman_service *service;
1113         const char *ident;
1114         char *name;
1115
1116         ident = __connman_device_get_ident(device);
1117         if (ident == NULL)
1118                 return NULL;
1119
1120         name = g_strdup_printf("%s_%s",
1121                                 __connman_device_get_type(device), ident);
1122
1123         service = __connman_service_get(name);
1124         if (service == NULL)
1125                 goto done;
1126
1127         if (service->path != NULL) {
1128                 __connman_service_put(service);
1129                 service = NULL;
1130                 goto done;
1131         }
1132
1133         service->type = convert_device_type(device);
1134
1135         service->device = device;
1136
1137         service_register(service);
1138
1139 done:
1140         g_free(name);
1141
1142         return service;
1143 }
1144
1145 /**
1146  * connman_service_lookup_from_network:
1147  * @network: network structure
1148  *
1149  * Look up a service by network (reference count will not be increased)
1150  */
1151 struct connman_service *__connman_service_lookup_from_network(struct connman_network *network)
1152 {
1153         struct connman_service *service;
1154         const char *ident, *group;
1155         char *name;
1156
1157         ident = __connman_network_get_ident(network);
1158         if (ident == NULL)
1159                 return NULL;
1160
1161         group = __connman_network_get_group(network);
1162         if (group == NULL)
1163                 return NULL;
1164
1165         name = g_strdup_printf("%s_%s_%s",
1166                         __connman_network_get_type(network), ident, group);
1167
1168         service = __connman_service_lookup(name);
1169
1170         g_free(name);
1171
1172         return service;
1173 }
1174
1175 unsigned int __connman_service_get_order(struct connman_service *service)
1176 {
1177         return service->order;
1178 }
1179
1180 static enum connman_service_type convert_network_type(struct connman_network *network)
1181 {
1182         enum connman_network_type type = connman_network_get_type(network);
1183
1184         switch (type) {
1185         case CONNMAN_NETWORK_TYPE_UNKNOWN:
1186         case CONNMAN_NETWORK_TYPE_VENDOR:
1187         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
1188         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
1189         case CONNMAN_NETWORK_TYPE_HSO:
1190                 break;
1191         case CONNMAN_NETWORK_TYPE_WIFI:
1192                 return CONNMAN_SERVICE_TYPE_WIFI;
1193         case CONNMAN_NETWORK_TYPE_WIMAX:
1194                 return CONNMAN_SERVICE_TYPE_WIMAX;
1195         }
1196
1197         return CONNMAN_SERVICE_TYPE_UNKNOWN;
1198 }
1199
1200 static enum connman_service_mode convert_wifi_mode(const char *mode)
1201 {
1202         if (mode == NULL)
1203                 return CONNMAN_SERVICE_MODE_UNKNOWN;
1204         else if (g_str_equal(mode, "managed") == TRUE)
1205                 return CONNMAN_SERVICE_MODE_MANAGED;
1206         else if (g_str_equal(mode, "adhoc") == TRUE)
1207                 return CONNMAN_SERVICE_MODE_ADHOC;
1208         else
1209                 return CONNMAN_SERVICE_MODE_UNKNOWN;
1210 }
1211
1212 static enum connman_service_mode convert_wifi_security(const char *security)
1213 {
1214         if (security == NULL)
1215                 return CONNMAN_SERVICE_SECURITY_UNKNOWN;
1216         else if (g_str_equal(security, "none") == TRUE)
1217                 return CONNMAN_SERVICE_SECURITY_NONE;
1218         else if (g_str_equal(security, "wep") == TRUE)
1219                 return CONNMAN_SERVICE_SECURITY_WEP;
1220         else if (g_str_equal(security, "wpa") == TRUE)
1221                 return CONNMAN_SERVICE_SECURITY_WPA;
1222         else if (g_str_equal(security, "rsn") == TRUE)
1223                 return CONNMAN_SERVICE_SECURITY_RSN;
1224         else
1225                 return CONNMAN_SERVICE_SECURITY_UNKNOWN;
1226 }
1227
1228 static void update_from_network(struct connman_service *service,
1229                                         struct connman_network *network)
1230 {
1231         connman_uint8_t strength = service->strength;
1232         GSequenceIter *iter;
1233         const char *str;
1234
1235         str = connman_network_get_string(network, "Name");
1236         if (str != NULL) {
1237                 g_free(service->name);
1238                 service->name = g_strdup(str);
1239                 service->hidden = FALSE;
1240         } else {
1241                 g_free(service->name);
1242                 service->name = NULL;
1243                 service->hidden = TRUE;
1244         }
1245
1246         service->strength = connman_network_get_uint8(network, "Strength");
1247
1248         str = connman_network_get_string(network, "WiFi.Mode");
1249         service->mode = convert_wifi_mode(str);
1250
1251         str = connman_network_get_string(network, "WiFi.Security");
1252         service->security = convert_wifi_security(str);
1253
1254         if (service->strength > strength && service->network != NULL) {
1255                 connman_network_unref(service->network);
1256                 service->network = NULL;
1257
1258                 strength_changed(service);
1259         }
1260
1261         if (service->network == NULL) {
1262                 service->network = connman_network_ref(network);
1263
1264                 str = connman_network_get_string(network, "WiFi.Passphrase");
1265                 if (str != NULL) {
1266                         g_free(service->passphrase);
1267                         service->passphrase = g_strdup(str);
1268                 }
1269         }
1270
1271         iter = g_hash_table_lookup(service_hash, service->identifier);
1272         if (iter != NULL)
1273                 g_sequence_sort_changed(iter, service_compare, NULL);
1274 }
1275
1276 /**
1277  * connman_service_create_from_network:
1278  * @network: network structure
1279  *
1280  * Look up service by network and if not found, create one
1281  */
1282 struct connman_service *__connman_service_create_from_network(struct connman_network *network)
1283 {
1284         struct connman_service *service;
1285         const char *ident, *group;
1286         char *name;
1287
1288         if (__connman_service_lookup_from_network(network) != NULL) {
1289                 connman_error("Service already exists");
1290                 return NULL;
1291         }
1292
1293         ident = __connman_network_get_ident(network);
1294         if (ident == NULL)
1295                 return NULL;
1296
1297         group = __connman_network_get_group(network);
1298         if (group == NULL)
1299                 return NULL;
1300
1301         name = g_strdup_printf("%s_%s_%s",
1302                         __connman_network_get_type(network), ident, group);
1303
1304         service = __connman_service_get(name);
1305         if (service == NULL)
1306                 goto done;
1307
1308         if (service->path != NULL) {
1309                 update_from_network(service, network);
1310
1311                 __connman_profile_changed();
1312
1313                 __connman_service_put(service);
1314                 service = NULL;
1315                 goto done;
1316         }
1317
1318         service->type = convert_network_type(network);
1319
1320         service->state = CONNMAN_SERVICE_STATE_IDLE;
1321
1322         update_from_network(service, network);
1323
1324         service_register(service);
1325
1326 done:
1327         g_free(name);
1328
1329         return service;
1330 }
1331
1332 static int service_load(struct connman_service *service)
1333 {
1334         GKeyFile *keyfile;
1335         gchar *pathname, *data = NULL;
1336         gsize length;
1337         gchar *str;
1338
1339         DBG("service %p", service);
1340
1341         if (service->profile == NULL)
1342                 return -EINVAL;
1343
1344         pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, service->profile);
1345         if (pathname == NULL)
1346                 return -ENOMEM;
1347
1348         keyfile = g_key_file_new();
1349
1350         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) {
1351                 g_free(pathname);
1352                 return -ENOENT;
1353         }
1354
1355         g_free(pathname);
1356
1357         if (g_key_file_load_from_data(keyfile, data, length,
1358                                                         0, NULL) == FALSE) {
1359                 g_free(data);
1360                 return -EILSEQ;
1361         }
1362
1363         g_free(data);
1364
1365         switch (service->type) {
1366         case CONNMAN_SERVICE_TYPE_UNKNOWN:
1367         case CONNMAN_SERVICE_TYPE_ETHERNET:
1368                 break;
1369         case CONNMAN_SERVICE_TYPE_WIFI:
1370         case CONNMAN_SERVICE_TYPE_WIMAX:
1371         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
1372         case CONNMAN_SERVICE_TYPE_CELLULAR:
1373                 service->favorite = g_key_file_get_boolean(keyfile,
1374                                 service->identifier, "Favorite", NULL);
1375
1376                 str = g_key_file_get_string(keyfile,
1377                                 service->identifier, "Failure", NULL);
1378                 if (str != NULL) {
1379                         service->state = CONNMAN_SERVICE_STATE_FAILURE;
1380                         service->error = string2error(str);
1381                 }
1382                 break;
1383         }
1384
1385         str = g_key_file_get_string(keyfile,
1386                                 service->identifier, "Modified", NULL);
1387         if (str != NULL) {
1388                 g_time_val_from_iso8601(str, &service->modified);
1389                 g_free(str);
1390         }
1391
1392         str = g_key_file_get_string(keyfile,
1393                                 service->identifier, "Passphrase", NULL);
1394         if (str != NULL) {
1395                 g_free(service->passphrase);
1396                 service->passphrase = str;
1397         }
1398
1399         __connman_ipconfig_load(service->ipconfig, keyfile,
1400                                         service->identifier, "IPv4.");
1401
1402         g_key_file_free(keyfile);
1403
1404         return 0;
1405 }
1406
1407 static int service_save(struct connman_service *service)
1408 {
1409         GKeyFile *keyfile;
1410         gchar *pathname, *data = NULL;
1411         gsize length;
1412         gchar *str;
1413
1414         DBG("service %p", service);
1415
1416         if (service->profile == NULL)
1417                 return -EINVAL;
1418
1419         pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, service->profile);
1420         if (pathname == NULL)
1421                 return -ENOMEM;
1422
1423         keyfile = g_key_file_new();
1424
1425         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE)
1426                 goto update;
1427
1428         if (length > 0) {
1429                 if (g_key_file_load_from_data(keyfile, data, length,
1430                                                         0, NULL) == FALSE)
1431                         goto done;
1432         }
1433
1434         g_free(data);
1435
1436 update:
1437         if (service->name != NULL)
1438                 g_key_file_set_string(keyfile, service->identifier,
1439                                                 "Name", service->name);
1440
1441         switch (service->type) {
1442         case CONNMAN_SERVICE_TYPE_UNKNOWN:
1443         case CONNMAN_SERVICE_TYPE_ETHERNET:
1444                 break;
1445         case CONNMAN_SERVICE_TYPE_WIFI:
1446         case CONNMAN_SERVICE_TYPE_WIMAX:
1447         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
1448         case CONNMAN_SERVICE_TYPE_CELLULAR:
1449                 g_key_file_set_boolean(keyfile, service->identifier,
1450                                         "Favorite", service->favorite);
1451
1452                 if (service->state == CONNMAN_SERVICE_STATE_FAILURE) {
1453                         const char *failure = error2string(service->error);
1454                         if (failure != NULL)
1455                                 g_key_file_set_string(keyfile,
1456                                                         service->identifier,
1457                                                         "Failure", failure);
1458                 } else {
1459                         g_key_file_remove_key(keyfile, service->identifier,
1460                                                         "Failure", NULL);
1461                 }
1462                 break;
1463         }
1464
1465         str = g_time_val_to_iso8601(&service->modified);
1466         if (str != NULL) {
1467                 g_key_file_set_string(keyfile, service->identifier,
1468                                                         "Modified", str);
1469                 g_free(str);
1470         }
1471
1472         if (service->passphrase != NULL)
1473                 g_key_file_set_string(keyfile, service->identifier,
1474                                         "Passphrase", service->passphrase);
1475         else
1476                 g_key_file_remove_key(keyfile, service->identifier,
1477                                                         "Passphrase", NULL);
1478
1479         __connman_ipconfig_save(service->ipconfig, keyfile,
1480                                         service->identifier, "IPv4.");
1481
1482         data = g_key_file_to_data(keyfile, &length, NULL);
1483
1484         if (g_file_set_contents(pathname, data, length, NULL) == FALSE)
1485                 connman_error("Failed to store service information");
1486
1487 done:
1488         g_free(data);
1489
1490         g_key_file_free(keyfile);
1491
1492         g_free(pathname);
1493
1494         return 0;
1495 }
1496
1497 static struct connman_storage service_storage = {
1498         .name           = "service",
1499         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
1500         .service_load   = service_load,
1501         .service_save   = service_save,
1502 };
1503
1504 int __connman_service_init(void)
1505 {
1506         DBG("");
1507
1508         connection = connman_dbus_get_connection();
1509
1510         if (connman_storage_register(&service_storage) < 0)
1511                 connman_error("Failed to register service storage");
1512
1513         service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1514                                                                 NULL, NULL);
1515
1516         service_list = g_sequence_new(service_free);
1517
1518         return 0;
1519 }
1520
1521 void __connman_service_cleanup(void)
1522 {
1523         DBG("");
1524
1525         g_sequence_free(service_list);
1526         service_list = NULL;
1527
1528         g_hash_table_destroy(service_hash);
1529         service_hash = NULL;
1530
1531         connman_storage_unregister(&service_storage);
1532
1533         dbus_connection_unref(connection);
1534 }