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