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