Fix wrong service state handling on timeouts
[framework/connectivity/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 <stdio.h>
27 #include <string.h>
28 #include <gdbus.h>
29
30 #include "connman.h"
31
32 static DBusConnection *connection = NULL;
33
34 static GSequence *service_list = NULL;
35 static GHashTable *service_hash = NULL;
36
37 struct connman_service {
38         gint refcount;
39         char *identifier;
40         char *path;
41         enum connman_service_type type;
42         enum connman_service_mode mode;
43         enum connman_service_security security;
44         enum connman_service_state state;
45         enum connman_service_error error;
46         connman_uint8_t strength;
47         connman_bool_t favorite;
48         connman_bool_t hidden;
49         connman_bool_t ignore;
50         connman_bool_t autoconnect;
51         GTimeVal modified;
52         unsigned int order;
53         char *name;
54         char *passphrase;
55         char *profile;
56         struct connman_ipconfig *ipconfig;
57         struct connman_device *device;
58         struct connman_network *network;
59         DBusMessage *pending;
60         guint timeout;
61 };
62
63 static void append_path(gpointer value, gpointer user_data)
64 {
65         struct connman_service *service = value;
66         DBusMessageIter *iter = user_data;
67
68         if (service->path == NULL || service->hidden == TRUE)
69                 return;
70
71         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
72                                                         &service->path);
73 }
74
75 void __connman_service_list(DBusMessageIter *iter)
76 {
77         DBG("");
78
79         g_sequence_foreach(service_list, append_path, iter);
80 }
81
82 struct find_data {
83         const char *path;
84         struct connman_service *service;
85 };
86
87 static void compare_path(gpointer value, gpointer user_data)
88 {
89         struct connman_service *service = value;
90         struct find_data *data = user_data;
91
92         if (data->service != NULL)
93                 return;
94
95         if (g_strcmp0(service->path, data->path) == 0)
96                 data->service = service;
97 }
98
99 static struct connman_service *find_service(const char *path)
100 {
101         struct find_data data = { .path = path, .service = NULL };
102
103         DBG("path %s", path);
104
105         g_sequence_foreach(service_list, compare_path, &data);
106
107         return data.service;
108 }
109
110 static const char *type2string(enum connman_service_type type)
111 {
112         switch (type) {
113         case CONNMAN_SERVICE_TYPE_UNKNOWN:
114                 break;
115         case CONNMAN_SERVICE_TYPE_ETHERNET:
116                 return "ethernet";
117         case CONNMAN_SERVICE_TYPE_WIFI:
118                 return "wifi";
119         case CONNMAN_SERVICE_TYPE_WIMAX:
120                 return "wimax";
121         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
122                 return "bluetooth";
123         case CONNMAN_SERVICE_TYPE_CELLULAR:
124                 return "cellular";
125         }
126
127         return NULL;
128 }
129
130 static const char *mode2string(enum connman_service_mode mode)
131 {
132         switch (mode) {
133         case CONNMAN_SERVICE_MODE_UNKNOWN:
134                 break;
135         case CONNMAN_SERVICE_MODE_MANAGED:
136                 return "managed";
137         case CONNMAN_SERVICE_MODE_ADHOC:
138                 return "adhoc";
139         }
140
141         return NULL;
142 }
143
144 static const char *security2string(enum connman_service_security security)
145 {
146         switch (security) {
147         case CONNMAN_SERVICE_SECURITY_UNKNOWN:
148                 break;
149         case CONNMAN_SERVICE_SECURITY_NONE:
150                 return "none";
151         case CONNMAN_SERVICE_SECURITY_WEP:
152                 return "wep";
153         case CONNMAN_SERVICE_SECURITY_WPA:
154                 return "wpa";
155         case CONNMAN_SERVICE_SECURITY_RSN:
156                 return "rsn";
157         }
158
159         return NULL;
160 }
161
162 static const char *state2string(enum connman_service_state state)
163 {
164         switch (state) {
165         case CONNMAN_SERVICE_STATE_UNKNOWN:
166                 break;
167         case CONNMAN_SERVICE_STATE_IDLE:
168                 return "idle";
169         case CONNMAN_SERVICE_STATE_CARRIER:
170                 return "carrier";
171         case CONNMAN_SERVICE_STATE_ASSOCIATION:
172                 return "association";
173         case CONNMAN_SERVICE_STATE_CONFIGURATION:
174                 return "configuration";
175         case CONNMAN_SERVICE_STATE_READY:
176                 return "ready";
177         case CONNMAN_SERVICE_STATE_DISCONNECT:
178                 return "disconnect";
179         case CONNMAN_SERVICE_STATE_FAILURE:
180                 return "failure";
181         }
182
183         return NULL;
184 }
185
186 static const char *error2string(enum connman_service_error error)
187 {
188         switch (error) {
189         case CONNMAN_SERVICE_ERROR_UNKNOWN:
190                 break;
191         case CONNMAN_SERVICE_ERROR_OUT_OF_RANGE:
192                 return "out-of-range";
193         case CONNMAN_SERVICE_ERROR_PIN_MISSING:
194                 return "pin-missing";
195         case CONNMAN_SERVICE_ERROR_DHCP_FAILED:
196                 return "dhcp-failed";
197         case CONNMAN_SERVICE_ERROR_CONNECT_FAILED:
198                 return "connect-failed";
199         }
200
201         return NULL;
202 }
203
204 static enum connman_service_error string2error(const char *error)
205 {
206         if (g_strcmp0(error, "dhcp-failed") == 0)
207                 return CONNMAN_SERVICE_ERROR_DHCP_FAILED;
208         else if (g_strcmp0(error, "pin-missing") == 0)
209                 return CONNMAN_SERVICE_ERROR_PIN_MISSING;
210
211         return CONNMAN_SERVICE_ERROR_UNKNOWN;
212 }
213
214 const char *__connman_service_default(void)
215 {
216         struct connman_service *service;
217         GSequenceIter *iter;
218
219         iter = g_sequence_get_begin_iter(service_list);
220
221         if (g_sequence_iter_is_end(iter) == TRUE)
222                 return "";
223
224         service = g_sequence_get(iter);
225         if (service == NULL)
226                 return "";
227
228         if (service->state != CONNMAN_SERVICE_STATE_READY)
229                 return "";
230
231         return type2string(service->type);
232 }
233
234 static void state_changed(struct connman_service *service)
235 {
236         DBusMessage *signal;
237         DBusMessageIter entry, value;
238         const char *str, *key = "State";
239
240         if (service->path == NULL)
241                 return;
242
243         str = state2string(service->state);
244         if (str == NULL)
245                 return;
246
247         signal = dbus_message_new_signal(service->path,
248                                 CONNMAN_SERVICE_INTERFACE, "PropertyChanged");
249         if (signal == NULL)
250                 return;
251
252         dbus_message_iter_init_append(signal, &entry);
253
254         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
255
256         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
257                                         DBUS_TYPE_STRING_AS_STRING, &value);
258         dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
259         dbus_message_iter_close_container(&entry, &value);
260
261         g_dbus_send_message(connection, signal);
262 }
263
264 static void strength_changed(struct connman_service *service)
265 {
266         DBusMessage *signal;
267         DBusMessageIter entry, value;
268         const char *key = "Strength";
269
270         if (service->path == NULL)
271                 return;
272
273         if (service->strength == 0)
274                 return;
275
276         signal = dbus_message_new_signal(service->path,
277                                 CONNMAN_SERVICE_INTERFACE, "PropertyChanged");
278         if (signal == NULL)
279                 return;
280
281         dbus_message_iter_init_append(signal, &entry);
282
283         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
284
285         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
286                                         DBUS_TYPE_BYTE_AS_STRING, &value);
287         dbus_message_iter_append_basic(&value, DBUS_TYPE_BYTE,
288                                                         &service->strength);
289         dbus_message_iter_close_container(&entry, &value);
290
291         g_dbus_send_message(connection, signal);
292 }
293
294 static void passphrase_changed(struct connman_service *service)
295 {
296         DBusMessage *signal;
297         DBusMessageIter entry, value;
298         dbus_bool_t required;
299         const char *key = "PassphraseRequired";
300
301         if (service->path == NULL)
302                 return;
303
304         switch (service->type) {
305         case CONNMAN_SERVICE_TYPE_UNKNOWN:
306         case CONNMAN_SERVICE_TYPE_ETHERNET:
307         case CONNMAN_SERVICE_TYPE_WIMAX:
308         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
309         case CONNMAN_SERVICE_TYPE_CELLULAR:
310                 return;
311         case CONNMAN_SERVICE_TYPE_WIFI:
312                 required = FALSE;
313
314                 switch (service->security) {
315                 case CONNMAN_SERVICE_SECURITY_UNKNOWN:
316                 case CONNMAN_SERVICE_SECURITY_NONE:
317                         break;
318                 case CONNMAN_SERVICE_SECURITY_WEP:
319                 case CONNMAN_SERVICE_SECURITY_WPA:
320                 case CONNMAN_SERVICE_SECURITY_RSN:
321                         if (service->passphrase == NULL)
322                                 required = TRUE;
323                         break;
324                 }
325                 break;
326         }
327
328         signal = dbus_message_new_signal(service->path,
329                                 CONNMAN_SERVICE_INTERFACE, "PropertyChanged");
330         if (signal == NULL)
331                 return;
332
333         dbus_message_iter_init_append(signal, &entry);
334
335         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
336
337         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
338                                         DBUS_TYPE_BOOLEAN_AS_STRING, &value);
339         dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &required);
340         dbus_message_iter_close_container(&entry, &value);
341
342         g_dbus_send_message(connection, signal);
343 }
344
345 static void autoconnect_changed(struct connman_service *service)
346 {
347         DBusMessage *signal;
348         DBusMessageIter entry, value;
349         const char *key = "AutoConnect";
350
351         if (service->path == NULL)
352                 return;
353
354         signal = dbus_message_new_signal(service->path,
355                                 CONNMAN_SERVICE_INTERFACE, "PropertyChanged");
356         if (signal == NULL)
357                 return;
358
359         dbus_message_iter_init_append(signal, &entry);
360
361         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
362
363         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
364                                         DBUS_TYPE_BOOLEAN_AS_STRING, &value);
365         dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN,
366                                                         &service->autoconnect);
367         dbus_message_iter_close_container(&entry, &value);
368
369         g_dbus_send_message(connection, signal);
370 }
371
372 static DBusMessage *get_properties(DBusConnection *conn,
373                                         DBusMessage *msg, void *user_data)
374 {
375         struct connman_service *service = user_data;
376         DBusMessage *reply;
377         DBusMessageIter array, dict;
378         dbus_bool_t required;
379         const char *str;
380
381         DBG("service %p", service);
382
383         reply = dbus_message_new_method_return(msg);
384         if (reply == NULL)
385                 return NULL;
386
387         dbus_message_iter_init_append(reply, &array);
388
389         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
390                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
391                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
392                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
393
394         str = type2string(service->type);
395         if (str != NULL)
396                 connman_dbus_dict_append_variant(&dict, "Type",
397                                                 DBUS_TYPE_STRING, &str);
398
399         str = mode2string(service->mode);
400         if (str != NULL)
401                 connman_dbus_dict_append_variant(&dict, "Mode",
402                                                 DBUS_TYPE_STRING, &str);
403
404         str = security2string(service->security);
405         if (str != NULL)
406                 connman_dbus_dict_append_variant(&dict, "Security",
407                                                 DBUS_TYPE_STRING, &str);
408
409         str = state2string(service->state);
410         if (str != NULL)
411                 connman_dbus_dict_append_variant(&dict, "State",
412                                                 DBUS_TYPE_STRING, &str);
413
414         str = error2string(service->error);
415         if (str != NULL)
416                 connman_dbus_dict_append_variant(&dict, "Error",
417                                                 DBUS_TYPE_STRING, &str);
418
419         if (service->strength > 0)
420                 connman_dbus_dict_append_variant(&dict, "Strength",
421                                         DBUS_TYPE_BYTE, &service->strength);
422
423         connman_dbus_dict_append_variant(&dict, "Favorite",
424                                         DBUS_TYPE_BOOLEAN, &service->favorite);
425
426         if (service->favorite == TRUE)
427                 connman_dbus_dict_append_variant(&dict, "AutoConnect",
428                                 DBUS_TYPE_BOOLEAN, &service->autoconnect);
429         else
430                 connman_dbus_dict_append_variant(&dict, "AutoConnect",
431                                         DBUS_TYPE_BOOLEAN, &service->favorite);
432
433         if (service->name != NULL)
434                 connman_dbus_dict_append_variant(&dict, "Name",
435                                         DBUS_TYPE_STRING, &service->name);
436
437         switch (service->type) {
438         case CONNMAN_SERVICE_TYPE_UNKNOWN:
439         case CONNMAN_SERVICE_TYPE_ETHERNET:
440         case CONNMAN_SERVICE_TYPE_WIMAX:
441         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
442         case CONNMAN_SERVICE_TYPE_CELLULAR:
443                 break;
444         case CONNMAN_SERVICE_TYPE_WIFI:
445                 if (service->passphrase != NULL &&
446                                 __connman_security_check_privilege(msg,
447                                         CONNMAN_SECURITY_PRIVILEGE_SECRET) == 0)
448                         connman_dbus_dict_append_variant(&dict, "Passphrase",
449                                 DBUS_TYPE_STRING, &service->passphrase);
450
451                 required = FALSE;
452
453                 switch (service->security) {
454                 case CONNMAN_SERVICE_SECURITY_UNKNOWN:
455                 case CONNMAN_SERVICE_SECURITY_NONE:
456                         break;
457                 case CONNMAN_SERVICE_SECURITY_WEP:
458                 case CONNMAN_SERVICE_SECURITY_WPA:
459                 case CONNMAN_SERVICE_SECURITY_RSN:
460                         if (service->passphrase == NULL)
461                                 required = TRUE;
462                         break;
463                 }
464
465                 connman_dbus_dict_append_variant(&dict, "PassphraseRequired",
466                                                 DBUS_TYPE_BOOLEAN, &required);
467                 break;
468         }
469
470         if (service->ipconfig != NULL)
471                 __connman_ipconfig_append_ipv4(service->ipconfig,
472                                                         &dict, "IPv4.");
473
474         dbus_message_iter_close_container(&array, &dict);
475
476         return reply;
477 }
478
479 static DBusMessage *set_property(DBusConnection *conn,
480                                         DBusMessage *msg, void *user_data)
481 {
482         struct connman_service *service = user_data;
483         DBusMessageIter iter, value;
484         const char *name;
485         int type;
486
487         DBG("service %p", service);
488
489         if (dbus_message_iter_init(msg, &iter) == FALSE)
490                 return __connman_error_invalid_arguments(msg);
491
492         dbus_message_iter_get_basic(&iter, &name);
493         dbus_message_iter_next(&iter);
494         dbus_message_iter_recurse(&iter, &value);
495
496         if (__connman_security_check_privilege(msg,
497                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
498                 return __connman_error_permission_denied(msg);
499
500         type = dbus_message_iter_get_arg_type(&value);
501
502         if (g_str_equal(name, "Passphrase") == TRUE) {
503                 const char *passphrase;
504
505                 if (type != DBUS_TYPE_STRING)
506                         return __connman_error_invalid_arguments(msg);
507
508                 if (__connman_security_check_privilege(msg,
509                                         CONNMAN_SECURITY_PRIVILEGE_SECRET) < 0)
510                         return __connman_error_permission_denied(msg);
511
512                 dbus_message_iter_get_basic(&value, &passphrase);
513
514                 g_free(service->passphrase);
515                 service->passphrase = g_strdup(passphrase);
516
517                 passphrase_changed(service);
518
519                 if (service->network != NULL)
520                         connman_network_set_string(service->network,
521                                 "WiFi.Passphrase", service->passphrase);
522
523                 __connman_storage_save_service(service);
524         } else if (g_str_has_prefix(name, "AutoConnect") == TRUE) {
525                 connman_bool_t autoconnect;
526
527                 if (type != DBUS_TYPE_BOOLEAN)
528                         return __connman_error_invalid_arguments(msg);
529
530                 if (service->favorite == FALSE)
531                         return __connman_error_invalid_service(msg);
532
533                 dbus_message_iter_get_basic(&value, &autoconnect);
534
535                 if (service->autoconnect == autoconnect)
536                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
537
538                 service->autoconnect = autoconnect;
539
540                 autoconnect_changed(service);
541
542                 __connman_storage_save_service(service);
543         } else if (g_str_has_prefix(name, "IPv4.") == TRUE) {
544                 int err;
545
546                 if (service->ipconfig == NULL)
547                         return __connman_error_invalid_property(msg);
548
549                 err = __connman_ipconfig_set_ipv4(service->ipconfig,
550                                                         name + 5, &value);
551                 if (err < 0)
552                         return __connman_error_failed(msg, -err);
553         } else
554                 return __connman_error_invalid_property(msg);
555
556         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
557 }
558
559 static void set_idle(struct connman_service *service)
560 {
561         service->state = CONNMAN_SERVICE_STATE_IDLE;
562         service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;
563         state_changed(service);
564 }
565
566 static DBusMessage *clear_property(DBusConnection *conn,
567                                         DBusMessage *msg, void *user_data)
568 {
569         struct connman_service *service = user_data;
570         const char *name;
571
572         DBG("service %p", service);
573
574         dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name,
575                                                         DBUS_TYPE_INVALID);
576
577         if (__connman_security_check_privilege(msg,
578                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
579                 return __connman_error_permission_denied(msg);
580
581         if (g_str_equal(name, "Error") == TRUE) {
582                 set_idle(service);
583
584                 g_get_current_time(&service->modified);
585                 __connman_storage_save_service(service);
586         } else if (g_str_equal(name, "Passphrase") == TRUE) {
587                 g_free(service->passphrase);
588                 service->passphrase = NULL;
589
590                 passphrase_changed(service);
591
592                 __connman_storage_save_service(service);
593         } else
594                 return __connman_error_invalid_property(msg);
595
596         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
597 }
598
599 static connman_bool_t is_connecting(struct connman_service *service)
600 {
601         switch (service->state) {
602         case CONNMAN_SERVICE_STATE_UNKNOWN:
603         case CONNMAN_SERVICE_STATE_IDLE:
604         case CONNMAN_SERVICE_STATE_CARRIER:
605         case CONNMAN_SERVICE_STATE_FAILURE:
606         case CONNMAN_SERVICE_STATE_DISCONNECT:
607         case CONNMAN_SERVICE_STATE_READY:
608                 break;
609         case CONNMAN_SERVICE_STATE_ASSOCIATION:
610         case CONNMAN_SERVICE_STATE_CONFIGURATION:
611                 return TRUE;
612         }
613
614         return FALSE;
615 }
616
617 static connman_bool_t is_ignore(struct connman_service *service)
618 {
619         if (service->autoconnect == FALSE)
620                 return TRUE;
621
622         if (service->ignore == TRUE)
623                 return TRUE;
624
625         if (service->state == CONNMAN_SERVICE_STATE_FAILURE)
626                 return TRUE;
627
628         return FALSE;
629 }
630
631 void __connman_service_auto_connect(void)
632 {
633         struct connman_service *service = NULL;
634         GSequenceIter *iter;
635
636         DBG("");
637
638         iter = g_sequence_get_begin_iter(service_list);
639
640         while (g_sequence_iter_is_end(iter) == FALSE) {
641                 service = g_sequence_get(iter);
642
643                 if (service->pending != NULL)
644                         return;
645
646                 if (is_connecting(service) == TRUE)
647                         return;
648
649                 if (service->favorite == FALSE)
650                         return;
651
652                 if (service->state == CONNMAN_SERVICE_STATE_READY)
653                         return;
654
655                 if (is_ignore(service) == FALSE &&
656                                 service->state == CONNMAN_SERVICE_STATE_IDLE)
657                         break;
658
659                 service = NULL;
660
661                 iter = g_sequence_iter_next(iter);
662         }
663
664         if (service != NULL)
665                 __connman_service_connect(service);
666 }
667
668 static void reply_pending(struct connman_service *service, int error)
669 {
670         if (service->timeout > 0) {
671                 g_source_remove(service->timeout);
672                 service->timeout = 0;
673         }
674
675         if (service->pending != NULL) {
676                 if (error > 0) {
677                         DBusMessage *reply;
678
679                         reply = __connman_error_failed(service->pending,
680                                                                 error);
681                         if (reply != NULL)
682                                 g_dbus_send_message(connection, reply);
683                 } else
684                         g_dbus_send_reply(connection, service->pending,
685                                                         DBUS_TYPE_INVALID);
686
687                 dbus_message_unref(service->pending);
688                 service->pending = NULL;
689         }
690 }
691
692 static gboolean connect_timeout(gpointer user_data)
693 {
694         struct connman_service *service = user_data;
695         connman_bool_t auto_connect = FALSE;
696
697         DBG("service %p", service);
698
699         service->timeout = 0;
700
701         if (service->network != NULL)
702                 __connman_network_disconnect(service->network);
703         else if (service->device != NULL)
704                 __connman_device_disconnect(service->device);
705
706         if (service->pending != NULL) {
707                 DBusMessage *reply;
708
709                 reply = __connman_error_operation_timeout(service->pending);
710                 if (reply != NULL)
711                         g_dbus_send_message(connection, reply);
712
713                 dbus_message_unref(service->pending);
714                 service->pending = NULL;
715         } else
716                 auto_connect = TRUE;
717
718         __connman_service_indicate_state(service,
719                                         CONNMAN_SERVICE_STATE_FAILURE);
720
721         if (auto_connect == TRUE)
722                 __connman_service_auto_connect();
723
724         return FALSE;
725 }
726
727 static DBusMessage *connect_service(DBusConnection *conn,
728                                         DBusMessage *msg, void *user_data)
729 {
730         struct connman_service *service = user_data;
731         GSequenceIter *iter;
732         int err;
733
734         DBG("service %p", service);
735
736         if (service->pending != NULL)
737                 return __connman_error_in_progress(msg);
738
739         iter = g_sequence_get_begin_iter(service_list);
740
741         while (g_sequence_iter_is_end(iter) == FALSE) {
742                 struct connman_service *temp = g_sequence_get(iter);
743
744                 if (service->type == temp->type &&
745                                         is_connecting(temp) == TRUE)
746                         return __connman_error_in_progress(msg);
747
748                 iter = g_sequence_iter_next(iter);
749         }
750
751         service->ignore = FALSE;
752
753         service->pending = dbus_message_ref(msg);
754
755         err = __connman_service_connect(service);
756         if (err < 0) {
757                 if (err != -EINPROGRESS) {
758                         dbus_message_unref(service->pending);
759                         service->pending = NULL;
760
761                         return __connman_error_failed(msg, -err);
762                 }
763
764                 return NULL;
765         }
766
767         dbus_message_unref(service->pending);
768         service->pending = NULL;
769
770         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
771 }
772
773 static DBusMessage *disconnect_service(DBusConnection *conn,
774                                         DBusMessage *msg, void *user_data)
775 {
776         struct connman_service *service = user_data;
777         int err;
778
779         DBG("service %p", service);
780
781         service->ignore = TRUE;
782
783         err = __connman_service_disconnect(service);
784         if (err < 0) {
785                 if (err != -EINPROGRESS)
786                         return __connman_error_failed(msg, -err);
787
788                 return NULL;
789         }
790
791         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
792 }
793
794 static DBusMessage *remove_service(DBusConnection *conn,
795                                         DBusMessage *msg, void *user_data)
796 {
797         struct connman_service *service = user_data;
798
799         DBG("service %p", service);
800
801         if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET)
802                 return __connman_error_not_supported(msg);
803
804         if (service->favorite == FALSE)
805                 return __connman_error_not_supported(msg);
806
807         if (service->network != NULL)
808                 __connman_network_disconnect(service->network);
809
810         g_free(service->passphrase);
811         service->passphrase = NULL;
812
813         passphrase_changed(service);
814
815         connman_service_set_favorite(service, FALSE);
816         __connman_storage_save_service(service);
817
818         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
819 }
820
821 static DBusMessage *move_before(DBusConnection *conn,
822                                         DBusMessage *msg, void *user_data)
823 {
824         struct connman_service *service = user_data;
825         struct connman_service *target;
826         const char *path;
827         GSequenceIter *src, *dst;
828
829         DBG("service %p", service);
830
831         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
832                                                         DBUS_TYPE_INVALID);
833
834         if (service->favorite == FALSE)
835                 return __connman_error_not_supported(msg);
836
837         target = find_service(path);
838         if (target == NULL || target->favorite == FALSE || target == service)
839                 return __connman_error_invalid_service(msg);
840
841         DBG("target %s", target->identifier);
842
843         if (target->state != service->state)
844                 return __connman_error_invalid_service(msg);
845
846         g_get_current_time(&service->modified);
847         __connman_storage_save_service(service);
848
849         src = g_hash_table_lookup(service_hash, service->identifier);
850         dst = g_hash_table_lookup(service_hash, target->identifier);
851
852         g_sequence_move(src, dst);
853
854         __connman_profile_changed(FALSE);
855
856         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
857 }
858
859 static DBusMessage *move_after(DBusConnection *conn,
860                                         DBusMessage *msg, void *user_data)
861 {
862         struct connman_service *service = user_data;
863         struct connman_service *target;
864         const char *path;
865
866         DBG("service %p", service);
867
868         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
869                                                         DBUS_TYPE_INVALID);
870
871         if (service->favorite == FALSE)
872                 return __connman_error_not_supported(msg);
873
874         target = find_service(path);
875         if (target == NULL || target->favorite == FALSE || target == service)
876                 return __connman_error_invalid_service(msg);
877
878         DBG("target %s", target->identifier);
879
880         if (target->state != service->state)
881                 return __connman_error_invalid_service(msg);
882
883         g_get_current_time(&service->modified);
884         __connman_storage_save_service(service);
885
886         return __connman_error_not_implemented(msg);
887 }
888
889 static GDBusMethodTable service_methods[] = {
890         { "GetProperties", "",   "a{sv}", get_properties     },
891         { "SetProperty",   "sv", "",      set_property       },
892         { "ClearProperty", "s",  "",      clear_property     },
893         { "Connect",       "",   "",      connect_service,
894                                                 G_DBUS_METHOD_FLAG_ASYNC },
895         { "Disconnect",    "",   "",      disconnect_service },
896         { "Remove",        "",   "",      remove_service     },
897         { "MoveBefore",    "o",  "",      move_before        },
898         { "MoveAfter",     "o",  "",      move_after         },
899         { },
900 };
901
902 static GDBusSignalTable service_signals[] = {
903         { "PropertyChanged", "sv" },
904         { },
905 };
906
907 static void service_free(gpointer user_data)
908 {
909         struct connman_service *service = user_data;
910         char *path = service->path;
911
912         DBG("service %p", service);
913
914         reply_pending(service, ENOENT);
915
916         g_hash_table_remove(service_hash, service->identifier);
917
918         service->path = NULL;
919
920         if (path != NULL) {
921                 __connman_profile_changed(FALSE);
922
923                 g_dbus_unregister_interface(connection, path,
924                                                 CONNMAN_SERVICE_INTERFACE);
925                 g_free(path);
926         }
927
928         if (service->network != NULL)
929                 connman_network_unref(service->network);
930
931         if (service->ipconfig != NULL) {
932                 connman_ipconfig_unref(service->ipconfig);
933                 service->ipconfig = NULL;
934         }
935
936         g_free(service->profile);
937         g_free(service->name);
938         g_free(service->passphrase);
939         g_free(service->identifier);
940         g_free(service);
941 }
942
943 /**
944  * __connman_service_put:
945  * @service: service structure
946  *
947  * Release service if no longer needed
948  */
949 void __connman_service_put(struct connman_service *service)
950 {
951         DBG("service %p", service);
952
953         if (g_atomic_int_dec_and_test(&service->refcount) == TRUE) {
954                 GSequenceIter *iter;
955
956                 iter = g_hash_table_lookup(service_hash, service->identifier);
957                 if (iter != NULL) {
958                         __connman_service_disconnect(service);
959                         g_sequence_remove(iter);
960                 } else
961                         service_free(service);
962         }
963 }
964
965 static void __connman_service_initialize(struct connman_service *service)
966 {
967         DBG("service %p", service);
968
969         service->refcount = 1;
970
971         service->type     = CONNMAN_SERVICE_TYPE_UNKNOWN;
972         service->mode     = CONNMAN_SERVICE_MODE_UNKNOWN;
973         service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN;
974         service->state    = CONNMAN_SERVICE_STATE_UNKNOWN;
975
976         service->favorite = FALSE;
977         service->hidden = FALSE;
978
979         service->ignore = FALSE;
980
981         service->order = 0;
982 }
983
984 /**
985  * connman_service_create:
986  *
987  * Allocate a new service.
988  *
989  * Returns: a newly-allocated #connman_service structure
990  */
991 struct connman_service *connman_service_create(void)
992 {
993         struct connman_service *service;
994
995         service = g_try_new0(struct connman_service, 1);
996         if (service == NULL)
997                 return NULL;
998
999         DBG("service %p", service);
1000
1001         __connman_service_initialize(service);
1002
1003         return service;
1004 }
1005
1006 /**
1007  * connman_service_ref:
1008  * @service: service structure
1009  *
1010  * Increase reference counter of service
1011  */
1012 struct connman_service *connman_service_ref(struct connman_service *service)
1013 {
1014         g_atomic_int_inc(&service->refcount);
1015
1016         return service;
1017 }
1018
1019 /**
1020  * connman_service_unref:
1021  * @service: service structure
1022  *
1023  * Decrease reference counter of service
1024  */
1025 void connman_service_unref(struct connman_service *service)
1026 {
1027         __connman_service_put(service);
1028 }
1029
1030 static gint service_compare(gconstpointer a, gconstpointer b,
1031                                                         gpointer user_data)
1032 {
1033         struct connman_service *service_a = (void *) a;
1034         struct connman_service *service_b = (void *) b;
1035
1036         if (service_a->state != service_b->state) {
1037                 if (service_a->state == CONNMAN_SERVICE_STATE_READY)
1038                         return -1;
1039                 if (service_b->state == CONNMAN_SERVICE_STATE_READY)
1040                         return 1;
1041         }
1042
1043         if (service_a->order > service_b->order)
1044                 return -1;
1045
1046         if (service_a->order < service_b->order)
1047                 return 1;
1048
1049         if (service_a->favorite == TRUE && service_b->favorite == FALSE)
1050                 return -1;
1051
1052         if (service_a->favorite == FALSE && service_b->favorite == TRUE)
1053                 return 1;
1054
1055         if (service_a->type != service_b->type) {
1056                 switch (service_a->type) {
1057                 case CONNMAN_SERVICE_TYPE_UNKNOWN:
1058                 case CONNMAN_SERVICE_TYPE_ETHERNET:
1059                         break;
1060                 case CONNMAN_SERVICE_TYPE_WIFI:
1061                         return 1;
1062                 case CONNMAN_SERVICE_TYPE_WIMAX:
1063                 case CONNMAN_SERVICE_TYPE_BLUETOOTH:
1064                 case CONNMAN_SERVICE_TYPE_CELLULAR:
1065                         return -1;
1066                 }
1067         }
1068
1069         return (gint) service_b->strength - (gint) service_a->strength;
1070 }
1071
1072 /**
1073  * connman_service_set_favorite:
1074  * @service: service structure
1075  * @favorite: favorite value
1076  *
1077  * Change the favorite setting of service
1078  */
1079 int connman_service_set_favorite(struct connman_service *service,
1080                                                 connman_bool_t favorite)
1081 {
1082         GSequenceIter *iter;
1083
1084         iter = g_hash_table_lookup(service_hash, service->identifier);
1085         if (iter == NULL)
1086                 return -ENOENT;
1087
1088         if (service->favorite == favorite)
1089                 return -EALREADY;
1090
1091         service->favorite = favorite;
1092
1093         g_sequence_sort_changed(iter, service_compare, NULL);
1094
1095         __connman_profile_changed(FALSE);
1096
1097         return 0;
1098 }
1099
1100 static void default_changed(void)
1101 {
1102         DBusMessage *signal;
1103         DBusMessageIter entry, value;
1104         const char *key = "DefaultTechnology";
1105         const char *str = __connman_service_default();
1106
1107         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
1108                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
1109         if (signal == NULL)
1110                 return;
1111
1112         dbus_message_iter_init_append(signal, &entry);
1113
1114         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
1115
1116         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
1117                                         DBUS_TYPE_STRING_AS_STRING, &value);
1118         dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
1119         dbus_message_iter_close_container(&entry, &value);
1120
1121         g_dbus_send_message(connection, signal);
1122 }
1123
1124 int __connman_service_indicate_state(struct connman_service *service,
1125                                         enum connman_service_state state)
1126 {
1127         GSequenceIter *iter;
1128
1129         DBG("service %p state %d", service, state);
1130
1131         if (service == NULL)
1132                 return -EINVAL;
1133
1134         if (service->state == state)
1135                 return -EALREADY;
1136
1137         if (service->state == CONNMAN_SERVICE_STATE_FAILURE &&
1138                                 state == CONNMAN_SERVICE_STATE_IDLE)
1139                 return -EINVAL;
1140
1141         if (service->state == CONNMAN_SERVICE_STATE_IDLE &&
1142                                 state == CONNMAN_SERVICE_STATE_DISCONNECT)
1143                 return -EINVAL;
1144
1145         if (state == CONNMAN_SERVICE_STATE_IDLE &&
1146                         service->state != CONNMAN_SERVICE_STATE_DISCONNECT) {
1147                 service->state = CONNMAN_SERVICE_STATE_DISCONNECT;
1148                 state_changed(service);
1149
1150                 __connman_service_disconnect(service);
1151         }
1152
1153         service->state = state;
1154         state_changed(service);
1155
1156         if (state == CONNMAN_SERVICE_STATE_READY) {
1157                 connman_service_set_favorite(service, TRUE);
1158
1159                 reply_pending(service, 0);
1160
1161                 g_get_current_time(&service->modified);
1162                 __connman_storage_save_service(service);
1163
1164                 __connman_notifier_connect(service->type);
1165
1166                 default_changed();
1167         } else if (state == CONNMAN_SERVICE_STATE_DISCONNECT) {
1168                 default_changed();
1169
1170                 __connman_notifier_disconnect(service->type);
1171         }
1172
1173         if (state == CONNMAN_SERVICE_STATE_FAILURE) {
1174                 reply_pending(service, EIO);
1175
1176                 g_get_current_time(&service->modified);
1177                 __connman_storage_save_service(service);
1178         } else
1179                 service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;
1180
1181         iter = g_hash_table_lookup(service_hash, service->identifier);
1182         if (iter != NULL)
1183                 g_sequence_sort_changed(iter, service_compare, NULL);
1184
1185         __connman_profile_changed(FALSE);
1186
1187         if (service->state == CONNMAN_SERVICE_STATE_IDLE ||
1188                         service->state == CONNMAN_SERVICE_STATE_FAILURE)
1189                 __connman_element_request_scan(CONNMAN_ELEMENT_TYPE_UNKNOWN);
1190
1191         return 0;
1192 }
1193
1194 int __connman_service_indicate_error(struct connman_service *service,
1195                                         enum connman_service_error error)
1196 {
1197         DBG("service %p error %d", service, error);
1198
1199         if (service == NULL)
1200                 return -EINVAL;
1201
1202         service->error = error;
1203
1204         return __connman_service_indicate_state(service,
1205                                         CONNMAN_SERVICE_STATE_FAILURE);
1206 }
1207
1208 int __connman_service_indicate_default(struct connman_service *service)
1209 {
1210         DBG("service %p", service);
1211
1212         default_changed();
1213
1214         return 0;
1215 }
1216
1217 static connman_bool_t prepare_network(struct connman_service *service)
1218 {
1219         enum connman_network_type type;
1220         unsigned int ssid_len;
1221
1222         type = connman_network_get_type(service->network);
1223
1224         switch (type) {
1225         case CONNMAN_NETWORK_TYPE_UNKNOWN:
1226         case CONNMAN_NETWORK_TYPE_VENDOR:
1227                 return FALSE;
1228         case CONNMAN_NETWORK_TYPE_WIFI:
1229                 if (connman_network_get_blob(service->network, "WiFi.SSID",
1230                                                         &ssid_len) == NULL)
1231                         return FALSE;
1232
1233                 connman_network_set_string(service->network,
1234                                 "WiFi.Passphrase", service->passphrase);
1235                 break;
1236         case CONNMAN_NETWORK_TYPE_WIMAX:
1237         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
1238         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
1239         case CONNMAN_NETWORK_TYPE_MBM:
1240         case CONNMAN_NETWORK_TYPE_HSO:
1241                 break;
1242         }
1243
1244         return TRUE;
1245 }
1246
1247 int __connman_service_connect(struct connman_service *service)
1248 {
1249         int err;
1250
1251         DBG("service %p", service);
1252
1253         if (service->state == CONNMAN_SERVICE_STATE_READY)
1254                 return -EISCONN;
1255
1256         if (is_connecting(service) == TRUE)
1257                 return -EALREADY;
1258
1259         switch (service->type) {
1260         case CONNMAN_SERVICE_TYPE_UNKNOWN:
1261                 return -EINVAL;
1262         case CONNMAN_SERVICE_TYPE_ETHERNET:
1263         case CONNMAN_SERVICE_TYPE_WIMAX:
1264         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
1265         case CONNMAN_SERVICE_TYPE_CELLULAR:
1266                 break;
1267         case CONNMAN_SERVICE_TYPE_WIFI:
1268                 switch (service->security) {
1269                 case CONNMAN_SERVICE_SECURITY_UNKNOWN:
1270                 case CONNMAN_SERVICE_SECURITY_NONE:
1271                         break;
1272                 case CONNMAN_SERVICE_SECURITY_WEP:
1273                 case CONNMAN_SERVICE_SECURITY_WPA:
1274                 case CONNMAN_SERVICE_SECURITY_RSN:
1275                         if (service->passphrase == NULL)
1276                                 return -ENOKEY;
1277                         break;
1278                 }
1279                 break;
1280         }
1281
1282         if (service->network != NULL) {
1283                 if (prepare_network(service) == FALSE)
1284                         return -EINVAL;
1285
1286                 err = __connman_network_connect(service->network);
1287         } else if (service->device != NULL) {
1288                 if (service->favorite == FALSE)
1289                         return -ENOLINK;
1290
1291                 err = __connman_device_connect(service->device);
1292         } else
1293                 return -EOPNOTSUPP;
1294
1295         if (err < 0) {
1296                 if (err != -EINPROGRESS)
1297                         return err;
1298
1299                 service->timeout = g_timeout_add_seconds(45,
1300                                                 connect_timeout, service);
1301
1302                 return -EINPROGRESS;
1303         }
1304
1305         return 0;
1306 }
1307
1308 int __connman_service_disconnect(struct connman_service *service)
1309 {
1310         int err;
1311
1312         DBG("service %p", service);
1313
1314         reply_pending(service, ECONNABORTED);
1315
1316         if (service->network != NULL) {
1317                 err = __connman_network_disconnect(service->network);
1318         } else if (service->device != NULL) {
1319                 if (service->favorite == FALSE)
1320                         return -ENOLINK;
1321                 err = __connman_device_disconnect(service->device);
1322         } else
1323                 return -EOPNOTSUPP;
1324
1325         if (err < 0) {
1326                 if (err != -EINPROGRESS)
1327                         return err;
1328
1329                 return -EINPROGRESS;
1330         }
1331
1332         return 0;
1333 }
1334
1335 /**
1336  * __connman_service_lookup:
1337  * @identifier: service identifier
1338  *
1339  * Look up a service by identifier (reference count will not be increased)
1340  */
1341 static struct connman_service *__connman_service_lookup(const char *identifier)
1342 {
1343         GSequenceIter *iter;
1344
1345         iter = g_hash_table_lookup(service_hash, identifier);
1346         if (iter != NULL)
1347                 return g_sequence_get(iter);
1348
1349         return NULL;
1350 }
1351
1352 static struct connman_network *create_hidden_wifi(struct connman_device *device,
1353                 const char *ssid, const char *mode, const char *security)
1354 {
1355         struct connman_network *network;
1356         char *name;
1357         int index;
1358         unsigned int i, ssid_len;
1359
1360         ssid_len = strlen(ssid);
1361         if (ssid_len < 1)
1362                 return NULL;
1363
1364         network = connman_network_create(NULL, CONNMAN_NETWORK_TYPE_WIFI);
1365         if (network == NULL)
1366                 return NULL;
1367
1368         connman_network_set_blob(network, "WiFi.SSID",
1369                                         (unsigned char *) ssid, ssid_len);
1370
1371         connman_network_set_string(network, "WiFi.Mode", mode);
1372         connman_network_set_string(network, "WiFi.Security", security);
1373
1374         name = g_try_malloc0(ssid_len + 1);
1375         if (name == NULL) {
1376                 connman_network_unref(network);
1377                 return NULL;
1378         }
1379
1380         for (i = 0; i < ssid_len; i++) {
1381                 if (g_ascii_isprint(ssid[i]))
1382                         name[i] = ssid[i];
1383                 else
1384                         name[i] = ' ';
1385         }
1386
1387         connman_network_set_name(network, name);
1388
1389         g_free(name);
1390
1391         index = connman_device_get_index(device);
1392         connman_network_set_index(network, index);
1393
1394         connman_network_set_protocol(network, CONNMAN_NETWORK_PROTOCOL_IP);
1395
1396         if (connman_device_add_network(device, network) < 0) {
1397                 connman_network_unref(network);
1398                 return NULL;
1399         }
1400
1401         connman_network_set_available(network, TRUE);
1402
1403         return network;
1404 }
1405
1406 int __connman_service_create_and_connect(DBusMessage *msg)
1407 {
1408         struct connman_service *service;
1409         struct connman_network *network;
1410         struct connman_device *device;
1411         DBusMessageIter iter, array;
1412         const char *mode = "managed", *security = "none";
1413         const char *type = NULL, *ssid = NULL, *passphrase = NULL;
1414         unsigned int ssid_len = 0;
1415         const char *ident;
1416         char *name, *group;
1417         gboolean created = FALSE;
1418         int err;
1419
1420         dbus_message_iter_init(msg, &iter);
1421         dbus_message_iter_recurse(&iter, &array);
1422
1423         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1424                 DBusMessageIter entry, value;
1425                 const char *key;
1426
1427                 dbus_message_iter_recurse(&array, &entry);
1428                 dbus_message_iter_get_basic(&entry, &key);
1429
1430                 dbus_message_iter_next(&entry);
1431                 dbus_message_iter_recurse(&entry, &value);
1432
1433                 switch (dbus_message_iter_get_arg_type(&value)) {
1434                 case DBUS_TYPE_STRING:
1435                         if (g_str_equal(key, "Type") == TRUE)
1436                                 dbus_message_iter_get_basic(&value, &type);
1437                         else if (g_str_equal(key, "WiFi.Mode") == TRUE ||
1438                                         g_str_equal(key, "Mode") == TRUE)
1439                                 dbus_message_iter_get_basic(&value, &mode);
1440                         else if (g_str_equal(key, "WiFi.Security") == TRUE ||
1441                                         g_str_equal(key, "Security") == TRUE)
1442                                 dbus_message_iter_get_basic(&value, &security);
1443                         else if (g_str_equal(key, "WiFi.Passphrase") == TRUE ||
1444                                         g_str_equal(key, "Passphrase") == TRUE)
1445                                 dbus_message_iter_get_basic(&value, &passphrase);
1446                         else if (g_str_equal(key, "WiFi.SSID") == TRUE ||
1447                                         g_str_equal(key, "SSID") == TRUE)
1448                                 dbus_message_iter_get_basic(&value, &ssid);
1449                 }
1450
1451                 dbus_message_iter_next(&array);
1452         }
1453
1454         if (type == NULL)
1455                 return -EINVAL;
1456
1457         if (g_strcmp0(type, "wifi") != 0 || g_strcmp0(mode, "managed") != 0)
1458                 return -EOPNOTSUPP;
1459
1460         if (ssid == NULL)
1461                 return -EINVAL;
1462
1463         ssid_len = strlen(ssid);
1464         if (ssid_len < 1)
1465                 return -EINVAL;
1466
1467         if (g_strcmp0(security, "none") != 0 &&
1468                         g_strcmp0(security, "wep") != 0 &&
1469                                 g_strcmp0(security, "wpa") != 0 &&
1470                                         g_strcmp0(security, "rsn") != 0)
1471                 return -EINVAL;
1472
1473         device = __connman_element_find_device(CONNMAN_SERVICE_TYPE_WIFI);
1474         if (device == NULL)
1475                 return -EOPNOTSUPP;
1476
1477         ident = __connman_device_get_ident(device);
1478         if (ident == NULL)
1479                 return -EOPNOTSUPP;
1480
1481         group = connman_wifi_build_group_name((unsigned char *) ssid,
1482                                                 ssid_len, mode, security);
1483         if (group == NULL)
1484                 return -EINVAL;
1485
1486         name = g_strdup_printf("%s_%s_%s", type, ident, group);
1487
1488         service = __connman_service_lookup(name);
1489
1490         if (service != NULL)
1491                 goto done;
1492
1493         network = create_hidden_wifi(device, ssid, mode, security);
1494         if (network != NULL) {
1495                 connman_network_set_group(network, group);
1496                 created = TRUE;
1497         }
1498
1499         service = __connman_service_lookup(name);
1500
1501 done:
1502         g_free(name);
1503         g_free(group);
1504
1505         if (service == NULL) {
1506                 err = -EOPNOTSUPP;
1507                 goto failed;
1508         }
1509
1510         __connman_device_disconnect(device);
1511
1512         if (passphrase != NULL) {
1513                 g_free(service->passphrase);
1514                 service->passphrase = g_strdup(passphrase);
1515         }
1516
1517         err = __connman_service_connect(service);
1518         if (err < 0 && err != -EINPROGRESS)
1519                 goto failed;
1520
1521         g_dbus_send_reply(connection, msg,
1522                                 DBUS_TYPE_OBJECT_PATH, &service->path,
1523                                                         DBUS_TYPE_INVALID);
1524
1525         return 0;
1526
1527 failed:
1528         if (service != NULL && created == TRUE) {
1529                 struct connman_network *network = service->network;
1530
1531                 if (network != NULL) {
1532                         connman_network_set_available(network, FALSE);
1533                         __connman_device_cleanup_networks(device);
1534                 }
1535
1536                 __connman_service_put(service);
1537         }
1538
1539         return err;
1540 }
1541
1542 /**
1543  * __connman_service_get:
1544  * @identifier: service identifier
1545  *
1546  * Look up a service by identifier or create a new one if not found
1547  */
1548 static struct connman_service *__connman_service_get(const char *identifier)
1549 {
1550         struct connman_service *service;
1551         GSequenceIter *iter;
1552
1553         iter = g_hash_table_lookup(service_hash, identifier);
1554         if (iter != NULL) {
1555                 service = g_sequence_get(iter);
1556                 if (service != NULL)
1557                         g_atomic_int_inc(&service->refcount);
1558                 return service;
1559         }
1560
1561         service = connman_service_create();
1562         if (service == NULL)
1563                 return NULL;
1564
1565         DBG("service %p", service);
1566
1567         service->identifier = g_strdup(identifier);
1568
1569         service->profile = g_strdup(__connman_profile_active_ident());
1570
1571         __connman_storage_load_service(service);
1572
1573         iter = g_sequence_insert_sorted(service_list, service,
1574                                                 service_compare, NULL);
1575
1576         g_hash_table_insert(service_hash, service->identifier, iter);
1577
1578         return service;
1579 }
1580
1581 static int service_register(struct connman_service *service)
1582 {
1583         const char *path = __connman_profile_active_path();
1584         GSequenceIter *iter;
1585
1586         DBG("service %p", service);
1587
1588         if (service->path != NULL)
1589                 return -EALREADY;
1590
1591         service->path = g_strdup_printf("%s/%s", path, service->identifier);
1592
1593         DBG("path %s", service->path);
1594
1595         g_dbus_register_interface(connection, service->path,
1596                                         CONNMAN_SERVICE_INTERFACE,
1597                                         service_methods, service_signals,
1598                                                         NULL, service, NULL);
1599
1600         __connman_storage_load_service(service);
1601
1602         iter = g_hash_table_lookup(service_hash, service->identifier);
1603         if (iter != NULL)
1604                 g_sequence_sort_changed(iter, service_compare, NULL);
1605
1606         __connman_profile_changed(TRUE);
1607
1608         return 0;
1609 }
1610
1611 /**
1612  * __connman_service_lookup_from_device:
1613  * @device: device structure
1614  *
1615  * Look up a service by device (reference count will not be increased)
1616  */
1617 struct connman_service *__connman_service_lookup_from_device(struct connman_device *device)
1618 {
1619         struct connman_service *service;
1620         const char *ident;
1621         char *name;
1622
1623         ident = __connman_device_get_ident(device);
1624         if (ident == NULL)
1625                 return NULL;
1626
1627         name = g_strdup_printf("%s_%s",
1628                                 __connman_device_get_type(device), ident);
1629         service = __connman_service_lookup(name);
1630         g_free(name);
1631
1632         return service;
1633 }
1634
1635 /**
1636  * __connman_service_create_from_device:
1637  * @device: device structure
1638  *
1639  * Look up service by device and if not found, create one
1640  */
1641 struct connman_service *__connman_service_create_from_device(struct connman_device *device)
1642 {
1643         struct connman_service *service;
1644         const char *ident;
1645         char *name;
1646
1647         ident = __connman_device_get_ident(device);
1648         if (ident == NULL)
1649                 return NULL;
1650
1651         name = g_strdup_printf("%s_%s",
1652                                 __connman_device_get_type(device), ident);
1653         service = __connman_service_get(name);
1654         g_free(name);
1655
1656         if (service == NULL)
1657                 return NULL;
1658
1659         if (service->path != NULL) {
1660                 __connman_profile_changed(TRUE);
1661                 return service;
1662         }
1663
1664         service->type = __connman_device_get_service_type(device);
1665
1666         service->autoconnect = FALSE;
1667
1668         service->device = device;
1669
1670         service_register(service);
1671
1672         __connman_profile_changed(TRUE);
1673
1674         if (service->favorite == TRUE)
1675                 __connman_service_auto_connect();
1676
1677         return service;
1678 }
1679
1680 void __connman_service_remove_from_device(struct connman_device *device)
1681 {
1682         struct connman_service *service;
1683         enum connman_service_type type;
1684
1685         service = __connman_service_lookup_from_device(device);
1686         if (service == NULL)
1687                 return;
1688
1689         type = service->type;
1690
1691         __connman_service_put(service);
1692
1693         default_changed();
1694
1695         __connman_notifier_disconnect(type);
1696
1697         __connman_service_auto_connect();
1698 }
1699
1700 /**
1701  * __connman_service_lookup_from_network:
1702  * @network: network structure
1703  *
1704  * Look up a service by network (reference count will not be increased)
1705  */
1706 struct connman_service *__connman_service_lookup_from_network(struct connman_network *network)
1707 {
1708         struct connman_service *service;
1709         const char *ident, *group;
1710         char *name;
1711
1712         ident = __connman_network_get_ident(network);
1713         if (ident == NULL)
1714                 return NULL;
1715
1716         group = connman_network_get_group(network);
1717         if (group == NULL)
1718                 return NULL;
1719
1720         name = g_strdup_printf("%s_%s_%s",
1721                         __connman_network_get_type(network), ident, group);
1722         service = __connman_service_lookup(name);
1723         g_free(name);
1724
1725         return service;
1726 }
1727
1728 unsigned int __connman_service_get_order(struct connman_service *service)
1729 {
1730         GSequenceIter *iter;
1731
1732         if (service == NULL)
1733                 return 0;
1734
1735         if (service->favorite == FALSE) {
1736                 service->order = 0;
1737                 goto done;
1738         }
1739
1740         iter = g_hash_table_lookup(service_hash, service->identifier);
1741         if (iter != NULL) {
1742                 if (g_sequence_iter_get_position(iter) == 0)
1743                         service->order = 1;
1744                 else
1745                         service->order = 0;
1746         }
1747
1748 done:
1749         return service->order;
1750 }
1751
1752 static enum connman_service_type convert_network_type(struct connman_network *network)
1753 {
1754         enum connman_network_type type = connman_network_get_type(network);
1755
1756         switch (type) {
1757         case CONNMAN_NETWORK_TYPE_UNKNOWN:
1758         case CONNMAN_NETWORK_TYPE_VENDOR:
1759                 break;
1760         case CONNMAN_NETWORK_TYPE_WIFI:
1761                 return CONNMAN_SERVICE_TYPE_WIFI;
1762         case CONNMAN_NETWORK_TYPE_WIMAX:
1763                 return CONNMAN_SERVICE_TYPE_WIMAX;
1764         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
1765         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
1766                 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
1767         case CONNMAN_NETWORK_TYPE_MBM:
1768         case CONNMAN_NETWORK_TYPE_HSO:
1769                 return CONNMAN_SERVICE_TYPE_CELLULAR;
1770         }
1771
1772         return CONNMAN_SERVICE_TYPE_UNKNOWN;
1773 }
1774
1775 static enum connman_service_mode convert_wifi_mode(const char *mode)
1776 {
1777         if (mode == NULL)
1778                 return CONNMAN_SERVICE_MODE_UNKNOWN;
1779         else if (g_str_equal(mode, "managed") == TRUE)
1780                 return CONNMAN_SERVICE_MODE_MANAGED;
1781         else if (g_str_equal(mode, "adhoc") == TRUE)
1782                 return CONNMAN_SERVICE_MODE_ADHOC;
1783         else
1784                 return CONNMAN_SERVICE_MODE_UNKNOWN;
1785 }
1786
1787 static enum connman_service_mode convert_wifi_security(const char *security)
1788 {
1789         if (security == NULL)
1790                 return CONNMAN_SERVICE_SECURITY_UNKNOWN;
1791         else if (g_str_equal(security, "none") == TRUE)
1792                 return CONNMAN_SERVICE_SECURITY_NONE;
1793         else if (g_str_equal(security, "wep") == TRUE)
1794                 return CONNMAN_SERVICE_SECURITY_WEP;
1795         else if (g_str_equal(security, "wpa") == TRUE)
1796                 return CONNMAN_SERVICE_SECURITY_WPA;
1797         else if (g_str_equal(security, "rsn") == TRUE)
1798                 return CONNMAN_SERVICE_SECURITY_RSN;
1799         else
1800                 return CONNMAN_SERVICE_SECURITY_UNKNOWN;
1801 }
1802
1803 static void update_from_network(struct connman_service *service,
1804                                         struct connman_network *network)
1805 {
1806         connman_uint8_t strength = service->strength;
1807         GSequenceIter *iter;
1808         const char *str;
1809
1810         if (service->state == CONNMAN_SERVICE_STATE_READY)
1811                 return;
1812
1813         if (is_connecting(service) == TRUE)
1814                 return;
1815
1816         str = connman_network_get_string(network, "Name");
1817         if (str != NULL) {
1818                 g_free(service->name);
1819                 service->name = g_strdup(str);
1820                 service->hidden = FALSE;
1821         } else {
1822                 g_free(service->name);
1823                 service->name = NULL;
1824                 service->hidden = TRUE;
1825         }
1826
1827         service->strength = connman_network_get_uint8(network, "Strength");
1828
1829         str = connman_network_get_string(network, "WiFi.Mode");
1830         service->mode = convert_wifi_mode(str);
1831
1832         str = connman_network_get_string(network, "WiFi.Security");
1833         service->security = convert_wifi_security(str);
1834
1835         if (service->strength > strength && service->network != NULL) {
1836                 connman_network_unref(service->network);
1837                 service->network = connman_network_ref(network);
1838
1839                 strength_changed(service);
1840         }
1841
1842         if (service->network == NULL)
1843                 service->network = connman_network_ref(network);
1844
1845         iter = g_hash_table_lookup(service_hash, service->identifier);
1846         if (iter != NULL)
1847                 g_sequence_sort_changed(iter, service_compare, NULL);
1848 }
1849
1850 /**
1851  * __connman_service_create_from_network:
1852  * @network: network structure
1853  *
1854  * Look up service by network and if not found, create one
1855  */
1856 struct connman_service *__connman_service_create_from_network(struct connman_network *network)
1857 {
1858         struct connman_service *service;
1859         const char *ident, *group;
1860         char *name;
1861
1862         ident = __connman_network_get_ident(network);
1863         if (ident == NULL)
1864                 return NULL;
1865
1866         group = connman_network_get_group(network);
1867         if (group == NULL)
1868                 return NULL;
1869
1870         name = g_strdup_printf("%s_%s_%s",
1871                         __connman_network_get_type(network), ident, group);
1872         service = __connman_service_get(name);
1873         g_free(name);
1874
1875         if (service == NULL)
1876                 return NULL;
1877
1878         if (__connman_network_get_weakness(network) == TRUE)
1879                 return service;
1880
1881         if (service->path != NULL) {
1882                 update_from_network(service, network);
1883                 __connman_profile_changed(TRUE);
1884                 return service;
1885         }
1886
1887         service->type = convert_network_type(network);
1888
1889         switch (service->type) {
1890         case CONNMAN_SERVICE_TYPE_UNKNOWN:
1891         case CONNMAN_SERVICE_TYPE_ETHERNET:
1892         case CONNMAN_SERVICE_TYPE_WIMAX:
1893         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
1894         case CONNMAN_SERVICE_TYPE_CELLULAR:
1895                 service->autoconnect = FALSE;
1896                 break;
1897         case CONNMAN_SERVICE_TYPE_WIFI:
1898                 service->autoconnect = TRUE;
1899                 break;
1900         }
1901
1902         service->state = CONNMAN_SERVICE_STATE_IDLE;
1903
1904         update_from_network(service, network);
1905
1906         service_register(service);
1907
1908         __connman_profile_changed(TRUE);
1909
1910         if (service->favorite == TRUE)
1911                 __connman_service_auto_connect();
1912
1913         return service;
1914 }
1915
1916 void __connman_service_update_from_network(struct connman_network *network)
1917 {
1918         struct connman_service *service;
1919         connman_uint8_t strength;
1920
1921         service = __connman_service_lookup_from_network(network);
1922         if (service == NULL)
1923                 return;
1924
1925         if (service->network == NULL)
1926                 return;
1927
1928         strength = connman_network_get_uint8(service->network, "Strength");
1929         if (strength == service->strength)
1930                 return;
1931
1932         service->strength = strength;
1933
1934         strength_changed(service);
1935 }
1936
1937 void __connman_service_remove_from_network(struct connman_network *network)
1938 {
1939         struct connman_service *service;
1940
1941         service = __connman_service_lookup_from_network(network);
1942         if (service == NULL)
1943                 return;
1944
1945         __connman_service_put(service);
1946 }
1947
1948 static int service_load(struct connman_service *service)
1949 {
1950         const char *ident = service->profile;
1951         GKeyFile *keyfile;
1952         gchar *pathname, *data = NULL;
1953         gsize length;
1954         gchar *str;
1955         unsigned int ssid_len;
1956         int err = 0;
1957
1958         DBG("service %p", service);
1959
1960         if (ident == NULL)
1961                 return -EINVAL;
1962
1963         pathname = g_strdup_printf("%s/%s.profile", STORAGEDIR, ident);
1964         if (pathname == NULL)
1965                 return -ENOMEM;
1966
1967         keyfile = g_key_file_new();
1968
1969         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) {
1970                 g_free(pathname);
1971                 return -ENOENT;
1972         }
1973
1974         g_free(pathname);
1975
1976         if (g_key_file_load_from_data(keyfile, data, length,
1977                                                         0, NULL) == FALSE) {
1978                 g_free(data);
1979                 return -EILSEQ;
1980         }
1981
1982         g_free(data);
1983
1984         switch (service->type) {
1985         case CONNMAN_SERVICE_TYPE_UNKNOWN:
1986         case CONNMAN_SERVICE_TYPE_ETHERNET:
1987                 break;
1988         case CONNMAN_SERVICE_TYPE_WIFI:
1989                 if (service->name == NULL) {
1990                         gchar *name;
1991
1992                         name = g_key_file_get_string(keyfile,
1993                                         service->identifier, "Name", NULL);
1994                         if (name != NULL) {
1995                                 g_free(service->name);
1996                                 service->name = name;
1997                         }
1998
1999                         if (service->network != NULL)
2000                                 connman_network_set_name(service->network,
2001                                                                         name);
2002                 }
2003
2004                 if (service->network &&
2005                                 connman_network_get_blob(service->network,
2006                                         "WiFi.SSID", &ssid_len) == NULL) {
2007                         gchar *hex_ssid;
2008
2009                         hex_ssid = g_key_file_get_string(keyfile,
2010                                                         service->identifier,
2011                                                                 "SSID", NULL);
2012
2013                         if (hex_ssid != NULL) {
2014                                 gchar *ssid;
2015                                 unsigned int i, j = 0, hex;
2016                                 size_t hex_ssid_len = strlen(hex_ssid);
2017
2018                                 ssid = g_try_malloc0(hex_ssid_len / 2);
2019                                 if (ssid == NULL) {
2020                                         g_free(hex_ssid);
2021                                         err = -ENOMEM;
2022                                         goto done;
2023                                 }
2024
2025                                 for (i = 0; i < hex_ssid_len; i += 2) {
2026                                         sscanf(hex_ssid + i, "%02x", &hex);
2027                                         ssid[j++] = hex;
2028                                 }
2029
2030                                 connman_network_set_blob(service->network,
2031                                         "WiFi.SSID", ssid, hex_ssid_len / 2);
2032                         }
2033
2034                         g_free(hex_ssid);
2035                 }
2036                 /* fall through */
2037
2038         case CONNMAN_SERVICE_TYPE_WIMAX:
2039         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
2040         case CONNMAN_SERVICE_TYPE_CELLULAR:
2041                 service->favorite = g_key_file_get_boolean(keyfile,
2042                                 service->identifier, "Favorite", NULL);
2043
2044                 service->autoconnect = g_key_file_get_boolean(keyfile,
2045                                 service->identifier, "AutoConnect", NULL);
2046
2047                 str = g_key_file_get_string(keyfile,
2048                                 service->identifier, "Failure", NULL);
2049                 if (str != NULL) {
2050                         service->state = CONNMAN_SERVICE_STATE_FAILURE;
2051                         service->error = string2error(str);
2052                 }
2053                 break;
2054         }
2055
2056         str = g_key_file_get_string(keyfile,
2057                                 service->identifier, "Modified", NULL);
2058         if (str != NULL) {
2059                 g_time_val_from_iso8601(str, &service->modified);
2060                 g_free(str);
2061         }
2062
2063         str = g_key_file_get_string(keyfile,
2064                                 service->identifier, "Passphrase", NULL);
2065         if (str != NULL) {
2066                 g_free(service->passphrase);
2067                 service->passphrase = str;
2068         }
2069
2070         if (service->ipconfig != NULL)
2071                 __connman_ipconfig_load(service->ipconfig, keyfile,
2072                                         service->identifier, "IPv4.");
2073
2074 done:
2075         g_key_file_free(keyfile);
2076
2077         return err;
2078 }
2079
2080 static int service_save(struct connman_service *service)
2081 {
2082         const char *ident = service->profile;
2083         GKeyFile *keyfile;
2084         gchar *pathname, *data = NULL;
2085         gsize length;
2086         gchar *str;
2087         int err = 0;
2088
2089         DBG("service %p", service);
2090
2091         if (ident == NULL)
2092                 return -EINVAL;
2093
2094         pathname = g_strdup_printf("%s/%s.profile", STORAGEDIR, ident);
2095         if (pathname == NULL)
2096                 return -ENOMEM;
2097
2098         keyfile = g_key_file_new();
2099
2100         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE)
2101                 goto update;
2102
2103         if (length > 0) {
2104                 if (g_key_file_load_from_data(keyfile, data, length,
2105                                                         0, NULL) == FALSE)
2106                         goto done;
2107         }
2108
2109         g_free(data);
2110
2111 update:
2112         if (service->name != NULL)
2113                 g_key_file_set_string(keyfile, service->identifier,
2114                                                 "Name", service->name);
2115
2116         switch (service->type) {
2117         case CONNMAN_SERVICE_TYPE_UNKNOWN:
2118         case CONNMAN_SERVICE_TYPE_ETHERNET:
2119                 break;
2120         case CONNMAN_SERVICE_TYPE_WIFI:
2121                 if (service->network) {
2122                         const unsigned char *ssid;
2123                         unsigned int ssid_len = 0;
2124
2125                         ssid = connman_network_get_blob(service->network,
2126                                                         "WiFi.SSID", &ssid_len);
2127
2128                         if (ssid != NULL && ssid_len > 0 && ssid[0] != '\0') {
2129                                 char *identifier = service->identifier;
2130                                 GString *str;
2131                                 unsigned int i;
2132
2133                                 str = g_string_sized_new(ssid_len * 2);
2134                                 if (str == NULL) {
2135                                         err = -ENOMEM;
2136                                         goto done;
2137                                 }
2138
2139                                 for (i = 0; i < ssid_len; i++)
2140                                         g_string_append_printf(str,
2141                                                         "%02x", ssid[i]);
2142
2143                                 g_key_file_set_string(keyfile, identifier,
2144                                                         "SSID", str->str);
2145
2146                                 g_string_free(str, TRUE);
2147                         }
2148                 }
2149                 /* fall through */
2150
2151         case CONNMAN_SERVICE_TYPE_WIMAX:
2152         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
2153         case CONNMAN_SERVICE_TYPE_CELLULAR:
2154                 g_key_file_set_boolean(keyfile, service->identifier,
2155                                         "Favorite", service->favorite);
2156
2157                 if (service->favorite == TRUE)
2158                         g_key_file_set_boolean(keyfile, service->identifier,
2159                                         "AutoConnect", service->autoconnect);
2160
2161                 if (service->state == CONNMAN_SERVICE_STATE_FAILURE) {
2162                         const char *failure = error2string(service->error);
2163                         if (failure != NULL)
2164                                 g_key_file_set_string(keyfile,
2165                                                         service->identifier,
2166                                                         "Failure", failure);
2167                 } else {
2168                         g_key_file_remove_key(keyfile, service->identifier,
2169                                                         "Failure", NULL);
2170                 }
2171                 break;
2172         }
2173
2174         str = g_time_val_to_iso8601(&service->modified);
2175         if (str != NULL) {
2176                 g_key_file_set_string(keyfile, service->identifier,
2177                                                         "Modified", str);
2178                 g_free(str);
2179         }
2180
2181         if (service->passphrase != NULL && strlen(service->passphrase) > 0)
2182                 g_key_file_set_string(keyfile, service->identifier,
2183                                         "Passphrase", service->passphrase);
2184         else
2185                 g_key_file_remove_key(keyfile, service->identifier,
2186                                                         "Passphrase", NULL);
2187
2188         if (service->ipconfig != NULL)
2189                 __connman_ipconfig_save(service->ipconfig, keyfile,
2190                                         service->identifier, "IPv4.");
2191
2192         data = g_key_file_to_data(keyfile, &length, NULL);
2193
2194         if (g_file_set_contents(pathname, data, length, NULL) == FALSE)
2195                 connman_error("Failed to store service information");
2196
2197 done:
2198         g_free(data);
2199
2200         g_key_file_free(keyfile);
2201
2202         g_free(pathname);
2203
2204         return err;
2205 }
2206
2207 static struct connman_storage service_storage = {
2208         .name           = "service",
2209         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
2210         .service_load   = service_load,
2211         .service_save   = service_save,
2212 };
2213
2214 int __connman_service_init(void)
2215 {
2216         DBG("");
2217
2218         connection = connman_dbus_get_connection();
2219
2220         if (connman_storage_register(&service_storage) < 0)
2221                 connman_error("Failed to register service storage");
2222
2223         service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
2224                                                                 NULL, NULL);
2225
2226         service_list = g_sequence_new(service_free);
2227
2228         return 0;
2229 }
2230
2231 void __connman_service_cleanup(void)
2232 {
2233         DBG("");
2234
2235         g_sequence_free(service_list);
2236         service_list = NULL;
2237
2238         g_hash_table_destroy(service_hash);
2239         service_hash = NULL;
2240
2241         connman_storage_unregister(&service_storage);
2242
2243         dbus_connection_unref(connection);
2244 }