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