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