Disable the get_bssid() function for now
[platform/upstream/connman.git] / 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 <stdio.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
33 #include <linux/if_arp.h>
34 #include <linux/wireless.h>
35 #include <net/ethernet.h>
36
37 #include <gdbus.h>
38
39 #define CONNMAN_API_SUBJECT_TO_CHANGE
40 #include <connman/device.h>
41 #include <connman/option.h>
42 #include <connman/inet.h>
43 #include <connman/dbus.h>
44 #include <connman/log.h>
45
46 #include "supplicant.h"
47
48 #define TIMEOUT 5000
49
50 #define IEEE80211_CAP_ESS       0x0001
51 #define IEEE80211_CAP_IBSS      0x0002
52 #define IEEE80211_CAP_PRIVACY   0x0010
53
54 #define SUPPLICANT_NAME  "fi.epitest.hostap.WPASupplicant"
55 #define SUPPLICANT_INTF  "fi.epitest.hostap.WPASupplicant"
56 #define SUPPLICANT_PATH  "/fi/epitest/hostap/WPASupplicant"
57
58 /* Taken from "WPA Supplicant - Common definitions" */
59 enum supplicant_state {
60         /**
61          * WPA_DISCONNECTED - Disconnected state
62          *
63          * This state indicates that client is not associated, but is likely to
64          * start looking for an access point. This state is entered when a
65          * connection is lost.
66          */
67         WPA_DISCONNECTED,
68
69         /**
70          * WPA_INACTIVE - Inactive state (wpa_supplicant disabled)
71          *
72          * This state is entered if there are no enabled networks in the
73          * configuration. wpa_supplicant is not trying to associate with a new
74          * network and external interaction (e.g., ctrl_iface call to add or
75          * enable a network) is needed to start association.
76          */
77         WPA_INACTIVE,
78
79         /**
80          * WPA_SCANNING - Scanning for a network
81          *
82          * This state is entered when wpa_supplicant starts scanning for a
83          * network.
84          */
85         WPA_SCANNING,
86
87         /**
88          * WPA_ASSOCIATING - Trying to associate with a BSS/SSID
89          *
90          * This state is entered when wpa_supplicant has found a suitable BSS
91          * to associate with and the driver is configured to try to associate
92          * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this
93          * state is entered when the driver is configured to try to associate
94          * with a network using the configured SSID and security policy.
95          */
96         WPA_ASSOCIATING,
97
98         /**
99          * WPA_ASSOCIATED - Association completed
100          *
101          * This state is entered when the driver reports that association has
102          * been successfully completed with an AP. If IEEE 802.1X is used
103          * (with or without WPA/WPA2), wpa_supplicant remains in this state
104          * until the IEEE 802.1X/EAPOL authentication has been completed.
105          */
106         WPA_ASSOCIATED,
107
108         /**
109          * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress
110          *
111          * This state is entered when WPA/WPA2 4-Way Handshake is started. In
112          * case of WPA-PSK, this happens when receiving the first EAPOL-Key
113          * frame after association. In case of WPA-EAP, this state is entered
114          * when the IEEE 802.1X/EAPOL authentication has been completed.
115          */
116         WPA_4WAY_HANDSHAKE,
117
118         /**
119          * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress
120          *
121          * This state is entered when 4-Way Key Handshake has been completed
122          * (i.e., when the supplicant sends out message 4/4) and when Group
123          * Key rekeying is started by the AP (i.e., when supplicant receives
124          * message 1/2).
125          */
126         WPA_GROUP_HANDSHAKE,
127
128         /**
129          * WPA_COMPLETED - All authentication completed
130          *
131          * This state is entered when the full authentication process is
132          * completed. In case of WPA2, this happens when the 4-Way Handshake is
133          * successfully completed. With WPA, this state is entered after the
134          * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is
135          * completed after dynamic keys are received (or if not used, after
136          * the EAP authentication has been completed). With static WEP keys and
137          * plaintext connections, this state is entered when an association
138          * has been completed.
139          *
140          * This state indicates that the supplicant has completed its
141          * processing for the association phase and that data connection is
142          * fully configured.
143          */
144         WPA_COMPLETED,
145
146         /**
147          * WPA_INVALID - Invalid state (parsing error)
148          *
149          * This state is returned if the string input is invalid. It is not
150          * an official wpa_supplicant state.
151          */
152         WPA_INVALID,
153 };
154
155 struct supplicant_result {
156         char *path;
157         char *name;
158         unsigned char *addr;
159         unsigned int addr_len;
160         unsigned char *ssid;
161         unsigned int ssid_len;
162         dbus_uint16_t capabilities;
163         gboolean adhoc;
164         gboolean has_wep;
165         gboolean has_wpa;
166         gboolean has_rsn;
167         gboolean has_wps;
168         dbus_int32_t frequency;
169         dbus_int32_t quality;
170         dbus_int32_t noise;
171         dbus_int32_t level;
172         dbus_int32_t maxrate;
173 };
174
175 struct supplicant_task {
176         int ifindex;
177         char *ifname;
178         struct connman_device *device;
179         struct connman_network *network;
180         struct connman_network *pending_network;
181         char *path;
182         char *netpath;
183         gboolean created;
184         enum supplicant_state state;
185         gboolean noscan;
186         GSList *scan_results;
187         struct iw_range *range;
188         gboolean disconnecting;
189 };
190
191 static GSList *task_list = NULL;
192
193 static DBusConnection *connection;
194
195 static void free_task(struct supplicant_task *task)
196 {
197         DBG("task %p", task);
198
199         g_free(task->ifname);
200         g_free(task->path);
201         g_free(task);
202 }
203
204 static struct supplicant_task *find_task_by_index(int index)
205 {
206         GSList *list;
207
208         for (list = task_list; list; list = list->next) {
209                 struct supplicant_task *task = list->data;
210
211                 if (task->ifindex == index)
212                         return task;
213         }
214
215         return NULL;
216 }
217
218 static struct supplicant_task *find_task_by_path(const char *path)
219 {
220         GSList *list;
221
222         for (list = task_list; list; list = list->next) {
223                 struct supplicant_task *task = list->data;
224
225                 if (g_strcmp0(task->path, path) == 0)
226                         return task;
227         }
228
229         return NULL;
230 }
231
232 static int get_range(struct supplicant_task *task)
233 {
234         struct iwreq wrq;
235         int fd, err;
236
237         fd = socket(PF_INET, SOCK_DGRAM, 0);
238         if (fd < 0)
239                 return -1;
240
241         memset(&wrq, 0, sizeof(struct iwreq));
242         strncpy(wrq.ifr_name, task->ifname, IFNAMSIZ);
243         wrq.u.data.pointer = task->range;
244         wrq.u.data.length = sizeof(struct iw_range);
245
246         err = ioctl(fd, SIOCGIWRANGE, &wrq);
247
248         close(fd);
249
250         return err;
251 }
252
253 #if 0
254 static char *get_bssid(struct connman_device *device)
255 {
256         char *bssid;
257         unsigned char ioctl_bssid[ETH_ALEN];
258         int ifindex;
259         char *ifname;
260         struct iwreq wrq;
261         int fd, err;
262
263         ifindex = connman_device_get_index(device);
264         if (ifindex < 0)
265                 return NULL;
266
267         ifname = connman_inet_ifname(ifindex);
268         if (ifname == NULL)
269                 return NULL;
270
271         fd = socket(PF_INET, SOCK_DGRAM, 0);
272         if (fd < 0) {
273                 g_free(ifname);
274                 return NULL;
275         }
276
277         memset(&wrq, 0, sizeof(wrq));
278         strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
279
280         err = ioctl(fd, SIOCGIWAP, &wrq);
281
282         g_free(ifname);
283         close(fd);
284
285         if (err < 0)
286                 return NULL;
287
288         memcpy(ioctl_bssid, wrq.u.ap_addr.sa_data, ETH_ALEN);
289
290         bssid = g_try_malloc0(13);
291         if (bssid == NULL)
292                 return NULL;
293
294         snprintf(bssid, 13, "%02x%02x%02x%02x%02x%02x",
295                                         ioctl_bssid[0], ioctl_bssid[1],
296                                         ioctl_bssid[2], ioctl_bssid[3],
297                                         ioctl_bssid[4], ioctl_bssid[5]);
298
299         return bssid;
300 }
301 #endif
302
303 static void add_interface_reply(DBusPendingCall *call, void *user_data)
304 {
305         struct supplicant_task *task = user_data;
306         DBusMessage *reply;
307         DBusError error;
308         const char *path;
309
310         DBG("task %p", task);
311
312         reply = dbus_pending_call_steal_reply(call);
313         if (reply == NULL)
314                 return;
315
316         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
317                 goto failed;
318
319         dbus_error_init(&error);
320
321         if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
322                                                 DBUS_TYPE_INVALID) == FALSE) {
323                 if (dbus_error_is_set(&error) == TRUE) {
324                         connman_error("%s", error.message);
325                         dbus_error_free(&error);
326                 } else
327                         connman_error("Wrong arguments for add interface");
328                 goto failed;
329         }
330
331         DBG("path %s", path);
332
333         task->path = g_strdup(path);
334         task->created = TRUE;
335
336         connman_device_set_powered(task->device, TRUE);
337
338         dbus_message_unref(reply);
339
340         return;
341
342 failed:
343         task_list = g_slist_remove(task_list, task);
344
345         connman_device_unref(task->device);
346
347         free_task(task);
348 }
349
350 static int add_interface(struct supplicant_task *task)
351 {
352         const char *driver = connman_option_get_string("wifi");
353         DBusMessage *message;
354         DBusMessageIter array, dict;
355         DBusPendingCall *call;
356
357         DBG("task %p", task);
358
359         message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
360                                         SUPPLICANT_INTF, "addInterface");
361         if (message == NULL)
362                 return -ENOMEM;
363
364         dbus_message_iter_init_append(message, &array);
365
366         dbus_message_iter_append_basic(&array,
367                                         DBUS_TYPE_STRING, &task->ifname);
368
369         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
370                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
371                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
372                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
373
374         connman_dbus_dict_append_variant(&dict, "driver",
375                                                 DBUS_TYPE_STRING, &driver);
376
377         dbus_message_iter_close_container(&array, &dict);
378
379         if (dbus_connection_send_with_reply(connection, message,
380                                                 &call, TIMEOUT) == FALSE) {
381                 connman_error("Failed to add interface");
382                 dbus_message_unref(message);
383                 return -EIO;
384         }
385
386         if (call == NULL) {
387                 connman_error("D-Bus connection not available");
388                 dbus_message_unref(message);
389                 return -EIO;
390         }
391
392         dbus_pending_call_set_notify(call, add_interface_reply, task, NULL);
393
394         dbus_message_unref(message);
395
396         return -EINPROGRESS;
397 }
398
399 static void get_interface_reply(DBusPendingCall *call, void *user_data)
400 {
401         struct supplicant_task *task = user_data;
402         DBusMessage *reply;
403         DBusError error;
404         const char *path;
405
406         DBG("task %p", task);
407
408         reply = dbus_pending_call_steal_reply(call);
409         if (reply == NULL)
410                 return;
411
412         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
413                 add_interface(task);
414                 goto done;
415         }
416
417         dbus_error_init(&error);
418
419         if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
420                                                 DBUS_TYPE_INVALID) == FALSE) {
421                 if (dbus_error_is_set(&error) == TRUE) {
422                         connman_error("%s", error.message);
423                         dbus_error_free(&error);
424                 } else
425                         connman_error("Wrong arguments for get interface");
426                 goto done;
427         }
428
429         DBG("path %s", path);
430
431         task->path = g_strdup(path);
432         task->created = FALSE;
433
434         connman_device_set_powered(task->device, TRUE);
435
436 done:
437         dbus_message_unref(reply);
438 }
439
440 static int create_interface(struct supplicant_task *task)
441 {
442         DBusMessage *message;
443         DBusPendingCall *call;
444
445         DBG("task %p", task);
446
447         message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
448                                         SUPPLICANT_INTF, "getInterface");
449         if (message == NULL)
450                 return -ENOMEM;
451
452         dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname,
453                                                         DBUS_TYPE_INVALID);
454
455         if (dbus_connection_send_with_reply(connection, message,
456                                                 &call, TIMEOUT) == FALSE) {
457                 connman_error("Failed to get interface");
458                 dbus_message_unref(message);
459                 return -EIO;
460         }
461
462         if (call == NULL) {
463                 connman_error("D-Bus connection not available");
464                 dbus_message_unref(message);
465                 return -EIO;
466         }
467
468         dbus_pending_call_set_notify(call, get_interface_reply, task, NULL);
469
470         dbus_message_unref(message);
471
472         return -EINPROGRESS;
473 }
474
475 static void remove_interface_reply(DBusPendingCall *call, void *user_data)
476 {
477         struct supplicant_task *task = user_data;
478         DBusMessage *reply;
479
480         DBG("task %p", task);
481
482         reply = dbus_pending_call_steal_reply(call);
483
484         connman_device_set_powered(task->device, FALSE);
485
486         connman_device_unref(task->device);
487
488         connman_inet_ifdown(task->ifindex);
489
490         free_task(task);
491
492         dbus_message_unref(reply);
493 }
494
495 static int remove_interface(struct supplicant_task *task)
496 {
497         DBusMessage *message;
498         DBusPendingCall *call;
499
500         DBG("task %p", task);
501
502         if (task->created == FALSE) {
503                 connman_device_set_powered(task->device, FALSE);
504                 return 0;
505         }
506
507         message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
508                                         SUPPLICANT_INTF, "removeInterface");
509         if (message == NULL)
510                 return -ENOMEM;
511
512         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->path,
513                                                         DBUS_TYPE_INVALID);
514
515         if (dbus_connection_send_with_reply(connection, message,
516                                                 &call, TIMEOUT) == FALSE) {
517                 connman_error("Failed to remove interface");
518                 dbus_message_unref(message);
519                 return -EIO;
520         }
521
522         if (call == NULL) {
523                 connman_error("D-Bus connection not available");
524                 dbus_message_unref(message);
525                 return -EIO;
526         }
527
528         dbus_pending_call_set_notify(call, remove_interface_reply, task, NULL);
529
530         dbus_message_unref(message);
531
532         return -EINPROGRESS;
533 }
534
535 #if 0
536 static int set_ap_scan(struct supplicant_task *task)
537 {
538         DBusMessage *message, *reply;
539         DBusError error;
540         guint32 ap_scan = 1;
541
542         DBG("task %p", task);
543
544         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
545                                 SUPPLICANT_INTF ".Interface", "setAPScan");
546         if (message == NULL)
547                 return -ENOMEM;
548
549         dbus_message_append_args(message, DBUS_TYPE_UINT32, &ap_scan,
550                                                         DBUS_TYPE_INVALID);
551
552         dbus_error_init(&error);
553
554         reply = dbus_connection_send_with_reply_and_block(connection,
555                                                         message, -1, &error);
556         if (reply == NULL) {
557                 if (dbus_error_is_set(&error) == TRUE) {
558                         connman_error("%s", error.message);
559                         dbus_error_free(&error);
560                 } else
561                         connman_error("Failed to set AP scan");
562                 dbus_message_unref(message);
563                 return -EIO;
564         }
565
566         dbus_message_unref(message);
567
568         dbus_message_unref(reply);
569
570         return 0;
571 }
572 #endif
573
574 static int add_network(struct supplicant_task *task)
575 {
576         DBusMessage *message, *reply;
577         DBusError error;
578         const char *path;
579
580         DBG("task %p", task);
581
582         if (task->netpath != NULL)
583                 return -EALREADY;
584
585         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
586                                 SUPPLICANT_INTF ".Interface", "addNetwork");
587         if (message == NULL)
588                 return -ENOMEM;
589
590         dbus_error_init(&error);
591
592         reply = dbus_connection_send_with_reply_and_block(connection,
593                                                         message, -1, &error);
594         if (reply == NULL) {
595                 if (dbus_error_is_set(&error) == TRUE) {
596                         connman_error("%s", error.message);
597                         dbus_error_free(&error);
598                 } else
599                         connman_error("Failed to add network");
600                 dbus_message_unref(message);
601                 return -EIO;
602         }
603
604         dbus_message_unref(message);
605
606         dbus_error_init(&error);
607
608         if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
609                                                 DBUS_TYPE_INVALID) == FALSE) {
610                 if (dbus_error_is_set(&error) == TRUE) {
611                         connman_error("%s", error.message);
612                         dbus_error_free(&error);
613                 } else
614                         connman_error("Wrong arguments for network");
615                 dbus_message_unref(reply);
616                 return -EIO;
617         }
618
619         DBG("path %s", path);
620
621         task->netpath = g_strdup(path);
622
623         dbus_message_unref(reply);
624
625         return 0;
626 }
627
628 static int remove_network(struct supplicant_task *task)
629 {
630         DBusMessage *message, *reply;
631         DBusError error;
632
633         DBG("task %p", task);
634
635         if (task->netpath == NULL)
636                 return -EINVAL;
637
638         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
639                                 SUPPLICANT_INTF ".Interface", "removeNetwork");
640         if (message == NULL)
641                 return -ENOMEM;
642
643         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->netpath,
644                                                         DBUS_TYPE_INVALID);
645
646         dbus_error_init(&error);
647
648         reply = dbus_connection_send_with_reply_and_block(connection,
649                                                         message, -1, &error);
650         if (reply == NULL) {
651                 if (dbus_error_is_set(&error) == TRUE) {
652                         connman_error("%s", error.message);
653                         dbus_error_free(&error);
654                 } else
655                         connman_error("Failed to remove network");
656                 dbus_message_unref(message);
657                 return -EIO;
658         }
659
660         dbus_message_unref(message);
661
662         dbus_message_unref(reply);
663
664         g_free(task->netpath);
665         task->netpath = NULL;
666
667         return 0;
668 }
669
670 static int select_network(struct supplicant_task *task)
671 {
672         DBusMessage *message, *reply;
673         DBusError error;
674
675         DBG("task %p", task);
676
677         if (task->netpath == NULL)
678                 return -EINVAL;
679
680         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
681                                 SUPPLICANT_INTF ".Interface", "selectNetwork");
682         if (message == NULL)
683                 return -ENOMEM;
684
685         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->netpath,
686                                                         DBUS_TYPE_INVALID);
687
688         dbus_error_init(&error);
689
690         reply = dbus_connection_send_with_reply_and_block(connection,
691                                                         message, -1, &error);
692         if (reply == NULL) {
693                 if (dbus_error_is_set(&error) == TRUE) {
694                         connman_error("%s", error.message);
695                         dbus_error_free(&error);
696                 } else
697                         connman_error("Failed to select network");
698                 dbus_message_unref(message);
699                 return -EIO;
700         }
701
702         dbus_message_unref(message);
703
704         dbus_message_unref(reply);
705
706         return 0;
707 }
708
709 static int enable_network(struct supplicant_task *task)
710 {
711         DBusMessage *message, *reply;
712         DBusError error;
713
714         DBG("task %p", task);
715
716         if (task->netpath == NULL)
717                 return -EINVAL;
718
719         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath,
720                                         SUPPLICANT_INTF ".Network", "enable");
721         if (message == NULL)
722                 return -ENOMEM;
723
724         dbus_error_init(&error);
725
726         reply = dbus_connection_send_with_reply_and_block(connection,
727                                                         message, -1, &error);
728         if (reply == NULL) {
729                 if (dbus_error_is_set(&error) == TRUE) {
730                         connman_error("%s", error.message);
731                         dbus_error_free(&error);
732                 } else
733                         connman_error("Failed to enable network");
734                 dbus_message_unref(message);
735                 return -EIO;
736         }
737
738         dbus_message_unref(message);
739
740         dbus_message_unref(reply);
741
742         return 0;
743 }
744
745 static int disable_network(struct supplicant_task *task)
746 {
747         DBusMessage *message, *reply;
748         DBusError error;
749
750         DBG("task %p", task);
751
752         if (task->netpath == NULL)
753                 return -EINVAL;
754
755         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath,
756                                         SUPPLICANT_INTF ".Network", "disable");
757         if (message == NULL)
758                 return -ENOMEM;
759
760         dbus_error_init(&error);
761
762         reply = dbus_connection_send_with_reply_and_block(connection,
763                                                         message, -1, &error);
764         if (reply == NULL) {
765                 if (dbus_error_is_set(&error) == TRUE) {
766                         connman_error("%s", error.message);
767                         dbus_error_free(&error);
768                 } else
769                         connman_error("Failed to disable network");
770                 dbus_message_unref(message);
771                 return -EIO;
772         }
773
774         dbus_message_unref(message);
775
776         dbus_message_unref(reply);
777
778         return 0;
779 }
780
781 static int set_network(struct supplicant_task *task,
782                                 const unsigned char *network, int len,
783                                 const char *address, const char *security,
784                                                         const char *passphrase)
785 {
786         DBusMessage *message, *reply;
787         DBusMessageIter array, dict;
788         DBusError error;
789         dbus_uint32_t scan_ssid = 1;
790
791         DBG("task %p", task);
792
793         if (task->netpath == NULL)
794                 return -EINVAL;
795
796         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath,
797                                         SUPPLICANT_INTF ".Network", "set");
798         if (message == NULL)
799                 return -ENOMEM;
800
801         dbus_message_iter_init_append(message, &array);
802
803         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
804                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
805                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
806                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
807
808         connman_dbus_dict_append_variant(&dict, "scan_ssid",
809                                          DBUS_TYPE_UINT32, &scan_ssid);
810
811         if (address)
812                 connman_dbus_dict_append_variant(&dict, "bssid",
813                                                 DBUS_TYPE_STRING, &address);
814
815         connman_dbus_dict_append_array(&dict, "ssid",
816                                         DBUS_TYPE_BYTE, &network, len);
817
818         if (g_ascii_strcasecmp(security, "wpa") == 0 ||
819                                 g_ascii_strcasecmp(security, "rsn") == 0) {
820                 const char *key_mgmt = "WPA-PSK";
821                 connman_dbus_dict_append_variant(&dict, "key_mgmt",
822                                                 DBUS_TYPE_STRING, &key_mgmt);
823
824                 if (passphrase && strlen(passphrase) > 0)
825                         connman_dbus_dict_append_variant(&dict, "psk",
826                                                 DBUS_TYPE_STRING, &passphrase);
827         } else if (g_ascii_strcasecmp(security, "wep") == 0) {
828                 const char *key_mgmt = "NONE";
829                 const char *auth_alg = "OPEN SHARED";
830                 const char *key_index = "0";
831
832                 connman_dbus_dict_append_variant(&dict, "auth_alg",
833                                                 DBUS_TYPE_STRING, &auth_alg);
834
835                 connman_dbus_dict_append_variant(&dict, "key_mgmt",
836                                                 DBUS_TYPE_STRING, &key_mgmt);
837
838                 if (passphrase) {
839                         int size = strlen(passphrase);
840                         if (size == 10 || size == 26) {
841                                 unsigned char *key = malloc(13);
842                                 char tmp[3];
843                                 int i;
844                                 memset(tmp, 0, sizeof(tmp));
845                                 if (key == NULL)
846                                         size = 0;
847                                 for (i = 0; i < size / 2; i++) {
848                                         memcpy(tmp, passphrase + (i * 2), 2);
849                                         key[i] = (unsigned char) strtol(tmp,
850                                                                 NULL, 16);
851                                 }
852                                 connman_dbus_dict_append_array(&dict,
853                                                 "wep_key0", DBUS_TYPE_BYTE,
854                                                         &key, size / 2);
855                                 free(key);
856                         } else
857                                 connman_dbus_dict_append_variant(&dict,
858                                                 "wep_key0", DBUS_TYPE_STRING,
859                                                                 &passphrase);
860
861                         connman_dbus_dict_append_variant(&dict, "wep_tx_keyidx",
862                                                 DBUS_TYPE_STRING, &key_index);
863                 }
864         } else {
865                 const char *key_mgmt = "NONE";
866                 connman_dbus_dict_append_variant(&dict, "key_mgmt",
867                                                 DBUS_TYPE_STRING, &key_mgmt);
868         }
869
870         dbus_message_iter_close_container(&array, &dict);
871
872         dbus_error_init(&error);
873
874         reply = dbus_connection_send_with_reply_and_block(connection,
875                                                         message, -1, &error);
876         if (reply == NULL) {
877                 if (dbus_error_is_set(&error) == TRUE) {
878                         connman_error("%s", error.message);
879                         dbus_error_free(&error);
880                 } else
881                         connman_error("Failed to set network options");
882                 dbus_message_unref(message);
883                 return -EIO;
884         }
885
886         dbus_message_unref(message);
887
888         dbus_message_unref(reply);
889
890         return 0;
891 }
892
893 static int initiate_scan(struct supplicant_task *task)
894 {
895         DBusMessage *message;
896         DBusPendingCall *call;
897
898         DBG("task %p", task);
899
900         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
901                                         SUPPLICANT_INTF ".Interface", "scan");
902         if (message == NULL)
903                 return -ENOMEM;
904
905         if (dbus_connection_send_with_reply(connection, message,
906                                                 &call, TIMEOUT) == FALSE) {
907                 connman_error("Failed to initiate scan");
908                 dbus_message_unref(message);
909                 return -EIO;
910         }
911
912         dbus_message_unref(message);
913
914         return 0;
915 }
916
917 static struct {
918         char *name;
919         char *value;
920 } special_ssid[] = {
921         { "<hidden>", "hidden"  },
922         { "default",  "linksys" },
923         { "wireless"  },
924         { "linksys"   },
925         { "netgear"   },
926         { "dlink"     },
927         { "2wire"     },
928         { "compaq"    },
929         { "tsunami"   },
930         { "comcomcom", "3com"     },
931         { "3Com",      "3com"     },
932         { "Symbol",    "symbol"   },
933         { "Motorola",  "motorola" },
934         { "Wireless" , "wireless" },
935         { "WLAN",      "wlan"     },
936         { }
937 };
938
939 static char *build_group(const char *addr, const char *name,
940                         const unsigned char *ssid, unsigned int ssid_len,
941                                         const char *mode, const char *security)
942 {
943         GString *str;
944         unsigned int i;
945
946         if (addr == NULL)
947                 return NULL;
948
949         str = g_string_sized_new((ssid_len * 2) + 24);
950         if (str == NULL)
951                 return NULL;
952
953         if (ssid == NULL) {
954                 g_string_append_printf(str, "hidden_%s", addr);
955                 goto done;
956         }
957
958         for (i = 0; special_ssid[i].name; i++) {
959                 if (g_strcmp0(special_ssid[i].name, name) == 0) {
960                         if (special_ssid[i].value == NULL)
961                                 g_string_append_printf(str, "%s_%s",
962                                                                 name, addr);
963                         else
964                                 g_string_append_printf(str, "%s_%s",
965                                                 special_ssid[i].value, addr);
966                         goto done;
967                 }
968         }
969
970         if (ssid_len > 0 && ssid[0] != '\0') {
971                 for (i = 0; i < ssid_len; i++)
972                         g_string_append_printf(str, "%02x", ssid[i]);
973         } else
974                 g_string_append_printf(str, "hidden_%s", addr);
975
976 done:
977         g_string_append_printf(str, "_%s_%s", mode, security);
978
979         return g_string_free(str, FALSE);
980 }
981
982 static void extract_addr(DBusMessageIter *value,
983                                         struct supplicant_result *result)
984 {
985         DBusMessageIter array;
986         struct ether_addr eth;
987         unsigned char *addr;
988         int addr_len;
989
990         dbus_message_iter_recurse(value, &array);
991         dbus_message_iter_get_fixed_array(&array, &addr, &addr_len);
992
993         if (addr_len != 6)
994                 return;
995
996         result->addr = g_try_malloc(addr_len);
997         if (result->addr == NULL)
998                 return;
999
1000         memcpy(result->addr, addr, addr_len);
1001         result->addr_len = addr_len;
1002
1003         result->path = g_try_malloc0(13);
1004         if (result->path == NULL)
1005                 return;
1006
1007         memcpy(&eth, addr, sizeof(eth));
1008         snprintf(result->path, 13, "%02x%02x%02x%02x%02x%02x",
1009                                                 eth.ether_addr_octet[0],
1010                                                 eth.ether_addr_octet[1],
1011                                                 eth.ether_addr_octet[2],
1012                                                 eth.ether_addr_octet[3],
1013                                                 eth.ether_addr_octet[4],
1014                                                 eth.ether_addr_octet[5]);
1015 }
1016
1017 static void extract_ssid(DBusMessageIter *value,
1018                                         struct supplicant_result *result)
1019 {
1020         DBusMessageIter array;
1021         unsigned char *ssid;
1022         int ssid_len, i;
1023
1024         dbus_message_iter_recurse(value, &array);
1025         dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
1026
1027         if (ssid_len < 1)
1028                 return;
1029
1030         if (ssid[0] == '\0')
1031                 return;
1032
1033         result->ssid = g_try_malloc(ssid_len);
1034         if (result->ssid == NULL)
1035                 return;
1036
1037         memcpy(result->ssid, ssid, ssid_len);
1038         result->ssid_len = ssid_len;
1039
1040         result->name = g_try_malloc0(ssid_len + 1);
1041         if (result->name == NULL)
1042                 return;
1043
1044         for (i = 0; i < ssid_len; i++) {
1045                 if (g_ascii_isprint(ssid[i]))
1046                         result->name[i] = ssid[i];
1047                 else
1048                         result->name[i] = ' ';
1049         }
1050 }
1051
1052 static void extract_wpaie(DBusMessageIter *value,
1053                                         struct supplicant_result *result)
1054 {
1055         DBusMessageIter array;
1056         unsigned char *ie;
1057         int ie_len;
1058
1059         dbus_message_iter_recurse(value, &array);
1060         dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
1061
1062         if (ie_len > 0)
1063                 result->has_wpa = TRUE;
1064 }
1065
1066 static void extract_rsnie(DBusMessageIter *value,
1067                                         struct supplicant_result *result)
1068 {
1069         DBusMessageIter array;
1070         unsigned char *ie;
1071         int ie_len;
1072
1073         dbus_message_iter_recurse(value, &array);
1074         dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
1075
1076         if (ie_len > 0)
1077                 result->has_rsn = TRUE;
1078 }
1079
1080 static void extract_wpsie(DBusMessageIter *value,
1081                                         struct supplicant_result *result)
1082 {
1083         DBusMessageIter array;
1084         unsigned char *ie;
1085         int ie_len;
1086
1087         dbus_message_iter_recurse(value, &array);
1088         dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
1089
1090         if (ie_len > 0)
1091                 result->has_wps = TRUE;
1092 }
1093
1094 static void extract_capabilites(DBusMessageIter *value,
1095                                         struct supplicant_result *result)
1096 {
1097         dbus_message_iter_get_basic(value, &result->capabilities);
1098
1099         if (result->capabilities & IEEE80211_CAP_ESS)
1100                 result->adhoc = FALSE;
1101         else if (result->capabilities & IEEE80211_CAP_IBSS)
1102                 result->adhoc = TRUE;
1103
1104         if (result->capabilities & IEEE80211_CAP_PRIVACY)
1105                 result->has_wep = TRUE;
1106 }
1107
1108 static unsigned char calculate_strength(struct supplicant_task *task,
1109                                         struct supplicant_result *result)
1110 {
1111         if (task->range->max_qual.qual == 0) {
1112                 unsigned char strength;
1113
1114                 if (result->level > 0)
1115                         strength = 100 - result->level;
1116                 else
1117                         strength = 120 + result->level;
1118
1119                 if (strength > 100)
1120                         strength = 100;
1121
1122                 return strength;
1123         }
1124
1125         return (result->quality * 100) / task->range->max_qual.qual;
1126 }
1127
1128 static unsigned short calculate_channel(struct supplicant_result *result)
1129 {
1130         if (result->frequency < 0)
1131                 return 0;
1132
1133         return (result->frequency - 2407) / 5;
1134 }
1135
1136 static void get_properties(struct supplicant_task *task);
1137
1138 static void properties_reply(DBusPendingCall *call, void *user_data)
1139 {
1140         struct supplicant_task *task = user_data;
1141         struct supplicant_result result;
1142         struct connman_network *network;
1143         DBusMessage *reply;
1144         DBusMessageIter array, dict;
1145         unsigned char strength;
1146         unsigned short channel, frequency;
1147         const char *mode, *security;
1148         char *group = NULL;
1149         unsigned int ssid_len;
1150
1151         DBG("task %p", task);
1152
1153         reply = dbus_pending_call_steal_reply(call);
1154         if (reply == NULL) {
1155                 get_properties(task);
1156                 return;
1157         }
1158
1159         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
1160                 dbus_message_unref(reply);
1161                 get_properties(task);
1162                 return;
1163         }
1164
1165         memset(&result, 0, sizeof(result));
1166         result.frequency = -1;
1167         result.quality = -1;
1168         result.level = 0;
1169         result.noise = 0;
1170
1171         dbus_message_iter_init(reply, &array);
1172
1173         dbus_message_iter_recurse(&array, &dict);
1174
1175         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
1176                 DBusMessageIter entry, value;
1177                 const char *key;
1178
1179                 dbus_message_iter_recurse(&dict, &entry);
1180                 dbus_message_iter_get_basic(&entry, &key);
1181
1182                 dbus_message_iter_next(&entry);
1183
1184                 dbus_message_iter_recurse(&entry, &value);
1185
1186                 //type = dbus_message_iter_get_arg_type(&value);
1187                 //dbus_message_iter_get_basic(&value, &val);
1188
1189                 /* 
1190                  * bssid        : a (97)
1191                  * ssid         : a (97)
1192                  * wpaie        : a (97)
1193                  * rsnie        : a (97)
1194                  * wpsie        : a (97)
1195                  * frequency    : i (105)
1196                  * capabilities : q (113)
1197                  * quality      : i (105)
1198                  * noise        : i (105)
1199                  * level        : i (105)
1200                  * maxrate      : i (105)
1201                  */
1202
1203                 if (g_str_equal(key, "bssid") == TRUE)
1204                         extract_addr(&value, &result);
1205                 else if (g_str_equal(key, "ssid") == TRUE)
1206                         extract_ssid(&value, &result);
1207                 else if (g_str_equal(key, "wpaie") == TRUE)
1208                         extract_wpaie(&value, &result);
1209                 else if (g_str_equal(key, "rsnie") == TRUE)
1210                         extract_rsnie(&value, &result);
1211                 else if (g_str_equal(key, "wpsie") == TRUE)
1212                         extract_wpsie(&value, &result);
1213                 else if (g_str_equal(key, "capabilities") == TRUE)
1214                         extract_capabilites(&value, &result);
1215                 else if (g_str_equal(key, "frequency") == TRUE)
1216                         dbus_message_iter_get_basic(&value, &result.frequency);
1217                 else if (g_str_equal(key, "quality") == TRUE)
1218                         dbus_message_iter_get_basic(&value, &result.quality);
1219                 else if (g_str_equal(key, "noise") == TRUE)
1220                         dbus_message_iter_get_basic(&value, &result.noise);
1221                 else if (g_str_equal(key, "level") == TRUE)
1222                         dbus_message_iter_get_basic(&value, &result.level);
1223                 else if (g_str_equal(key, "maxrate") == TRUE)
1224                         dbus_message_iter_get_basic(&value, &result.maxrate);
1225
1226                 dbus_message_iter_next(&dict);
1227         }
1228
1229         if (result.path == NULL)
1230                 goto done;
1231
1232         if (result.path[0] == '\0')
1233                 goto done;
1234
1235         if (result.frequency > 0 && result.frequency < 14)
1236                 result.frequency = 2407 + (5 * result.frequency);
1237         else if (result.frequency == 14)
1238                 result.frequency = 2484;
1239
1240         strength = calculate_strength(task, &result);
1241         channel  = calculate_channel(&result);
1242
1243         frequency = (result.frequency < 0) ? 0 : result.frequency;
1244
1245         if (result.has_rsn == TRUE)
1246                 security = "rsn";
1247         else if (result.has_wpa == TRUE)
1248                 security = "wpa";
1249         else if (result.has_wep == TRUE)
1250                 security = "wep";
1251         else
1252                 security = "none";
1253
1254         mode = (result.adhoc == TRUE) ? "adhoc" : "managed";
1255
1256         group = build_group(result.path, result.name,
1257                                         result.ssid, result.ssid_len,
1258                                                         mode, security);
1259
1260         network = connman_device_get_network(task->device, result.path);
1261         if (network == NULL) {
1262                 int index;
1263
1264                 network = connman_network_create(result.path,
1265                                                 CONNMAN_NETWORK_TYPE_WIFI);
1266                 if (network == NULL)
1267                         goto done;
1268
1269                 index = connman_device_get_index(task->device);
1270                 connman_network_set_index(network, index);
1271
1272                 connman_network_set_protocol(network,
1273                                                 CONNMAN_NETWORK_PROTOCOL_IP);
1274
1275                 connman_network_set_address(network, result.addr,
1276                                                         result.addr_len);
1277
1278                 if (connman_device_add_network(task->device, network) < 0) {
1279                         connman_network_unref(network);
1280                         goto done;
1281                 }
1282         }
1283
1284         if (result.name != NULL && result.name[0] != '\0')
1285                 connman_network_set_name(network, result.name);
1286
1287         if (connman_network_get_blob(network, "WiFi.SSID", &ssid_len) == NULL) {
1288                 connman_network_set_blob(network, "WiFi.SSID",
1289                                          result.ssid, result.ssid_len);
1290         }
1291
1292         connman_network_set_string(network, "WiFi.Mode", mode);
1293
1294         DBG("%s (%s %s) strength %d (%s)",
1295                                 result.name, mode, security, strength,
1296                                 (result.has_wps == TRUE) ? "WPS" : "no WPS");
1297
1298         connman_network_set_available(network, TRUE);
1299         connman_network_set_strength(network, strength);
1300
1301         connman_network_set_uint16(network, "Frequency", frequency);
1302         connman_network_set_uint16(network, "WiFi.Channel", channel);
1303         connman_network_set_string(network, "WiFi.Security", security);
1304
1305         if (result.ssid != NULL)
1306                 connman_network_set_group(network, group);
1307
1308 done:
1309         g_free(group);
1310
1311         g_free(result.path);
1312         g_free(result.addr);
1313         g_free(result.name);
1314         g_free(result.ssid);
1315
1316         dbus_message_unref(reply);
1317
1318         get_properties(task);
1319 }
1320
1321 static void get_properties(struct supplicant_task *task)
1322 {
1323         DBusMessage *message;
1324         DBusPendingCall *call;
1325         char *path;
1326
1327         path = g_slist_nth_data(task->scan_results, 0);
1328         if (path == NULL)
1329                 goto noscan;
1330
1331         message = dbus_message_new_method_call(SUPPLICANT_NAME, path,
1332                                                 SUPPLICANT_INTF ".BSSID",
1333                                                                 "properties");
1334
1335         task->scan_results = g_slist_remove(task->scan_results, path);
1336         g_free(path);
1337
1338         if (message == NULL)
1339                 goto noscan;
1340
1341         if (dbus_connection_send_with_reply(connection, message,
1342                                                 &call, TIMEOUT) == FALSE) {
1343                 connman_error("Failed to get network properties");
1344                 dbus_message_unref(message);
1345                 goto noscan;
1346         }
1347
1348         if (call == NULL) {
1349                 connman_error("D-Bus connection not available");
1350                 dbus_message_unref(message);
1351                 goto noscan;
1352         }
1353
1354         dbus_pending_call_set_notify(call, properties_reply, task, NULL);
1355
1356         dbus_message_unref(message);
1357
1358         return;
1359
1360 noscan:
1361         connman_device_set_scanning(task->device, FALSE);
1362 }
1363
1364 static void scan_results_reply(DBusPendingCall *call, void *user_data)
1365 {
1366         struct supplicant_task *task = user_data;
1367         DBusMessage *reply;
1368         DBusError error;
1369         char **results;
1370         int i, num_results;
1371
1372         DBG("task %p", task);
1373
1374         reply = dbus_pending_call_steal_reply(call);
1375         if (reply == NULL)
1376                 goto noscan;
1377
1378         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
1379                 goto done;
1380
1381         dbus_error_init(&error);
1382
1383         if (dbus_message_get_args(reply, &error,
1384                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
1385                                                 &results, &num_results,
1386                                                 DBUS_TYPE_INVALID) == FALSE) {
1387                 if (dbus_error_is_set(&error) == TRUE) {
1388                         connman_error("%s", error.message);
1389                         dbus_error_free(&error);
1390                 } else
1391                         connman_error("Wrong arguments for scan result");
1392                 goto done;
1393         }
1394
1395         if (num_results == 0)
1396                 goto done;
1397
1398         for (i = 0; i < num_results; i++) {
1399                 char *path = g_strdup(results[i]);
1400                 if (path == NULL)
1401                         continue;
1402
1403                 task->scan_results = g_slist_append(task->scan_results, path);
1404         }
1405
1406         g_strfreev(results);
1407
1408         dbus_message_unref(reply);
1409
1410         get_properties(task);
1411
1412         return;
1413
1414 done:
1415         dbus_message_unref(reply);
1416
1417 noscan:
1418         connman_device_set_scanning(task->device, FALSE);
1419 }
1420
1421 static void scan_results_available(struct supplicant_task *task)
1422 {
1423         DBusMessage *message;
1424         DBusPendingCall *call;
1425
1426         DBG("task %p", task);
1427
1428         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
1429                                                 SUPPLICANT_INTF ".Interface",
1430                                                         "scanResults");
1431         if (message == NULL)
1432                 return;
1433
1434         if (dbus_connection_send_with_reply(connection, message,
1435                                                 &call, TIMEOUT) == FALSE) {
1436                 connman_error("Failed to request scan result");
1437                 goto done;
1438         }
1439
1440         if (task->noscan == FALSE)
1441                 connman_device_set_scanning(task->device, TRUE);
1442
1443         if (call == NULL) {
1444                 connman_error("D-Bus connection not available");
1445                 goto done;
1446         }
1447
1448         dbus_pending_call_set_notify(call, scan_results_reply, task, NULL);
1449
1450 done:
1451         dbus_message_unref(message);
1452 }
1453
1454 static enum supplicant_state string2state(const char *state)
1455 {
1456         if (g_str_equal(state, "INACTIVE") == TRUE)
1457                 return WPA_INACTIVE;
1458         else if (g_str_equal(state, "SCANNING") == TRUE)
1459                 return WPA_SCANNING;
1460         else if (g_str_equal(state, "ASSOCIATING") == TRUE)
1461                 return WPA_ASSOCIATING;
1462         else if (g_str_equal(state, "ASSOCIATED") == TRUE)
1463                 return WPA_ASSOCIATED;
1464         else if (g_str_equal(state, "GROUP_HANDSHAKE") == TRUE)
1465                 return WPA_GROUP_HANDSHAKE;
1466         else if (g_str_equal(state, "4WAY_HANDSHAKE") == TRUE)
1467                 return WPA_4WAY_HANDSHAKE;
1468         else if (g_str_equal(state, "COMPLETED") == TRUE)
1469                 return WPA_COMPLETED;
1470         else if (g_str_equal(state, "DISCONNECTED") == TRUE)
1471                 return WPA_DISCONNECTED;
1472         else
1473                 return WPA_INVALID;
1474 }
1475
1476 static int task_connect(struct supplicant_task *task)
1477 {
1478         const char *address, *security, *passphrase;
1479         const void *ssid;
1480         unsigned int ssid_len;
1481
1482         address = connman_network_get_string(task->network, "Address");
1483         security = connman_network_get_string(task->network, "WiFi.Security");
1484         passphrase = connman_network_get_string(task->network, "WiFi.Passphrase");
1485
1486         ssid = connman_network_get_blob(task->network, "WiFi.SSID", &ssid_len);
1487
1488         DBG("address %s security %s passphrase %s",
1489                                         address, security, passphrase);
1490
1491         if (security == NULL && passphrase == NULL)
1492                 return -EINVAL;
1493
1494         if (g_str_equal(security, "none") == FALSE && passphrase == NULL)
1495                 return -EINVAL;
1496
1497         add_network(task);
1498
1499         select_network(task);
1500         disable_network(task);
1501
1502         set_network(task, ssid, ssid_len, address, security, passphrase);
1503
1504         enable_network(task);
1505
1506         return -EINPROGRESS;
1507 }
1508
1509 static void state_change(struct supplicant_task *task, DBusMessage *msg)
1510 {
1511         DBusError error;
1512         const char *newstate, *oldstate;
1513         enum supplicant_state state;
1514
1515         dbus_error_init(&error);
1516
1517         if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &newstate,
1518                                                 DBUS_TYPE_STRING, &oldstate,
1519                                                 DBUS_TYPE_INVALID) == FALSE) {
1520                 if (dbus_error_is_set(&error) == TRUE) {
1521                         connman_error("%s", error.message);
1522                         dbus_error_free(&error);
1523                 } else
1524                         connman_error("Wrong arguments for state change");
1525                 return;
1526         }
1527
1528         DBG("state %s ==> %s", oldstate, newstate);
1529
1530         state = string2state(newstate);
1531         if (state == WPA_INVALID)
1532                 return;
1533
1534         task->state = state;
1535
1536         switch (task->state) {
1537         case WPA_SCANNING:
1538                 task->noscan = TRUE;
1539                 connman_device_set_scanning(task->device, TRUE);
1540                 break;
1541         case WPA_ASSOCIATING:
1542         case WPA_ASSOCIATED:
1543         case WPA_4WAY_HANDSHAKE:
1544         case WPA_GROUP_HANDSHAKE:
1545                 task->noscan = TRUE;
1546                 break;
1547         case WPA_COMPLETED:
1548         case WPA_DISCONNECTED:
1549                 task->noscan = FALSE;
1550                 break;
1551         case WPA_INACTIVE:
1552                 task->noscan = FALSE;
1553                 connman_device_set_scanning(task->device, FALSE);
1554                 break;
1555         case WPA_INVALID:
1556                 break;
1557         }
1558
1559         if (task->network == NULL)
1560                 return;
1561
1562         switch (task->state) {
1563         case WPA_COMPLETED:
1564                 /* carrier on */
1565                 connman_network_set_connected(task->network, TRUE);
1566                 connman_device_set_scanning(task->device, FALSE);
1567                 break;
1568
1569         case WPA_DISCONNECTED:
1570                 disable_network(task);
1571
1572                 if (task->disconnecting == TRUE) {
1573                         connman_network_set_connected(task->network, FALSE);
1574                         connman_network_unref(task->network);
1575                         task->disconnecting = FALSE;
1576
1577                         if (task->pending_network != NULL) {
1578                                 task->network = task->pending_network;
1579                                 task->pending_network = NULL;
1580                                 task_connect(task);
1581                         }
1582                 } else {
1583                         /* carrier off */
1584                         connman_network_set_connected(task->network, FALSE);
1585                         connman_device_set_scanning(task->device, FALSE);
1586                 }
1587                 break;
1588
1589         case WPA_ASSOCIATING:
1590                 connman_network_set_associating(task->network, TRUE);
1591                 break;
1592
1593         case WPA_INACTIVE:
1594                 remove_network(task);
1595
1596                 if (task->disconnecting == TRUE) {
1597                         connman_network_set_connected(task->network, FALSE);
1598                         connman_network_unref(task->network);
1599                         task->disconnecting = FALSE;
1600
1601                         if (task->pending_network != NULL) {
1602                                 task->network = task->pending_network;
1603                                 task->pending_network = NULL;
1604                                 task_connect(task);
1605                         }
1606                 }
1607                 break;
1608
1609         default:
1610                 connman_network_set_associating(task->network, FALSE);
1611                 break;
1612         }
1613 }
1614
1615 static DBusHandlerResult supplicant_filter(DBusConnection *conn,
1616                                                 DBusMessage *msg, void *data)
1617 {
1618         struct supplicant_task *task;
1619         const char *member, *path;
1620
1621         if (dbus_message_has_interface(msg,
1622                                 SUPPLICANT_INTF ".Interface") == FALSE)
1623                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1624
1625         member = dbus_message_get_member(msg);
1626         if (member == NULL)
1627                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1628
1629         path = dbus_message_get_path(msg);
1630         if (path == NULL)
1631                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1632
1633         task = find_task_by_path(path);
1634         if (task == NULL)
1635                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1636
1637         DBG("task %p member %s", task, member);
1638
1639         if (g_str_equal(member, "ScanResultsAvailable") == TRUE)
1640                 scan_results_available(task);
1641         else if (g_str_equal(member, "StateChange") == TRUE)
1642                 state_change(task, msg);
1643
1644         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1645 }
1646
1647 int supplicant_start(struct connman_device *device)
1648 {
1649         struct supplicant_task *task;
1650         int err;
1651
1652         DBG("device %p", device);
1653
1654         task = g_try_new0(struct supplicant_task, 1);
1655         if (task == NULL)
1656                 return -ENOMEM;
1657
1658         task->ifindex = connman_device_get_index(device);
1659         task->ifname = connman_inet_ifname(task->ifindex);
1660
1661         if (task->ifname == NULL) {
1662                 err = -ENOMEM;
1663                 goto failed;
1664         }
1665
1666         task->range = g_try_malloc0(sizeof(struct iw_range));
1667         if (task->range == NULL) {
1668                 err = -ENOMEM;
1669                 goto failed;
1670         }
1671
1672         err = get_range(task);
1673         if (err < 0)
1674                 goto failed;
1675
1676         task->device = connman_device_ref(device);
1677
1678         task->created = FALSE;
1679         task->noscan = FALSE;
1680         task->state = WPA_INVALID;
1681         task->disconnecting = FALSE;
1682         task->pending_network = NULL;
1683
1684         task_list = g_slist_append(task_list, task);
1685
1686         return create_interface(task);
1687
1688 failed:
1689         g_free(task->range);
1690         g_free(task->ifname);
1691         g_free(task);
1692
1693         return err;
1694 }
1695
1696 int supplicant_stop(struct connman_device *device)
1697 {
1698         int index = connman_device_get_index(device);
1699         struct supplicant_task *task;
1700
1701         DBG("device %p", device);
1702
1703         task = find_task_by_index(index);
1704         if (task == NULL)
1705                 return -ENODEV;
1706
1707         g_free(task->range);
1708
1709         task_list = g_slist_remove(task_list, task);
1710
1711         disable_network(task);
1712
1713         remove_network(task);
1714
1715         return remove_interface(task);
1716 }
1717
1718 int supplicant_scan(struct connman_device *device)
1719 {
1720         int index = connman_device_get_index(device);
1721         struct supplicant_task *task;
1722         int err;
1723
1724         DBG("device %p", device);
1725
1726         task = find_task_by_index(index);
1727         if (task == NULL)
1728                 return -ENODEV;
1729
1730         switch (task->state) {
1731         case WPA_SCANNING:
1732                 return -EALREADY;
1733         case WPA_ASSOCIATING:
1734         case WPA_ASSOCIATED:
1735         case WPA_4WAY_HANDSHAKE:
1736         case WPA_GROUP_HANDSHAKE:
1737                 return -EBUSY;
1738         default:
1739                 break;
1740         }
1741
1742         err = initiate_scan(task);
1743
1744         return 0;
1745 }
1746
1747 int supplicant_connect(struct connman_network *network)
1748 {
1749         struct supplicant_task *task;
1750         int index;
1751
1752         DBG("network %p", network);
1753
1754         index = connman_network_get_index(network);
1755
1756         task = find_task_by_index(index);
1757         if (task == NULL)
1758                 return -ENODEV;
1759
1760         if (task->disconnecting == TRUE)
1761                 task->pending_network = connman_network_ref(network);
1762         else {
1763                 task->network = connman_network_ref(network);
1764                 return task_connect(task);
1765         }
1766
1767         return -EINPROGRESS;
1768 }
1769
1770 int supplicant_disconnect(struct connman_network *network)
1771 {
1772         struct supplicant_task *task;
1773         int index;
1774
1775         DBG("network %p", network);
1776
1777         index = connman_network_get_index(network);
1778
1779         task = find_task_by_index(index);
1780         if (task == NULL)
1781                 return -ENODEV;
1782
1783         if (task->disconnecting == TRUE)
1784                 return -EINPROGRESS;
1785
1786         disable_network(task);
1787
1788         remove_network(task);
1789
1790         task->disconnecting = TRUE;
1791
1792         return 0;
1793 }
1794
1795 static void supplicant_activate(DBusConnection *conn)
1796 {
1797         DBusMessage *message;
1798
1799         DBG("conn %p", conn);
1800
1801         message = dbus_message_new_method_call(SUPPLICANT_NAME, "/",
1802                                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect");
1803         if (message == NULL)
1804                 return;
1805
1806         dbus_message_set_no_reply(message, TRUE);
1807
1808         dbus_connection_send(conn, message, NULL);
1809
1810         dbus_message_unref(message);
1811 }
1812
1813 static GSList *driver_list = NULL;
1814
1815 static void supplicant_probe(DBusConnection *conn, void *user_data)
1816 {
1817         GSList *list;
1818
1819         DBG("conn %p", conn);
1820
1821         for (list = driver_list; list; list = list->next) {
1822                 struct supplicant_driver *driver = list->data;
1823
1824                 DBG("driver %p name %s", driver, driver->name);
1825
1826                 if (driver->probe)
1827                         driver->probe();
1828         }
1829 }
1830
1831 static void supplicant_remove(DBusConnection *conn, void *user_data)
1832 {
1833         GSList *list;
1834
1835         DBG("conn %p", conn);
1836
1837         for (list = driver_list; list; list = list->next) {
1838                 struct supplicant_driver *driver = list->data;
1839
1840                 DBG("driver %p name %s", driver, driver->name);
1841
1842                 if (driver->remove)
1843                         driver->remove();
1844         }
1845 }
1846
1847 static const char *supplicant_rule = "type=signal,"
1848                                 "interface=" SUPPLICANT_INTF ".Interface";
1849 static guint watch;
1850
1851 static int supplicant_create(void)
1852 {
1853         if (g_slist_length(driver_list) > 0)
1854                 return 0;
1855
1856         connection = connman_dbus_get_connection();
1857         if (connection == NULL)
1858                 return -EIO;
1859
1860         DBG("connection %p", connection);
1861
1862         if (dbus_connection_add_filter(connection,
1863                                 supplicant_filter, NULL, NULL) == FALSE) {
1864                 connection = connman_dbus_get_connection();
1865                 return -EIO;
1866         }
1867
1868         dbus_bus_add_match(connection, supplicant_rule, NULL);
1869         dbus_connection_flush(connection);
1870
1871         watch = g_dbus_add_service_watch(connection, SUPPLICANT_NAME,
1872                         supplicant_probe, supplicant_remove, NULL, NULL);
1873
1874         return 0;
1875 }
1876
1877 static void supplicant_destroy(void)
1878 {
1879         if (g_slist_length(driver_list) > 0)
1880                 return;
1881
1882         DBG("connection %p", connection);
1883
1884         if (watch > 0)
1885                 g_dbus_remove_watch(connection, watch);
1886
1887         dbus_bus_remove_match(connection, supplicant_rule, NULL);
1888         dbus_connection_flush(connection);
1889
1890         dbus_connection_remove_filter(connection, supplicant_filter, NULL);
1891
1892         dbus_connection_unref(connection);
1893         connection = NULL;
1894 }
1895
1896 int supplicant_register(struct supplicant_driver *driver)
1897 {
1898         int err;
1899
1900         DBG("driver %p name %s", driver, driver->name);
1901
1902         err = supplicant_create();
1903         if (err < 0)
1904                 return err;
1905
1906         driver_list = g_slist_append(driver_list, driver);
1907
1908         if (g_dbus_check_service(connection, SUPPLICANT_NAME) == TRUE)
1909                 supplicant_probe(connection, NULL);
1910         else
1911                 supplicant_activate(connection);
1912
1913         return 0;
1914 }
1915
1916 void supplicant_unregister(struct supplicant_driver *driver)
1917 {
1918         DBG("driver %p name %s", driver, driver->name);
1919
1920         supplicant_remove(connection, NULL);
1921
1922         driver_list = g_slist_remove(driver_list, driver);
1923
1924         supplicant_destroy();
1925 }