Fix WiFi connection handling
[connman] / plugins / supplicant.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 <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <gdbus.h>
31
32 #define CONNMAN_API_SUBJECT_TO_CHANGE
33 #include <connman/device.h>
34 #include <connman/dbus.h>
35 #include <connman/log.h>
36
37 #include "inet.h"
38 #include "supplicant.h"
39
40 #define TIMEOUT 5000
41
42 #define IEEE80211_CAP_ESS       0x0001
43 #define IEEE80211_CAP_IBSS      0x0002
44 #define IEEE80211_CAP_PRIVACY   0x0010
45
46 #define SUPPLICANT_NAME  "fi.epitest.hostap.WPASupplicant"
47 #define SUPPLICANT_INTF  "fi.epitest.hostap.WPASupplicant"
48 #define SUPPLICANT_PATH  "/fi/epitest/hostap/WPASupplicant"
49
50 enum supplicant_state {
51         STATE_INACTIVE,
52         STATE_SCANNING,
53         STATE_ASSOCIATING,
54         STATE_ASSOCIATED,
55         STATE_4WAY_HANDSHAKE,
56         STATE_GROUP_HANDSHAKE,
57         STATE_COMPLETED,
58         STATE_DISCONNECTED,
59 };
60
61 struct supplicant_result {
62         char *identifier;
63         unsigned char *ssid;
64         unsigned int ssid_len;
65         dbus_uint16_t capabilities;
66         gboolean adhoc;
67         gboolean has_wep;
68         gboolean has_wpa;
69         gboolean has_rsn;
70         dbus_int32_t quality;
71         dbus_int32_t noise;
72         dbus_int32_t level;
73         dbus_int32_t maxrate;
74 };
75
76 struct supplicant_task {
77         int ifindex;
78         char *ifname;
79         struct connman_device *device;
80         struct connman_network *network;
81         char *path;
82         char *netpath;
83         gboolean created;
84         enum supplicant_state state;
85         GSList *scan_results;
86 };
87
88 static GSList *task_list = NULL;
89
90 static DBusConnection *connection;
91
92 static void free_task(struct supplicant_task *task)
93 {
94         DBG("task %p", task);
95
96         g_free(task->ifname);
97         g_free(task->path);
98         g_free(task);
99 }
100
101 static struct supplicant_task *find_task_by_index(int index)
102 {
103         GSList *list;
104
105         for (list = task_list; list; list = list->next) {
106                 struct supplicant_task *task = list->data;
107
108                 if (task->ifindex == index)
109                         return task;
110         }
111
112         return NULL;
113 }
114
115 static struct supplicant_task *find_task_by_path(const char *path)
116 {
117         GSList *list;
118
119         for (list = task_list; list; list = list->next) {
120                 struct supplicant_task *task = list->data;
121
122                 if (g_str_equal(task->path, path) == TRUE)
123                         return task;
124         }
125
126         return NULL;
127 }
128
129 static void add_interface_reply(DBusPendingCall *call, void *user_data)
130 {
131         struct supplicant_task *task = user_data;
132         DBusMessage *reply;
133         DBusError error;
134         const char *path;
135
136         DBG("task %p", task);
137
138         reply = dbus_pending_call_steal_reply(call);
139         if (reply == NULL)
140                 return;
141
142         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
143                 goto done;
144
145         dbus_error_init(&error);
146
147         if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
148                                                 DBUS_TYPE_INVALID) == FALSE) {
149                 if (dbus_error_is_set(&error) == TRUE) {
150                         connman_error("%s", error.message);
151                         dbus_error_free(&error);
152                 } else
153                         connman_error("Wrong arguments for add interface");
154                 goto done;
155         }
156
157         DBG("path %s", path);
158
159         task->path = g_strdup(path);
160         task->created = TRUE;
161
162         connman_device_set_powered(task->device, TRUE);
163
164 done:
165         dbus_message_unref(reply);
166 }
167
168 static int add_interface(struct supplicant_task *task)
169 {
170         DBusMessage *message;
171         DBusPendingCall *call;
172
173         DBG("task %p", task);
174
175         message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
176                                         SUPPLICANT_INTF, "addInterface");
177         if (message == NULL)
178                 return -ENOMEM;
179
180         dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname,
181                                                         DBUS_TYPE_INVALID);
182
183         if (dbus_connection_send_with_reply(connection, message,
184                                                 &call, TIMEOUT) == FALSE) {
185                 connman_error("Failed to add interface");
186                 dbus_message_unref(message);
187                 return -EIO;
188         }
189
190         dbus_pending_call_set_notify(call, add_interface_reply, task, NULL);
191
192         dbus_message_unref(message);
193
194         return -EINPROGRESS;
195 }
196
197 static void get_interface_reply(DBusPendingCall *call, void *user_data)
198 {
199         struct supplicant_task *task = user_data;
200         DBusMessage *reply;
201         DBusError error;
202         const char *path;
203
204         DBG("task %p", task);
205
206         reply = dbus_pending_call_steal_reply(call);
207         if (reply == NULL)
208                 return;
209
210         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
211                 add_interface(task);
212                 goto done;
213         }
214
215         dbus_error_init(&error);
216
217         if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
218                                                 DBUS_TYPE_INVALID) == FALSE) {
219                 if (dbus_error_is_set(&error) == TRUE) {
220                         connman_error("%s", error.message);
221                         dbus_error_free(&error);
222                 } else
223                         connman_error("Wrong arguments for get interface");
224                 goto done;
225         }
226
227         DBG("path %s", path);
228
229         task->path = g_strdup(path);
230         task->created = FALSE;
231
232         connman_device_set_powered(task->device, TRUE);
233
234 done:
235         dbus_message_unref(reply);
236 }
237
238 static int create_interface(struct supplicant_task *task)
239 {
240         DBusMessage *message;
241         DBusPendingCall *call;
242
243         DBG("task %p", task);
244
245         message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
246                                         SUPPLICANT_INTF, "getInterface");
247         if (message == NULL)
248                 return -ENOMEM;
249
250         dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname,
251                                                         DBUS_TYPE_INVALID);
252
253         if (dbus_connection_send_with_reply(connection, message,
254                                                 &call, TIMEOUT) == FALSE) {
255                 connman_error("Failed to get interface");
256                 dbus_message_unref(message);
257                 return -EIO;
258         }
259
260         dbus_pending_call_set_notify(call, get_interface_reply, task, NULL);
261
262         dbus_message_unref(message);
263
264         return -EINPROGRESS;
265 }
266
267 static void remove_interface_reply(DBusPendingCall *call, void *user_data)
268 {
269         struct supplicant_task *task = user_data;
270         DBusMessage *reply;
271
272         DBG("task %p", task);
273
274         reply = dbus_pending_call_steal_reply(call);
275
276         connman_device_set_powered(task->device, FALSE);
277
278         free_task(task);
279
280         dbus_message_unref(reply);
281 }
282
283 static int remove_interface(struct supplicant_task *task)
284 {
285         DBusMessage *message;
286         DBusPendingCall *call;
287
288         DBG("task %p", task);
289
290         if (task->created == FALSE) {
291                 connman_device_set_powered(task->device, FALSE);
292                 return 0;
293         }
294
295         message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
296                                         SUPPLICANT_INTF, "removeInterface");
297         if (message == NULL)
298                 return -ENOMEM;
299
300         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->path,
301                                                         DBUS_TYPE_INVALID);
302
303         if (dbus_connection_send_with_reply(connection, message,
304                                                 &call, TIMEOUT) == FALSE) {
305                 connman_error("Failed to remove interface");
306                 dbus_message_unref(message);
307                 return -EIO;
308         }
309
310         dbus_pending_call_set_notify(call, remove_interface_reply, task, NULL);
311
312         dbus_message_unref(message);
313
314         return -EINPROGRESS;
315 }
316
317 #if 0
318 static int set_ap_scan(struct supplicant_task *task)
319 {
320         DBusMessage *message, *reply;
321         DBusError error;
322         guint32 ap_scan = 1;
323
324         DBG("task %p", task);
325
326         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
327                                 SUPPLICANT_INTF ".Interface", "setAPScan");
328         if (message == NULL)
329                 return -ENOMEM;
330
331         dbus_message_append_args(message, DBUS_TYPE_UINT32, &ap_scan,
332                                                         DBUS_TYPE_INVALID);
333
334         dbus_error_init(&error);
335
336         reply = dbus_connection_send_with_reply_and_block(connection,
337                                                         message, -1, &error);
338         if (reply == NULL) {
339                 if (dbus_error_is_set(&error) == TRUE) {
340                         connman_error("%s", error.message);
341                         dbus_error_free(&error);
342                 } else
343                         connman_error("Failed to set AP scan");
344                 dbus_message_unref(message);
345                 return -EIO;
346         }
347
348         dbus_message_unref(message);
349
350         dbus_message_unref(reply);
351
352         return 0;
353 }
354 #endif
355
356 static int add_network(struct supplicant_task *task)
357 {
358         DBusMessage *message, *reply;
359         DBusError error;
360         const char *path;
361
362         DBG("task %p", task);
363
364         if (task->netpath != NULL)
365                 return -EALREADY;
366
367         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
368                                 SUPPLICANT_INTF ".Interface", "addNetwork");
369         if (message == NULL)
370                 return -ENOMEM;
371
372         dbus_error_init(&error);
373
374         reply = dbus_connection_send_with_reply_and_block(connection,
375                                                         message, -1, &error);
376         if (reply == NULL) {
377                 if (dbus_error_is_set(&error) == TRUE) {
378                         connman_error("%s", error.message);
379                         dbus_error_free(&error);
380                 } else
381                         connman_error("Failed to add network");
382                 dbus_message_unref(message);
383                 return -EIO;
384         }
385
386         dbus_message_unref(message);
387
388         dbus_error_init(&error);
389
390         if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
391                                                 DBUS_TYPE_INVALID) == FALSE) {
392                 if (dbus_error_is_set(&error) == TRUE) {
393                         connman_error("%s", error.message);
394                         dbus_error_free(&error);
395                 } else
396                         connman_error("Wrong arguments for network");
397                 dbus_message_unref(reply);
398                 return -EIO;
399         }
400
401         DBG("path %s", path);
402
403         task->netpath = g_strdup(path);
404
405         dbus_message_unref(reply);
406
407         return 0;
408 }
409
410 static int remove_network(struct supplicant_task *task)
411 {
412         DBusMessage *message, *reply;
413         DBusError error;
414
415         DBG("task %p", task);
416
417         if (task->netpath == NULL)
418                 return -EINVAL;
419
420         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
421                                 SUPPLICANT_INTF ".Interface", "removeNetwork");
422         if (message == NULL)
423                 return -ENOMEM;
424
425         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->netpath,
426                                                         DBUS_TYPE_INVALID);
427
428         dbus_error_init(&error);
429
430         reply = dbus_connection_send_with_reply_and_block(connection,
431                                                         message, -1, &error);
432         if (reply == NULL) {
433                 if (dbus_error_is_set(&error) == TRUE) {
434                         connman_error("%s", error.message);
435                         dbus_error_free(&error);
436                 } else
437                         connman_error("Failed to remove network");
438                 dbus_message_unref(message);
439                 return -EIO;
440         }
441
442         dbus_message_unref(message);
443
444         dbus_message_unref(reply);
445
446         g_free(task->netpath);
447         task->netpath = NULL;
448
449         return 0;
450 }
451
452 static int select_network(struct supplicant_task *task)
453 {
454         DBusMessage *message, *reply;
455         DBusError error;
456
457         DBG("task %p", task);
458
459         if (task->netpath == NULL)
460                 return -EINVAL;
461
462         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
463                                 SUPPLICANT_INTF ".Interface", "selectNetwork");
464         if (message == NULL)
465                 return -ENOMEM;
466
467         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->netpath,
468                                                         DBUS_TYPE_INVALID);
469
470         dbus_error_init(&error);
471
472         reply = dbus_connection_send_with_reply_and_block(connection,
473                                                         message, -1, &error);
474         if (reply == NULL) {
475                 if (dbus_error_is_set(&error) == TRUE) {
476                         connman_error("%s", error.message);
477                         dbus_error_free(&error);
478                 } else
479                         connman_error("Failed to select network");
480                 dbus_message_unref(message);
481                 return -EIO;
482         }
483
484         dbus_message_unref(message);
485
486         dbus_message_unref(reply);
487
488         return 0;
489 }
490
491 static int enable_network(struct supplicant_task *task)
492 {
493         DBusMessage *message, *reply;
494         DBusError error;
495
496         DBG("task %p", task);
497
498         if (task->netpath == NULL)
499                 return -EINVAL;
500
501         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath,
502                                         SUPPLICANT_INTF ".Network", "enable");
503         if (message == NULL)
504                 return -ENOMEM;
505
506         dbus_error_init(&error);
507
508         reply = dbus_connection_send_with_reply_and_block(connection,
509                                                         message, -1, &error);
510         if (reply == NULL) {
511                 if (dbus_error_is_set(&error) == TRUE) {
512                         connman_error("%s", error.message);
513                         dbus_error_free(&error);
514                 } else
515                         connman_error("Failed to enable network");
516                 dbus_message_unref(message);
517                 return -EIO;
518         }
519
520         dbus_message_unref(message);
521
522         dbus_message_unref(reply);
523
524         return 0;
525 }
526
527 static int disable_network(struct supplicant_task *task)
528 {
529         DBusMessage *message, *reply;
530         DBusError error;
531
532         DBG("task %p", task);
533
534         if (task->netpath == NULL)
535                 return -EINVAL;
536
537         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath,
538                                         SUPPLICANT_INTF ".Network", "disable");
539         if (message == NULL)
540                 return -ENOMEM;
541
542         dbus_error_init(&error);
543
544         reply = dbus_connection_send_with_reply_and_block(connection,
545                                                         message, -1, &error);
546         if (reply == NULL) {
547                 if (dbus_error_is_set(&error) == TRUE) {
548                         connman_error("%s", error.message);
549                         dbus_error_free(&error);
550                 } else
551                         connman_error("Failed to disable network");
552                 dbus_message_unref(message);
553                 return -EIO;
554         }
555
556         dbus_message_unref(message);
557
558         dbus_message_unref(reply);
559
560         return 0;
561 }
562
563 static int set_network(struct supplicant_task *task,
564                                 const unsigned char *network, int len,
565                                 const char *security, const char *passphrase)
566 {
567         DBusMessage *message, *reply;
568         DBusMessageIter array, dict;
569         DBusError error;
570
571         DBG("task %p", task);
572
573         if (task->netpath == NULL)
574                 return -EINVAL;
575
576         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath,
577                                         SUPPLICANT_INTF ".Network", "set");
578         if (message == NULL)
579                 return -ENOMEM;
580
581         dbus_message_iter_init_append(message, &array);
582
583         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
584                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
585                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
586                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
587
588         connman_dbus_dict_append_array(&dict, "ssid",
589                                         DBUS_TYPE_BYTE, &network, len);
590
591         if (g_ascii_strcasecmp(security, "wpa") == 0 ||
592                                 g_ascii_strcasecmp(security, "wpa2") == 0) {
593                 const char *key_mgmt = "WPA-PSK";
594                 connman_dbus_dict_append_variant(&dict, "key_mgmt",
595                                                 DBUS_TYPE_STRING, &key_mgmt);
596
597                 if (passphrase && strlen(passphrase) > 0)
598                         connman_dbus_dict_append_variant(&dict, "psk",
599                                                 DBUS_TYPE_STRING, &passphrase);
600         } else if (g_ascii_strcasecmp(security, "wep") == 0) {
601                 const char *key_mgmt = "NONE", *index = "0";
602                 connman_dbus_dict_append_variant(&dict, "key_mgmt",
603                                                 DBUS_TYPE_STRING, &key_mgmt);
604
605                 if (passphrase) {
606                         int size = strlen(passphrase);
607                         if (size == 10 || size == 26) {
608                                 unsigned char *key = malloc(13);
609                                 char tmp[3];
610                                 int i;
611                                 memset(tmp, 0, sizeof(tmp));
612                                 if (key == NULL)
613                                         size = 0;
614                                 for (i = 0; i < size / 2; i++) {
615                                         memcpy(tmp, passphrase + (i * 2), 2);
616                                         key[i] = (unsigned char) strtol(tmp,
617                                                                 NULL, 16);
618                                 }
619                                 connman_dbus_dict_append_array(&dict,
620                                                 "wep_key0", DBUS_TYPE_BYTE,
621                                                         &key, size / 2);
622                                 free(key);
623                         } else
624                                 connman_dbus_dict_append_variant(&dict,
625                                                 "wep_key0", DBUS_TYPE_STRING,
626                                                                 &passphrase);
627                         connman_dbus_dict_append_variant(&dict, "wep_tx_keyidx",
628                                                 DBUS_TYPE_STRING, &index);
629                 }
630         } else {
631                 const char *key_mgmt = "NONE";
632                 connman_dbus_dict_append_variant(&dict, "key_mgmt",
633                                                 DBUS_TYPE_STRING, &key_mgmt);
634         }
635
636         dbus_message_iter_close_container(&array, &dict);
637
638         dbus_error_init(&error);
639
640         reply = dbus_connection_send_with_reply_and_block(connection,
641                                                         message, -1, &error);
642         if (reply == NULL) {
643                 if (dbus_error_is_set(&error) == TRUE) {
644                         connman_error("%s", error.message);
645                         dbus_error_free(&error);
646                 } else
647                         connman_error("Failed to set network options");
648                 dbus_message_unref(message);
649                 return -EIO;
650         }
651
652         dbus_message_unref(message);
653
654         dbus_message_unref(reply);
655
656         return 0;
657 }
658
659 static int initiate_scan(struct supplicant_task *task)
660 {
661         DBusMessage *message;
662         DBusPendingCall *call;
663
664         DBG("task %p", task);
665
666         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
667                                         SUPPLICANT_INTF ".Interface", "scan");
668         if (message == NULL)
669                 return -ENOMEM;
670
671         if (dbus_connection_send_with_reply(connection, message,
672                                                 &call, TIMEOUT) == FALSE) {
673                 connman_error("Failed to initiate scan");
674                 dbus_message_unref(message);
675                 return -EIO;
676         }
677
678         dbus_message_unref(message);
679
680         return 0;
681 }
682
683 static void extract_ssid(DBusMessageIter *value,
684                                         struct supplicant_result *result)
685 {
686         DBusMessageIter array;
687         unsigned char *ssid;
688         int ssid_len;
689
690         dbus_message_iter_recurse(value, &array);
691         dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
692
693         if (ssid_len < 1)
694                 return;
695
696         result->ssid = g_try_malloc(ssid_len);
697         if (result->ssid == NULL)
698                 return;
699
700         memcpy(result->ssid, ssid, ssid_len);
701         result->ssid_len = ssid_len;
702
703         result->identifier = g_try_malloc0(ssid_len + 1);
704         if (result->identifier == NULL)
705                 return;
706
707         memcpy(result->identifier, ssid, ssid_len);
708 }
709
710 static void extract_wpaie(DBusMessageIter *value,
711                                         struct supplicant_result *result)
712 {
713         DBusMessageIter array;
714         unsigned char *ie;
715         int ie_len;
716
717         dbus_message_iter_recurse(value, &array);
718         dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
719
720         if (ie_len > 0)
721                 result->has_wpa = TRUE;
722 }
723
724 static void extract_rsnie(DBusMessageIter *value,
725                                         struct supplicant_result *result)
726 {
727         DBusMessageIter array;
728         unsigned char *ie;
729         int ie_len;
730
731         dbus_message_iter_recurse(value, &array);
732         dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
733
734         if (ie_len > 0)
735                 result->has_rsn = TRUE;
736 }
737
738 static void extract_capabilites(DBusMessageIter *value,
739                                         struct supplicant_result *result)
740 {
741         dbus_message_iter_get_basic(value, &result->capabilities);
742
743         if (result->capabilities & IEEE80211_CAP_ESS)
744                 result->adhoc = FALSE;
745         else if (result->capabilities & IEEE80211_CAP_IBSS)
746                 result->adhoc = TRUE;
747
748         if (result->capabilities & IEEE80211_CAP_PRIVACY)
749                 result->has_wep = TRUE;
750 }
751
752 static int get_properties(struct supplicant_task *task);
753
754 static void properties_reply(DBusPendingCall *call, void *user_data)
755 {
756         struct supplicant_task *task = user_data;
757         struct supplicant_result result;
758         struct connman_network *network;
759         DBusMessage *reply;
760         DBusMessageIter array, dict;
761         char *security, *temp = NULL;
762         unsigned char strength;
763         unsigned int i;
764
765         DBG("task %p", task);
766
767         reply = dbus_pending_call_steal_reply(call);
768         if (reply == NULL) {
769                 get_properties(task);
770                 return;
771         }
772
773         memset(&result, 0, sizeof(result));
774
775         dbus_message_iter_init(reply, &array);
776
777         dbus_message_iter_recurse(&array, &dict);
778
779         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
780                 DBusMessageIter entry, value;
781                 const char *key;
782
783                 dbus_message_iter_recurse(&dict, &entry);
784                 dbus_message_iter_get_basic(&entry, &key);
785
786                 dbus_message_iter_next(&entry);
787
788                 dbus_message_iter_recurse(&entry, &value);
789
790                 //type = dbus_message_iter_get_arg_type(&value);
791                 //dbus_message_iter_get_basic(&value, &val);
792
793                 /* 
794                  * bssid        : a (97)
795                  * ssid         : a (97)
796                  * wpaie        : a (97)
797                  * rsnie        : a (97)
798                  * frequency    : i (105)
799                  * capabilities : q (113)
800                  * quality      : i (105)
801                  * noise        : i (105)
802                  * level        : i (105)
803                  * maxrate      : i (105)
804                  */
805
806                 if (g_str_equal(key, "ssid") == TRUE)
807                         extract_ssid(&value, &result);
808                 else if (g_str_equal(key, "wpaie") == TRUE)
809                         extract_wpaie(&value, &result);
810                 else if (g_str_equal(key, "rsnie") == TRUE)
811                         extract_rsnie(&value, &result);
812                 else if (g_str_equal(key, "capabilities") == TRUE)
813                         extract_capabilites(&value, &result);
814                 else if (g_str_equal(key, "quality") == TRUE)
815                         dbus_message_iter_get_basic(&value, &result.quality);
816                 else if (g_str_equal(key, "noise") == TRUE)
817                         dbus_message_iter_get_basic(&value, &result.noise);
818                 else if (g_str_equal(key, "level") == TRUE)
819                         dbus_message_iter_get_basic(&value, &result.level);
820                 else if (g_str_equal(key, "maxrate") == TRUE)
821                         dbus_message_iter_get_basic(&value, &result.maxrate);
822
823                 dbus_message_iter_next(&dict);
824         }
825
826         if (result.identifier == NULL)
827                 goto done;
828
829         if (result.identifier[0] == '\0')
830                 goto done;
831
832         temp = g_strdup(result.identifier);
833         if (temp == NULL)
834                 goto done;
835
836         for (i = 0; i < strlen(temp); i++) {
837                 char tmp = temp[i];
838                 if ((tmp < '0' || tmp > '9') && (tmp < 'A' || tmp > 'Z') &&
839                                                 (tmp < 'a' || tmp > 'z'))
840                         temp[i] = '_';
841         }
842
843         strength = result.quality;
844
845         if (result.has_rsn == TRUE)
846                 security = "wpa2";
847         else if (result.has_wpa == TRUE)
848                 security = "wpa";
849         else if (result.has_wep == TRUE)
850                 security = "wep";
851         else
852                 security = "none";
853
854         network = connman_device_get_network(task->device, temp);
855         if (network == NULL) {
856                 const char *mode;
857                 int index;
858
859                 network = connman_network_create(temp,
860                                                 CONNMAN_NETWORK_TYPE_WIFI);
861                 if (network == NULL)
862                         goto done;
863
864                 index = connman_device_get_index(task->device);
865                 connman_network_set_index(network, index);
866
867                 connman_network_set_protocol(network,
868                                                 CONNMAN_NETWORK_PROTOCOL_IP);
869
870                 connman_network_set_string(network, "Name", result.identifier);
871
872                 connman_network_set_blob(network, "WiFi.SSID",
873                                                 result.ssid, result.ssid_len);
874
875                 mode = (result.adhoc == TRUE) ? "adhoc" : "managed";
876                 connman_network_set_string(network, "WiFi.Mode", mode);
877
878                 DBG("%s (%s %s) strength %d", result.identifier, mode,
879                                                         security, strength);
880
881                 if (connman_device_add_network(task->device, network) < 0) {
882                         connman_network_unref(network);
883                         goto done;
884                 }
885         }
886
887         connman_network_set_uint8(network, "Strength", strength);
888
889         connman_network_set_string(network, "WiFi.Security", security);
890
891 done:
892         g_free(result.identifier);
893         g_free(result.ssid);
894         g_free(temp);
895
896         dbus_message_unref(reply);
897
898         get_properties(task);
899 }
900
901 static int get_properties(struct supplicant_task *task)
902 {
903         DBusMessage *message;
904         DBusPendingCall *call;
905         char *path;
906
907         path = g_slist_nth_data(task->scan_results, 0);
908         if (path == NULL) {
909                 connman_device_set_scanning(task->device, FALSE);
910                 return 0;
911         }
912
913         message = dbus_message_new_method_call(SUPPLICANT_NAME, path,
914                                                 SUPPLICANT_INTF ".BSSID",
915                                                                 "properties");
916
917         task->scan_results = g_slist_remove(task->scan_results, path);
918         g_free(path);
919
920         if (message == NULL)
921                 return -ENOMEM;
922
923         if (dbus_connection_send_with_reply(connection, message,
924                                                 &call, TIMEOUT) == FALSE) {
925                 connman_error("Failed to get network properties");
926                 dbus_message_unref(message);
927                 return -EIO;
928         }
929
930         dbus_pending_call_set_notify(call, properties_reply, task, NULL);
931
932         dbus_message_unref(message);
933
934         return 0;
935 }
936
937 static void scan_results_reply(DBusPendingCall *call, void *user_data)
938 {
939         struct supplicant_task *task = user_data;
940         DBusMessage *reply;
941         DBusError error;
942         char **results;
943         int i, num_results;
944
945         DBG("task %p", task);
946
947         reply = dbus_pending_call_steal_reply(call);
948         if (reply == NULL) {
949                 connman_device_set_scanning(task->device, FALSE);
950                 return;
951         }
952
953         dbus_error_init(&error);
954
955         if (dbus_message_get_args(reply, &error,
956                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
957                                                 &results, &num_results,
958                                                 DBUS_TYPE_INVALID) == FALSE) {
959                 if (dbus_error_is_set(&error) == TRUE) {
960                         connman_error("%s", error.message);
961                         dbus_error_free(&error);
962                 } else
963                         connman_error("Wrong arguments for scan result");
964                 connman_device_set_scanning(task->device, FALSE);
965                 goto done;
966         }
967
968         for (i = 0; i < num_results; i++) {
969                 char *path = g_strdup(results[i]);
970                 if (path == NULL)
971                         continue;
972
973                 task->scan_results = g_slist_append(task->scan_results, path);
974         }
975
976         g_strfreev(results);
977
978         get_properties(task);
979
980 done:
981         dbus_message_unref(reply);
982 }
983
984 static int scan_results_available(struct supplicant_task *task)
985 {
986         DBusMessage *message;
987         DBusPendingCall *call;
988
989         DBG("task %p", task);
990
991         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
992                                                 SUPPLICANT_INTF ".Interface",
993                                                         "scanResults");
994         if (message == NULL)
995                 return -ENOMEM;
996
997         if (dbus_connection_send_with_reply(connection, message,
998                                                 &call, TIMEOUT) == FALSE) {
999                 connman_error("Failed to request scan result");
1000                 dbus_message_unref(message);
1001                 return -EIO;
1002         }
1003
1004         connman_device_set_scanning(task->device, TRUE);
1005
1006         dbus_pending_call_set_notify(call, scan_results_reply, task, NULL);
1007
1008         dbus_message_unref(message);
1009
1010         return 0;
1011 }
1012
1013 static void state_change(struct supplicant_task *task, DBusMessage *msg)
1014 {
1015         DBusError error;
1016         const char *state, *previous;
1017
1018         dbus_error_init(&error);
1019
1020         if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &state,
1021                                                 DBUS_TYPE_STRING, &previous,
1022                                                 DBUS_TYPE_INVALID) == FALSE) {
1023                 if (dbus_error_is_set(&error) == TRUE) {
1024                         connman_error("%s", error.message);
1025                         dbus_error_free(&error);
1026                 } else
1027                         connman_error("Wrong arguments for state change");
1028                 return;
1029         }
1030
1031         DBG("state %s ==> %s", previous, state);
1032
1033         if (g_str_equal(state, "INACTIVE") == TRUE)
1034                 task->state = STATE_INACTIVE;
1035         else if (g_str_equal(state, "SCANNING") == TRUE)
1036                 task->state = STATE_SCANNING;
1037         else if (g_str_equal(state, "ASSOCIATING") == TRUE)
1038                 task->state = STATE_ASSOCIATING;
1039         else if (g_str_equal(state, "ASSOCIATED") == TRUE)
1040                 task->state = STATE_ASSOCIATED;
1041         else if (g_str_equal(state, "GROUP_HANDSHAKE") == TRUE)
1042                 task->state = STATE_GROUP_HANDSHAKE;
1043         else if (g_str_equal(state, "4WAY_HANDSHAKE") == TRUE)
1044                 task->state = STATE_4WAY_HANDSHAKE;
1045         else if (g_str_equal(state, "COMPLETED") == TRUE)
1046                 task->state = STATE_COMPLETED;
1047         else if (g_str_equal(state, "DISCONNECTED") == TRUE)
1048                 task->state = STATE_DISCONNECTED;
1049
1050         if (task->state == STATE_SCANNING)
1051                 connman_device_set_scanning(task->device, TRUE);
1052
1053         switch (task->state) {
1054         case STATE_COMPLETED:
1055                 /* carrier on */
1056                 connman_network_set_connected(task->network, TRUE);
1057                 break;
1058         case STATE_DISCONNECTED:
1059                 /* carrier off */
1060                 connman_network_set_connected(task->network, FALSE);
1061                 break;
1062         default:
1063                 break;
1064         }
1065 }
1066
1067 static DBusHandlerResult supplicant_filter(DBusConnection *conn,
1068                                                 DBusMessage *msg, void *data)
1069 {
1070         struct supplicant_task *task;
1071         const char *member, *path;
1072
1073         if (dbus_message_has_interface(msg,
1074                                 SUPPLICANT_INTF ".Interface") == FALSE)
1075                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1076
1077         member = dbus_message_get_member(msg);
1078         if (member == NULL)
1079                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1080
1081         path = dbus_message_get_path(msg);
1082         if (path == NULL)
1083                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1084
1085         task = find_task_by_path(path);
1086         if (task == NULL)
1087                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1088
1089         DBG("task %p member %s", task, member);
1090
1091         if (g_str_equal(member, "ScanResultsAvailable") == TRUE)
1092                 scan_results_available(task);
1093         else if (g_str_equal(member, "StateChange") == TRUE)
1094                 state_change(task, msg);
1095
1096         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1097 }
1098
1099 int supplicant_start(struct connman_device *device)
1100 {
1101         struct supplicant_task *task;
1102
1103         DBG("device %p", device);
1104
1105         task = g_try_new0(struct supplicant_task, 1);
1106         if (task == NULL)
1107                 return -ENOMEM;
1108
1109         task->ifindex = connman_device_get_index(device);
1110         task->ifname = inet_index2name(task->ifindex);
1111         task->device = device;
1112
1113         if (task->ifname == NULL) {
1114                 g_free(task);
1115                 return -ENOMEM;
1116         }
1117
1118         task->created = FALSE;
1119         task->state = STATE_INACTIVE;
1120
1121         task_list = g_slist_append(task_list, task);
1122
1123         return create_interface(task);
1124 }
1125
1126 int supplicant_stop(struct connman_device *device)
1127 {
1128         int index = connman_device_get_index(device);
1129         struct supplicant_task *task;
1130
1131         DBG("device %p", device);
1132
1133         task = find_task_by_index(index);
1134         if (task == NULL)
1135                 return -ENODEV;
1136
1137         task_list = g_slist_remove(task_list, task);
1138
1139         disable_network(task);
1140
1141         remove_network(task);
1142
1143         return remove_interface(task);
1144 }
1145
1146 int supplicant_scan(struct connman_device *device)
1147 {
1148         int index = connman_device_get_index(device);
1149         struct supplicant_task *task;
1150         int err;
1151
1152         DBG("device %p", device);
1153
1154         task = find_task_by_index(index);
1155         if (task == NULL)
1156                 return -ENODEV;
1157
1158         switch (task->state) {
1159         case STATE_SCANNING:
1160                 return -EALREADY;
1161         case STATE_ASSOCIATING:
1162         case STATE_ASSOCIATED:
1163         case STATE_4WAY_HANDSHAKE:
1164         case STATE_GROUP_HANDSHAKE:
1165                 return -EBUSY;
1166         default:
1167                 break;
1168         }
1169
1170         err = initiate_scan(task);
1171
1172         return 0;
1173 }
1174
1175 int supplicant_connect(struct connman_network *network)
1176 {
1177         struct supplicant_task *task;
1178         const char *security, *passphrase;
1179         const void *ssid;
1180         unsigned int ssid_len;
1181         int index;
1182
1183         DBG("network %p", network);
1184
1185         security = connman_network_get_string(network, "WiFi.Security");
1186         passphrase = connman_network_get_string(network, "WiFi.Passphrase");
1187
1188         ssid = connman_network_get_blob(network, "WiFi.SSID", &ssid_len);
1189
1190         DBG("security %s passphrase %s", security, passphrase);
1191
1192         if (security == NULL && passphrase == NULL)
1193                 return -EINVAL;
1194
1195         if (g_str_equal(security, "none") == FALSE && passphrase == NULL)
1196                 return -EINVAL;
1197
1198         index = connman_network_get_index(network);
1199
1200         task = find_task_by_index(index);
1201         if (task == NULL)
1202                 return -ENODEV;
1203
1204         task->network = connman_network_ref(network);
1205
1206         add_network(task);
1207
1208         select_network(task);
1209         disable_network(task);
1210
1211         set_network(task, ssid, ssid_len, security, passphrase);
1212
1213         enable_network(task);
1214
1215         return 0;
1216 }
1217
1218 int supplicant_disconnect(struct connman_network *network)
1219 {
1220         struct supplicant_task *task;
1221         int index;
1222
1223         DBG("network %p", network);
1224
1225         index = connman_network_get_index(network);
1226
1227         task = find_task_by_index(index);
1228         if (task == NULL)
1229                 return -ENODEV;
1230
1231         disable_network(task);
1232
1233         remove_network(task);
1234
1235         connman_network_unref(task->network);
1236
1237         return 0;
1238 }
1239
1240 static void supplicant_activate(DBusConnection *conn)
1241 {
1242         DBusMessage *message;
1243
1244         DBG("conn %p", conn);
1245
1246         message = dbus_message_new_method_call(SUPPLICANT_NAME, "/",
1247                                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect");
1248         if (message == NULL)
1249                 return;
1250
1251         dbus_message_set_no_reply(message, TRUE);
1252
1253         dbus_connection_send(conn, message, NULL);
1254
1255         dbus_message_unref(message);
1256 }
1257
1258 static GSList *driver_list = NULL;
1259
1260 static void supplicant_probe(DBusConnection *conn, void *user_data)
1261 {
1262         GSList *list;
1263
1264         DBG("conn %p", conn);
1265
1266         for (list = driver_list; list; list = list->next) {
1267                 struct supplicant_driver *driver = list->data;
1268
1269                 DBG("driver %p name %s", driver, driver->name);
1270
1271                 if (driver->probe)
1272                         driver->probe();
1273         }
1274 }
1275
1276 static void supplicant_remove(DBusConnection *conn, void *user_data)
1277 {
1278         GSList *list;
1279
1280         DBG("conn %p", conn);
1281
1282         for (list = driver_list; list; list = list->next) {
1283                 struct supplicant_driver *driver = list->data;
1284
1285                 DBG("driver %p name %s", driver, driver->name);
1286
1287                 if (driver->remove)
1288                         driver->remove();
1289         }
1290 }
1291
1292 static const char *supplicant_rule = "type=signal,"
1293                                 "interface=" SUPPLICANT_INTF ".Interface";
1294 static guint watch;
1295
1296 static int supplicant_create(void)
1297 {
1298         if (g_slist_length(driver_list) > 0)
1299                 return 0;
1300
1301         connection = connman_dbus_get_connection();
1302         if (connection == NULL)
1303                 return -EIO;
1304
1305         DBG("connection %p", connection);
1306
1307         if (dbus_connection_add_filter(connection,
1308                                 supplicant_filter, NULL, NULL) == FALSE) {
1309                 connection = connman_dbus_get_connection();
1310                 return -EIO;
1311         }
1312
1313         dbus_bus_add_match(connection, supplicant_rule, NULL);
1314         dbus_connection_flush(connection);
1315
1316         watch = g_dbus_add_service_watch(connection, SUPPLICANT_NAME,
1317                         supplicant_probe, supplicant_remove, NULL, NULL);
1318
1319         return 0;
1320 }
1321
1322 static void supplicant_destroy(void)
1323 {
1324         if (g_slist_length(driver_list) > 0)
1325                 return;
1326
1327         DBG("connection %p", connection);
1328
1329         if (watch > 0)
1330                 g_dbus_remove_watch(connection, watch);
1331
1332         dbus_bus_remove_match(connection, supplicant_rule, NULL);
1333         dbus_connection_flush(connection);
1334
1335         dbus_connection_remove_filter(connection, supplicant_filter, NULL);
1336
1337         dbus_connection_unref(connection);
1338         connection = NULL;
1339 }
1340
1341 int supplicant_register(struct supplicant_driver *driver)
1342 {
1343         int err;
1344
1345         DBG("driver %p name %s", driver, driver->name);
1346
1347         err = supplicant_create();
1348         if (err < 0)
1349                 return err;
1350
1351         driver_list = g_slist_append(driver_list, driver);
1352
1353         if (g_dbus_check_service(connection, SUPPLICANT_NAME) == TRUE)
1354                 supplicant_probe(connection, NULL);
1355         else
1356                 supplicant_activate(connection);
1357
1358         return 0;
1359 }
1360
1361 void supplicant_unregister(struct supplicant_driver *driver)
1362 {
1363         DBG("driver %p name %s", driver, driver->name);
1364
1365         supplicant_remove(connection, NULL);
1366
1367         driver_list = g_slist_remove(driver_list, driver);
1368
1369         supplicant_destroy();
1370 }