Add callbacks to supplicant driver
[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 <stdlib.h>
27 #include <string.h>
28
29 #include <gdbus.h>
30
31 #define CONNMAN_API_SUBJECT_TO_CHANGE
32 #include <connman/log.h>
33 #include <connman/dbus.h>
34
35 #include "inet.h"
36 #include "supplicant.h"
37
38 #define TIMEOUT 5000
39
40 #define IEEE80211_CAP_ESS       0x0001
41 #define IEEE80211_CAP_IBSS      0x0002
42 #define IEEE80211_CAP_PRIVACY   0x0010
43
44 static GSList *driver_list = NULL;
45
46 static void process_state_change(struct connman_device *device,
47                                                 enum supplicant_state state)
48 {
49         GSList *list;
50
51         for (list = driver_list; list; list = list->next) {
52                 struct supplicant_driver *driver = list->data;
53
54                 if (driver->state_change)
55                         driver->state_change(device, state);
56         }
57 }
58
59 static void process_clear_results(struct connman_device *device)
60 {
61         GSList *list;
62
63         for (list = driver_list; list; list = list->next) {
64                 struct supplicant_driver *driver = list->data;
65
66                 if (driver->clear_results)
67                         driver->clear_results(device);
68         }
69 }
70
71 static void process_scan_result(struct connman_device *device,
72                                         struct supplicant_network *network)
73 {
74         GSList *list;
75
76         for (list = driver_list; list; list = list->next) {
77                 struct supplicant_driver *driver = list->data;
78
79                 if (driver->scan_result)
80                         driver->scan_result(device, network);
81         }
82 }
83
84 struct supplicant_task {
85         int ifindex;
86         gchar *ifname;
87         struct connman_device *device;
88         gchar *path;
89         gboolean created;
90         gchar *network;
91         enum supplicant_state state;
92 };
93
94 static GSList *task_list = NULL;
95
96 static DBusConnection *connection;
97
98 static struct supplicant_task *find_task_by_index(int index)
99 {
100         GSList *list;
101
102         for (list = task_list; list; list = list->next) {
103                 struct supplicant_task *task = list->data;
104
105                 if (task->ifindex == index)
106                         return task;
107         }
108
109         return NULL;
110 }
111
112 static struct supplicant_task *find_task_by_path(const char *path)
113 {
114         GSList *list;
115
116         for (list = task_list; list; list = list->next) {
117                 struct supplicant_task *task = list->data;
118
119                 if (g_str_equal(task->path, path) == TRUE)
120                         return task;
121         }
122
123         return NULL;
124 }
125
126 static int get_interface(struct supplicant_task *task)
127 {
128         DBusMessage *message, *reply;
129         DBusError error;
130         const char *path;
131
132         DBG("task %p", task);
133
134         message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
135                                         SUPPLICANT_INTF, "getInterface");
136         if (message == NULL)
137                 return -ENOMEM;
138
139         dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname,
140                                                         DBUS_TYPE_INVALID);
141
142         dbus_error_init(&error);
143
144         reply = dbus_connection_send_with_reply_and_block(connection,
145                                                         message, -1, &error);
146         if (reply == NULL) {
147                 if (dbus_error_is_set(&error) == TRUE) {
148                         connman_error("%s", error.message);
149                         dbus_error_free(&error);
150                 } else
151                         connman_error("Failed to get interface");
152                 dbus_message_unref(message);
153                 return -EIO;
154         }
155
156         dbus_message_unref(message);
157
158         dbus_error_init(&error);
159
160         if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
161                                                 DBUS_TYPE_INVALID) == FALSE) {
162                 if (dbus_error_is_set(&error) == TRUE) {
163                         connman_error("%s", error.message);
164                         dbus_error_free(&error);
165                 } else
166                         connman_error("Wrong arguments for interface");
167                 dbus_message_unref(reply);
168                 return -EIO;
169         }
170
171         DBG("path %s", path);
172
173         task->path = g_strdup(path);
174         task->created = FALSE;
175
176         dbus_message_unref(reply);
177
178         return 0;
179 }
180
181 static int add_interface(struct supplicant_task *task)
182 {
183         DBusMessage *message, *reply;
184         DBusError error;
185         const char *path;
186
187         DBG("task %p", task);
188
189         message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
190                                         SUPPLICANT_INTF, "addInterface");
191         if (message == NULL)
192                 return -ENOMEM;
193
194         dbus_error_init(&error);
195
196         dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname,
197                                                         DBUS_TYPE_INVALID);
198
199         reply = dbus_connection_send_with_reply_and_block(connection,
200                                                         message, -1, &error);
201         if (reply == NULL) {
202                 if (dbus_error_is_set(&error) == TRUE) {
203                         connman_error("%s", error.message);
204                         dbus_error_free(&error);
205                 } else
206                         connman_error("Failed to add interface");
207                 dbus_message_unref(message);
208                 return -EIO;
209         }
210
211         dbus_message_unref(message);
212
213         dbus_error_init(&error);
214
215         if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
216                                                 DBUS_TYPE_INVALID) == FALSE) {
217                 if (dbus_error_is_set(&error) == TRUE) {
218                         connman_error("%s", error.message);
219                         dbus_error_free(&error);
220                 } else
221                         connman_error("Wrong arguments for interface");
222                 dbus_message_unref(reply);
223                 return -EIO;
224         }
225
226         DBG("path %s", path);
227
228         task->path = g_strdup(path);
229         task->created = TRUE;
230
231         dbus_message_unref(reply);
232
233         return 0;
234 }
235
236 static int remove_interface(struct supplicant_task *task)
237 {
238         DBusMessage *message, *reply;
239         DBusError error;
240
241         DBG("task %p", task);
242
243         if (task->created == FALSE)
244                 return -EINVAL;
245
246         message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
247                                         SUPPLICANT_INTF, "removeInterface");
248         if (message == NULL)
249                 return -ENOMEM;
250
251         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->path,
252                                                         DBUS_TYPE_INVALID);
253
254         dbus_error_init(&error);
255
256         reply = dbus_connection_send_with_reply_and_block(connection,
257                                                         message, -1, &error);
258         if (reply == NULL) {
259                 if (dbus_error_is_set(&error) == TRUE) {
260                         connman_error("%s", error.message);
261                         dbus_error_free(&error);
262                 } else
263                         connman_error("Failed to remove interface");
264                 dbus_message_unref(message);
265                 return -EIO;
266         }
267
268         dbus_message_unref(message);
269
270         dbus_message_unref(reply);
271
272         return 0;
273 }
274
275 static int set_ap_scan(struct supplicant_task *task)
276 {
277         DBusMessage *message, *reply;
278         DBusError error;
279         guint32 ap_scan = 1;
280
281         DBG("task %p", task);
282
283         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
284                                 SUPPLICANT_INTF ".Interface", "setAPScan");
285         if (message == NULL)
286                 return -ENOMEM;
287
288         dbus_message_append_args(message, DBUS_TYPE_UINT32, &ap_scan,
289                                                         DBUS_TYPE_INVALID);
290
291         dbus_error_init(&error);
292
293         reply = dbus_connection_send_with_reply_and_block(connection,
294                                                         message, -1, &error);
295         if (reply == NULL) {
296                 if (dbus_error_is_set(&error) == TRUE) {
297                         connman_error("%s", error.message);
298                         dbus_error_free(&error);
299                 } else
300                         connman_error("Failed to set AP scan");
301                 dbus_message_unref(message);
302                 return -EIO;
303         }
304
305         dbus_message_unref(message);
306
307         dbus_message_unref(reply);
308
309         return 0;
310 }
311
312 static int add_network(struct supplicant_task *task)
313 {
314         DBusMessage *message, *reply;
315         DBusError error;
316         const char *path;
317
318         DBG("task %p", task);
319
320         if (task->network != NULL)
321                 return -EALREADY;
322
323         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
324                                 SUPPLICANT_INTF ".Interface", "addNetwork");
325         if (message == NULL)
326                 return -ENOMEM;
327
328         dbus_error_init(&error);
329
330         reply = dbus_connection_send_with_reply_and_block(connection,
331                                                         message, -1, &error);
332         if (reply == NULL) {
333                 if (dbus_error_is_set(&error) == TRUE) {
334                         connman_error("%s", error.message);
335                         dbus_error_free(&error);
336                 } else
337                         connman_error("Failed to add network");
338                 dbus_message_unref(message);
339                 return -EIO;
340         }
341
342         dbus_message_unref(message);
343
344         dbus_error_init(&error);
345
346         if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
347                                                 DBUS_TYPE_INVALID) == FALSE) {
348                 if (dbus_error_is_set(&error) == TRUE) {
349                         connman_error("%s", error.message);
350                         dbus_error_free(&error);
351                 } else
352                         connman_error("Wrong arguments for network");
353                 dbus_message_unref(reply);
354                 return -EIO;
355         }
356
357         DBG("path %s", path);
358
359         task->network = g_strdup(path);
360
361         dbus_message_unref(reply);
362
363         return 0;
364 }
365
366 static int remove_network(struct supplicant_task *task)
367 {
368         DBusMessage *message, *reply;
369         DBusError error;
370
371         DBG("task %p", task);
372
373         if (task->network == NULL)
374                 return -EINVAL;
375
376         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
377                                 SUPPLICANT_INTF ".Interface", "removeNetwork");
378         if (message == NULL)
379                 return -ENOMEM;
380
381         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->network,
382                                                         DBUS_TYPE_INVALID);
383
384         dbus_error_init(&error);
385
386         reply = dbus_connection_send_with_reply_and_block(connection,
387                                                         message, -1, &error);
388         if (reply == NULL) {
389                 if (dbus_error_is_set(&error) == TRUE) {
390                         connman_error("%s", error.message);
391                         dbus_error_free(&error);
392                 } else
393                         connman_error("Failed to remove network");
394                 dbus_message_unref(message);
395                 return -EIO;
396         }
397
398         dbus_message_unref(message);
399
400         dbus_message_unref(reply);
401
402         g_free(task->network);
403         task->network = NULL;
404
405         return 0;
406 }
407
408 static int select_network(struct supplicant_task *task)
409 {
410         DBusMessage *message, *reply;
411         DBusError error;
412
413         DBG("task %p", task);
414
415         if (task->network == NULL)
416                 return -EINVAL;
417
418         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
419                                 SUPPLICANT_INTF ".Interface", "selectNetwork");
420         if (message == NULL)
421                 return -ENOMEM;
422
423         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->network,
424                                                         DBUS_TYPE_INVALID);
425
426         dbus_error_init(&error);
427
428         reply = dbus_connection_send_with_reply_and_block(connection,
429                                                         message, -1, &error);
430         if (reply == NULL) {
431                 if (dbus_error_is_set(&error) == TRUE) {
432                         connman_error("%s", error.message);
433                         dbus_error_free(&error);
434                 } else
435                         connman_error("Failed to select network");
436                 dbus_message_unref(message);
437                 return -EIO;
438         }
439
440         dbus_message_unref(message);
441
442         dbus_message_unref(reply);
443
444         return 0;
445 }
446
447 static int enable_network(struct supplicant_task *task)
448 {
449         DBusMessage *message, *reply;
450         DBusError error;
451
452         DBG("task %p", task);
453
454         if (task->network == NULL)
455                 return -EINVAL;
456
457         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network,
458                                         SUPPLICANT_INTF ".Network", "enable");
459         if (message == NULL)
460                 return -ENOMEM;
461
462         dbus_error_init(&error);
463
464         reply = dbus_connection_send_with_reply_and_block(connection,
465                                                         message, -1, &error);
466         if (reply == NULL) {
467                 if (dbus_error_is_set(&error) == TRUE) {
468                         connman_error("%s", error.message);
469                         dbus_error_free(&error);
470                 } else
471                         connman_error("Failed to enable network");
472                 dbus_message_unref(message);
473                 return -EIO;
474         }
475
476         dbus_message_unref(message);
477
478         dbus_message_unref(reply);
479
480         return 0;
481 }
482
483 static int disable_network(struct supplicant_task *task)
484 {
485         DBusMessage *message, *reply;
486         DBusError error;
487
488         DBG("task %p", task);
489
490         if (task->network == NULL)
491                 return -EINVAL;
492
493         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network,
494                                         SUPPLICANT_INTF ".Network", "disable");
495         if (message == NULL)
496                 return -ENOMEM;
497
498         dbus_error_init(&error);
499
500         reply = dbus_connection_send_with_reply_and_block(connection,
501                                                         message, -1, &error);
502         if (reply == NULL) {
503                 if (dbus_error_is_set(&error) == TRUE) {
504                         connman_error("%s", error.message);
505                         dbus_error_free(&error);
506                 } else
507                         connman_error("Failed to disable network");
508                 dbus_message_unref(message);
509                 return -EIO;
510         }
511
512         dbus_message_unref(message);
513
514         dbus_message_unref(reply);
515
516         return 0;
517 }
518
519 static int set_network(struct supplicant_task *task,
520                                 const unsigned char *network, int len,
521                                 const char *security, const char *passphrase)
522 {
523         DBusMessage *message, *reply;
524         DBusMessageIter array, dict;
525         DBusError error;
526
527         DBG("task %p", task);
528
529         if (task->network == NULL)
530                 return -EINVAL;
531
532         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network,
533                                         SUPPLICANT_INTF ".Network", "set");
534         if (message == NULL)
535                 return -ENOMEM;
536
537         dbus_message_iter_init_append(message, &array);
538
539         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
540                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
541                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
542                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
543
544         connman_dbus_dict_append_array(&dict, "ssid",
545                                         DBUS_TYPE_BYTE, &network, len);
546
547         if (g_ascii_strcasecmp(security, "wpa") == 0 ||
548                                 g_ascii_strcasecmp(security, "wpa2") == 0) {
549                 const char *key_mgmt = "WPA-PSK";
550                 connman_dbus_dict_append_variant(&dict, "key_mgmt",
551                                                 DBUS_TYPE_STRING, &key_mgmt);
552
553                 if (passphrase && strlen(passphrase) > 0)
554                         connman_dbus_dict_append_variant(&dict, "psk",
555                                                 DBUS_TYPE_STRING, &passphrase);
556         } else if (g_ascii_strcasecmp(security, "wep") == 0) {
557                 const char *key_mgmt = "NONE", *index = "0";
558                 connman_dbus_dict_append_variant(&dict, "key_mgmt",
559                                                 DBUS_TYPE_STRING, &key_mgmt);
560
561                 if (passphrase) {
562                         int size = strlen(passphrase);
563                         if (size == 10 || size == 26) {
564                                 unsigned char *key = malloc(13);
565                                 char tmp[3];
566                                 int i;
567                                 memset(tmp, 0, sizeof(tmp));
568                                 if (key == NULL)
569                                         size = 0;
570                                 for (i = 0; i < size / 2; i++) {
571                                         memcpy(tmp, passphrase + (i * 2), 2);
572                                         key[i] = (unsigned char) strtol(tmp,
573                                                                 NULL, 16);
574                                 }
575                                 connman_dbus_dict_append_array(&dict,
576                                                 "wep_key0", DBUS_TYPE_BYTE,
577                                                         &key, size / 2);
578                                 free(key);
579                         } else
580                                 connman_dbus_dict_append_variant(&dict,
581                                                 "wep_key0", DBUS_TYPE_STRING,
582                                                                 &passphrase);
583                         connman_dbus_dict_append_variant(&dict, "wep_tx_keyidx",
584                                                 DBUS_TYPE_STRING, &index);
585                 }
586         } else {
587                 const char *key_mgmt = "NONE";
588                 connman_dbus_dict_append_variant(&dict, "key_mgmt",
589                                                 DBUS_TYPE_STRING, &key_mgmt);
590         }
591
592         dbus_message_iter_close_container(&array, &dict);
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 set network options");
604                 dbus_message_unref(message);
605                 return -EIO;
606         }
607
608         dbus_message_unref(message);
609
610         dbus_message_unref(reply);
611
612         return 0;
613 }
614
615 static int initiate_scan(struct supplicant_task *task)
616 {
617         DBusMessage *message;
618         DBusPendingCall *call;
619
620         DBG("task %p", task);
621
622         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
623                                         SUPPLICANT_INTF ".Interface", "scan");
624         if (message == NULL)
625                 return -ENOMEM;
626
627         if (dbus_connection_send_with_reply(connection, message,
628                                                 &call, TIMEOUT) == FALSE) {
629                 connman_error("Failed to initiate scan");
630                 dbus_message_unref(message);
631                 return -EIO;
632         }
633
634         dbus_message_unref(message);
635
636         return 0;
637 }
638
639 static void extract_ssid(struct supplicant_network *network,
640                                                 DBusMessageIter *value)
641 {
642         DBusMessageIter array;
643         unsigned char *ssid;
644         int ssid_len;
645
646         dbus_message_iter_recurse(value, &array);
647         dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
648
649         if (ssid_len < 1)
650                 return;
651
652         network->ssid = g_try_malloc(ssid_len);
653         if (network->ssid == NULL)
654                 return;
655
656         memcpy(network->ssid, ssid, ssid_len);
657         network->ssid_len = ssid_len;
658
659         network->identifier = g_try_malloc0(ssid_len + 1);
660         if (network->identifier == NULL)
661                 return;
662
663         memcpy(network->identifier, ssid, ssid_len);
664 }
665
666 static void extract_wpaie(struct supplicant_network *network,
667                                                 DBusMessageIter *value)
668 {
669         DBusMessageIter array;
670         unsigned char *ie;
671         int ie_len;
672
673         dbus_message_iter_recurse(value, &array);
674         dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
675
676         if (ie_len > 0)
677                 network->has_wpa = TRUE;
678 }
679
680 static void extract_rsnie(struct supplicant_network *network,
681                                                 DBusMessageIter *value)
682 {
683         DBusMessageIter array;
684         unsigned char *ie;
685         int ie_len;
686
687         dbus_message_iter_recurse(value, &array);
688         dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
689
690         if (ie_len > 0)
691                 network->has_rsn = TRUE;
692 }
693
694 static void extract_capabilites(struct supplicant_network *network,
695                                                 DBusMessageIter *value)
696 {
697         dbus_message_iter_get_basic(value, &network->capabilities);
698
699         if (network->capabilities & IEEE80211_CAP_ESS)
700                 network->adhoc = FALSE;
701         else if (network->capabilities & IEEE80211_CAP_IBSS)
702                 network->adhoc = TRUE;
703
704         if (network->capabilities & IEEE80211_CAP_PRIVACY)
705                 network->has_wep = TRUE;
706 }
707
708 static void properties_reply(DBusPendingCall *call, void *user_data)
709 {
710         struct supplicant_task *task = user_data;
711         struct supplicant_network *network;
712         DBusMessage *reply;
713         DBusMessageIter array, dict;
714
715         DBG("task %p", task);
716
717         reply = dbus_pending_call_steal_reply(call);
718
719         network = g_try_new0(struct supplicant_network, 1);
720         if (network == NULL)
721                 goto done;
722
723         dbus_message_iter_init(reply, &array);
724
725         dbus_message_iter_recurse(&array, &dict);
726
727         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
728                 DBusMessageIter entry, value;
729                 const char *key;
730
731                 dbus_message_iter_recurse(&dict, &entry);
732                 dbus_message_iter_get_basic(&entry, &key);
733
734                 dbus_message_iter_next(&entry);
735
736                 dbus_message_iter_recurse(&entry, &value);
737
738                 //type = dbus_message_iter_get_arg_type(&value);
739                 //dbus_message_iter_get_basic(&value, &val);
740
741                 /* 
742                  * bssid        : a (97)
743                  * ssid         : a (97)
744                  * wpaie        : a (97)
745                  * rsnie        : a (97)
746                  * frequency    : i (105)
747                  * capabilities : q (113)
748                  * quality      : i (105)
749                  * noise        : i (105)
750                  * level        : i (105)
751                  * maxrate      : i (105)
752                  */
753
754                 if (g_str_equal(key, "ssid") == TRUE)
755                         extract_ssid(network, &value);
756                 else if (g_str_equal(key, "wpaie") == TRUE)
757                         extract_wpaie(network, &value);
758                 else if (g_str_equal(key, "rsnie") == TRUE)
759                         extract_rsnie(network, &value);
760                 else if (g_str_equal(key, "capabilities") == TRUE)
761                         extract_capabilites(network, &value);
762                 else if (g_str_equal(key, "quality") == TRUE)
763                         dbus_message_iter_get_basic(&value, &network->quality);
764                 else if (g_str_equal(key, "noise") == TRUE)
765                         dbus_message_iter_get_basic(&value, &network->noise);
766                 else if (g_str_equal(key, "level") == TRUE)
767                         dbus_message_iter_get_basic(&value, &network->level);
768                 else if (g_str_equal(key, "maxrate") == TRUE)
769                         dbus_message_iter_get_basic(&value, &network->maxrate);
770
771
772                 dbus_message_iter_next(&dict);
773         }
774
775         process_scan_result(task->device, network);
776
777         g_free(network->identifier);
778         g_free(network->ssid);
779         g_free(network);
780
781 done:
782         dbus_message_unref(reply);
783 }
784
785 static int get_network_properties(struct supplicant_task *task,
786                                                         const char *path)
787 {
788         DBusMessage *message;
789         DBusPendingCall *call;
790
791         message = dbus_message_new_method_call(SUPPLICANT_NAME, path,
792                                                 SUPPLICANT_INTF ".BSSID",
793                                                                 "properties");
794         if (message == NULL)
795                 return -ENOMEM;
796
797         if (dbus_connection_send_with_reply(connection, message,
798                                                 &call, TIMEOUT) == FALSE) {
799                 connman_error("Failed to get network properties");
800                 dbus_message_unref(message);
801                 return -EIO;
802         }
803
804         dbus_pending_call_set_notify(call, properties_reply, task, NULL);
805
806         dbus_message_unref(message);
807
808         return 0;
809 }
810
811 static void scan_results_reply(DBusPendingCall *call, void *user_data)
812 {
813         struct supplicant_task *task = user_data;
814         DBusMessage *reply;
815         DBusError error;
816         char **results;
817         int i, num_results;
818
819         DBG("task %p", task);
820
821         reply = dbus_pending_call_steal_reply(call);
822
823         dbus_error_init(&error);
824
825         if (dbus_message_get_args(reply, &error,
826                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
827                                                 &results, &num_results,
828                                                 DBUS_TYPE_INVALID) == FALSE) {
829                 if (dbus_error_is_set(&error) == TRUE) {
830                         connman_error("%s", error.message);
831                         dbus_error_free(&error);
832                 } else
833                         connman_error("Wrong arguments for scan result");
834                 goto done;
835         }
836
837         process_clear_results(task->device);
838
839         for (i = 0; i < num_results; i++)
840                 get_network_properties(task, results[i]);
841
842         g_strfreev(results);
843
844 done:
845         dbus_message_unref(reply);
846 }
847
848 static int scan_results_available(struct supplicant_task *task)
849 {
850         DBusMessage *message;
851         DBusPendingCall *call;
852
853         DBG("task %p", task);
854
855         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
856                                                 SUPPLICANT_INTF ".Interface",
857                                                         "scanResults");
858         if (message == NULL)
859                 return -ENOMEM;
860
861         if (dbus_connection_send_with_reply(connection, message,
862                                                 &call, TIMEOUT) == FALSE) {
863                 connman_error("Failed to request scan result");
864                 dbus_message_unref(message);
865                 return -EIO;
866         }
867
868         dbus_pending_call_set_notify(call, scan_results_reply, task, NULL);
869
870         dbus_message_unref(message);
871
872         return 0;
873 }
874
875 static void state_change(struct supplicant_task *task, DBusMessage *msg)
876 {
877         DBusError error;
878         const char *state, *previous;
879
880         dbus_error_init(&error);
881
882         if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &state,
883                                                 DBUS_TYPE_STRING, &previous,
884                                                 DBUS_TYPE_INVALID) == FALSE) {
885                 if (dbus_error_is_set(&error) == TRUE) {
886                         connman_error("%s", error.message);
887                         dbus_error_free(&error);
888                 } else
889                         connman_error("Wrong arguments for state change");
890                 return;
891         }
892
893         DBG("state %s ==> %s", previous, state);
894
895         if (g_str_equal(state, "INACTIVE") == TRUE)
896                 task->state = STATE_INACTIVE;
897         else if (g_str_equal(state, "SCANNING") == TRUE)
898                 task->state = STATE_SCANNING;
899         else if (g_str_equal(state, "ASSOCIATING") == TRUE)
900                 task->state = STATE_ASSOCIATING;
901         else if (g_str_equal(state, "ASSOCIATED") == TRUE)
902                 task->state = STATE_ASSOCIATED;
903         else if (g_str_equal(state, "GROUP_HANDSHAKE") == TRUE)
904                 task->state = STATE_4WAY_HANDSHAKE;
905         else if (g_str_equal(state, "4WAY_HANDSHAKE") == TRUE)
906                 task->state = STATE_4WAY_HANDSHAKE;
907         else if (g_str_equal(state, "COMPLETED") == TRUE)
908                 task->state = STATE_COMPLETED;
909         else if (g_str_equal(state, "DISCONNECTED") == TRUE)
910                 task->state = STATE_DISCONNECTED;
911
912         process_state_change(task->device, task->state);
913
914         switch (task->state) {
915         case STATE_COMPLETED:
916                 /* carrier on */
917                 break;
918         case STATE_DISCONNECTED:
919                 /* carrier off */
920                 break;
921         default:
922                 break;
923         }
924 }
925
926 static DBusHandlerResult supplicant_filter(DBusConnection *conn,
927                                                 DBusMessage *msg, void *data)
928 {
929         struct supplicant_task *task;
930         const char *member, *path;
931
932         if (dbus_message_has_interface(msg,
933                                 SUPPLICANT_INTF ".Interface") == FALSE)
934                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
935
936         member = dbus_message_get_member(msg);
937         if (member == NULL)
938                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
939
940         path = dbus_message_get_path(msg);
941         if (path == NULL)
942                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
943
944         task = find_task_by_path(path);
945         if (task == NULL)
946                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
947
948         DBG("task %p member %s", task, member);
949
950         if (g_str_equal(member, "ScanResultsAvailable") == TRUE)
951                 scan_results_available(task);
952         else if (g_str_equal(member, "StateChange") == TRUE)
953                 state_change(task, msg);
954
955         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
956 }
957
958 int supplicant_start(struct connman_device *device)
959 {
960         struct supplicant_task *task;
961         int err;
962
963         DBG("device %p", device);
964
965         task = g_try_new0(struct supplicant_task, 1);
966         if (task == NULL)
967                 return -ENOMEM;
968
969         task->ifindex = connman_device_get_index(device);
970         task->ifname = inet_index2name(task->ifindex);
971         task->device = device;
972
973         if (task->ifname == NULL) {
974                 g_free(task);
975                 return -ENOMEM;
976         }
977
978         task->created = FALSE;
979         task->state = STATE_INACTIVE;
980
981         task_list = g_slist_append(task_list, task);
982
983         err = get_interface(task);
984         if (err < 0) {
985                 err = add_interface(task);
986                 if (err < 0) {
987                         g_free(task);
988                         return err;
989                 }
990         }
991
992         set_ap_scan(task);
993
994         return 0;
995 }
996
997 int supplicant_stop(struct connman_device *device)
998 {
999         int index = connman_device_get_index(device);
1000         struct supplicant_task *task;
1001
1002         DBG("device %p", device);
1003
1004         task = find_task_by_index(index);
1005         if (task == NULL)
1006                 return -ENODEV;
1007
1008         task_list = g_slist_remove(task_list, task);
1009
1010         disable_network(task);
1011
1012         remove_network(task);
1013
1014         remove_interface(task);
1015
1016         g_free(task->ifname);
1017         g_free(task->path);
1018         g_free(task);
1019
1020         return 0;
1021 }
1022
1023 int supplicant_scan(struct connman_device *device)
1024 {
1025         int index = connman_device_get_index(device);
1026         struct supplicant_task *task;
1027         int err;
1028
1029         DBG("device %p", device);
1030
1031         task = find_task_by_index(index);
1032         if (task == NULL)
1033                 return -ENODEV;
1034
1035         switch (task->state) {
1036         case STATE_SCANNING:
1037                 return -EALREADY;
1038         case STATE_ASSOCIATING:
1039         case STATE_ASSOCIATED:
1040         case STATE_4WAY_HANDSHAKE:
1041         case STATE_GROUP_HANDSHAKE:
1042                 return -EBUSY;
1043         default:
1044                 break;
1045         }
1046
1047         err = initiate_scan(task);
1048
1049         return 0;
1050 }
1051
1052 int __supplicant_connect(struct connman_element *element,
1053                                 const unsigned char *ssid, int ssid_len,
1054                                 const char *security, const char *passphrase)
1055 {
1056         struct supplicant_task *task;
1057
1058         DBG("element %p", element);
1059
1060         task = find_task_by_index(element->index);
1061         if (task == NULL)
1062                 return -ENODEV;
1063
1064         add_network(task);
1065
1066         select_network(task);
1067         disable_network(task);
1068
1069         set_network(task, ssid, ssid_len, security, passphrase);
1070
1071         enable_network(task);
1072
1073         return 0;
1074 }
1075
1076 int __supplicant_disconnect(struct connman_element *element)
1077 {
1078         struct supplicant_task *task;
1079
1080         DBG("element %p", element);
1081
1082         task = find_task_by_index(element->index);
1083         if (task == NULL)
1084                 return -ENODEV;
1085
1086         disable_network(task);
1087
1088         remove_network(task);
1089
1090         return 0;
1091 }
1092
1093 static void supplicant_activate(DBusConnection *conn)
1094 {
1095         DBusMessage *message;
1096
1097         DBG("conn %p", conn);
1098
1099         message = dbus_message_new_method_call(SUPPLICANT_NAME, "/",
1100                                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect");
1101         if (message == NULL)
1102                 return;
1103
1104         dbus_message_set_no_reply(message, TRUE);
1105
1106         dbus_connection_send(conn, message, NULL);
1107
1108         dbus_message_unref(message);
1109 }
1110
1111 static void supplicant_probe(DBusConnection *conn, void *user_data)
1112 {
1113         GSList *list;
1114
1115         DBG("conn %p", conn);
1116
1117         for (list = driver_list; list; list = list->next) {
1118                 struct supplicant_driver *driver = list->data;
1119
1120                 DBG("driver %p name %s", driver, driver->name);
1121
1122                 if (driver->probe)
1123                         driver->probe();
1124         }
1125 }
1126
1127 static void supplicant_remove(DBusConnection *conn, void *user_data)
1128 {
1129         GSList *list;
1130
1131         DBG("conn %p", conn);
1132
1133         for (list = driver_list; list; list = list->next) {
1134                 struct supplicant_driver *driver = list->data;
1135
1136                 DBG("driver %p name %s", driver, driver->name);
1137
1138                 if (driver->remove)
1139                         driver->remove();
1140         }
1141 }
1142
1143 static const char *supplicant_rule = "type=signal,"
1144                                 "interface=" SUPPLICANT_INTF ".Interface";
1145 static guint watch;
1146
1147 static int supplicant_create(void)
1148 {
1149         if (g_slist_length(driver_list) > 0)
1150                 return 0;
1151
1152         connection = connman_dbus_get_connection();
1153         if (connection == NULL)
1154                 return -EIO;
1155
1156         DBG("connection %p", connection);
1157
1158         if (dbus_connection_add_filter(connection,
1159                                 supplicant_filter, NULL, NULL) == FALSE) {
1160                 connection = connman_dbus_get_connection();
1161                 return -EIO;
1162         }
1163
1164         dbus_bus_add_match(connection, supplicant_rule, NULL);
1165         dbus_connection_flush(connection);
1166
1167         watch = g_dbus_add_service_watch(connection, SUPPLICANT_NAME,
1168                         supplicant_probe, supplicant_remove, NULL, NULL);
1169
1170         return 0;
1171 }
1172
1173 static void supplicant_destroy(void)
1174 {
1175         if (g_slist_length(driver_list) > 0)
1176                 return;
1177
1178         DBG("connection %p", connection);
1179
1180         if (watch > 0)
1181                 g_dbus_remove_watch(connection, watch);
1182
1183         dbus_bus_remove_match(connection, supplicant_rule, NULL);
1184         dbus_connection_flush(connection);
1185
1186         dbus_connection_remove_filter(connection, supplicant_filter, NULL);
1187
1188         dbus_connection_unref(connection);
1189         connection = NULL;
1190 }
1191
1192 int supplicant_register(struct supplicant_driver *driver)
1193 {
1194         int err;
1195
1196         DBG("driver %p name %s", driver, driver->name);
1197
1198         err = supplicant_create();
1199         if (err < 0)
1200                 return err;
1201
1202         driver_list = g_slist_append(driver_list, driver);
1203
1204         if (g_dbus_check_service(connection, SUPPLICANT_NAME) == TRUE)
1205                 supplicant_probe(connection, NULL);
1206         else
1207                 supplicant_activate(connection);
1208
1209         return 0;
1210 }
1211
1212 void supplicant_unregister(struct supplicant_driver *driver)
1213 {
1214         DBG("driver %p name %s", driver, driver->name);
1215
1216         supplicant_remove(connection, NULL);
1217
1218         driver_list = g_slist_remove(driver_list, driver);
1219
1220         supplicant_destroy();
1221 }