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