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