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