Add support for connecting WiFi networks
[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, *security;
856                 unsigned char strength;
857
858                 network = connman_network_create(temp,
859                                                 CONNMAN_NETWORK_TYPE_WIFI);
860                 if (network == NULL)
861                         goto done;
862
863                 connman_network_set_string(network, "Name", result.identifier);
864
865                 connman_network_set_blob(network, "WiFi.SSID",
866                                                 result.ssid, result.ssid_len);
867
868                 mode = (result.adhoc == TRUE) ? "adhoc" : "managed";
869                 connman_network_set_string(network, "WiFi.Mode", mode);
870
871                 DBG("%s (%s %s) strength %d", result.identifier, mode,
872                                                         security, strength);
873
874                 if (connman_device_add_network(task->device, network) < 0) {
875                         connman_network_unref(network);
876                         goto done;
877                 }
878         }
879
880         connman_network_set_uint8(network, "Strength", strength);
881
882         connman_network_set_string(network, "WiFi.Security", security);
883
884 done:
885         g_free(result.identifier);
886         g_free(result.ssid);
887         g_free(temp);
888
889         dbus_message_unref(reply);
890
891         get_properties(task);
892 }
893
894 static int get_properties(struct supplicant_task *task)
895 {
896         DBusMessage *message;
897         DBusPendingCall *call;
898         char *path;
899
900         path = g_slist_nth_data(task->scan_results, 0);
901         if (path == NULL) {
902                 connman_device_set_scanning(task->device, FALSE);
903                 return 0;
904         }
905
906         message = dbus_message_new_method_call(SUPPLICANT_NAME, path,
907                                                 SUPPLICANT_INTF ".BSSID",
908                                                                 "properties");
909
910         task->scan_results = g_slist_remove(task->scan_results, path);
911         g_free(path);
912
913         if (message == NULL)
914                 return -ENOMEM;
915
916         if (dbus_connection_send_with_reply(connection, message,
917                                                 &call, TIMEOUT) == FALSE) {
918                 connman_error("Failed to get network properties");
919                 dbus_message_unref(message);
920                 return -EIO;
921         }
922
923         dbus_pending_call_set_notify(call, properties_reply, task, NULL);
924
925         dbus_message_unref(message);
926
927         return 0;
928 }
929
930 static void scan_results_reply(DBusPendingCall *call, void *user_data)
931 {
932         struct supplicant_task *task = user_data;
933         DBusMessage *reply;
934         DBusError error;
935         char **results;
936         int i, num_results;
937
938         DBG("task %p", task);
939
940         reply = dbus_pending_call_steal_reply(call);
941         if (reply == NULL) {
942                 connman_device_set_scanning(task->device, FALSE);
943                 return;
944         }
945
946         dbus_error_init(&error);
947
948         if (dbus_message_get_args(reply, &error,
949                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
950                                                 &results, &num_results,
951                                                 DBUS_TYPE_INVALID) == FALSE) {
952                 if (dbus_error_is_set(&error) == TRUE) {
953                         connman_error("%s", error.message);
954                         dbus_error_free(&error);
955                 } else
956                         connman_error("Wrong arguments for scan result");
957                 connman_device_set_scanning(task->device, FALSE);
958                 goto done;
959         }
960
961         for (i = 0; i < num_results; i++) {
962                 char *path = g_strdup(results[i]);
963                 if (path == NULL)
964                         continue;
965
966                 task->scan_results = g_slist_append(task->scan_results, path);
967         }
968
969         g_strfreev(results);
970
971         get_properties(task);
972
973 done:
974         dbus_message_unref(reply);
975 }
976
977 static int scan_results_available(struct supplicant_task *task)
978 {
979         DBusMessage *message;
980         DBusPendingCall *call;
981
982         DBG("task %p", task);
983
984         message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
985                                                 SUPPLICANT_INTF ".Interface",
986                                                         "scanResults");
987         if (message == NULL)
988                 return -ENOMEM;
989
990         if (dbus_connection_send_with_reply(connection, message,
991                                                 &call, TIMEOUT) == FALSE) {
992                 connman_error("Failed to request scan result");
993                 dbus_message_unref(message);
994                 return -EIO;
995         }
996
997         connman_device_set_scanning(task->device, TRUE);
998
999         dbus_pending_call_set_notify(call, scan_results_reply, task, NULL);
1000
1001         dbus_message_unref(message);
1002
1003         return 0;
1004 }
1005
1006 static void state_change(struct supplicant_task *task, DBusMessage *msg)
1007 {
1008         DBusError error;
1009         const char *state, *previous;
1010
1011         dbus_error_init(&error);
1012
1013         if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &state,
1014                                                 DBUS_TYPE_STRING, &previous,
1015                                                 DBUS_TYPE_INVALID) == FALSE) {
1016                 if (dbus_error_is_set(&error) == TRUE) {
1017                         connman_error("%s", error.message);
1018                         dbus_error_free(&error);
1019                 } else
1020                         connman_error("Wrong arguments for state change");
1021                 return;
1022         }
1023
1024         DBG("state %s ==> %s", previous, state);
1025
1026         if (g_str_equal(state, "INACTIVE") == TRUE)
1027                 task->state = STATE_INACTIVE;
1028         else if (g_str_equal(state, "SCANNING") == TRUE)
1029                 task->state = STATE_SCANNING;
1030         else if (g_str_equal(state, "ASSOCIATING") == TRUE)
1031                 task->state = STATE_ASSOCIATING;
1032         else if (g_str_equal(state, "ASSOCIATED") == TRUE)
1033                 task->state = STATE_ASSOCIATED;
1034         else if (g_str_equal(state, "GROUP_HANDSHAKE") == TRUE)
1035                 task->state = STATE_GROUP_HANDSHAKE;
1036         else if (g_str_equal(state, "4WAY_HANDSHAKE") == TRUE)
1037                 task->state = STATE_4WAY_HANDSHAKE;
1038         else if (g_str_equal(state, "COMPLETED") == TRUE)
1039                 task->state = STATE_COMPLETED;
1040         else if (g_str_equal(state, "DISCONNECTED") == TRUE)
1041                 task->state = STATE_DISCONNECTED;
1042
1043         if (task->state == STATE_SCANNING)
1044                 connman_device_set_scanning(task->device, TRUE);
1045
1046         switch (task->state) {
1047         case STATE_COMPLETED:
1048                 /* carrier on */
1049                 break;
1050         case STATE_DISCONNECTED:
1051                 /* carrier off */
1052                 break;
1053         default:
1054                 break;
1055         }
1056 }
1057
1058 static DBusHandlerResult supplicant_filter(DBusConnection *conn,
1059                                                 DBusMessage *msg, void *data)
1060 {
1061         struct supplicant_task *task;
1062         const char *member, *path;
1063
1064         if (dbus_message_has_interface(msg,
1065                                 SUPPLICANT_INTF ".Interface") == FALSE)
1066                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1067
1068         member = dbus_message_get_member(msg);
1069         if (member == NULL)
1070                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1071
1072         path = dbus_message_get_path(msg);
1073         if (path == NULL)
1074                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1075
1076         task = find_task_by_path(path);
1077         if (task == NULL)
1078                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1079
1080         DBG("task %p member %s", task, member);
1081
1082         if (g_str_equal(member, "ScanResultsAvailable") == TRUE)
1083                 scan_results_available(task);
1084         else if (g_str_equal(member, "StateChange") == TRUE)
1085                 state_change(task, msg);
1086
1087         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1088 }
1089
1090 int supplicant_start(struct connman_device *device)
1091 {
1092         struct supplicant_task *task;
1093
1094         DBG("device %p", device);
1095
1096         task = g_try_new0(struct supplicant_task, 1);
1097         if (task == NULL)
1098                 return -ENOMEM;
1099
1100         task->ifindex = connman_device_get_index(device);
1101         task->ifname = inet_index2name(task->ifindex);
1102         task->device = device;
1103
1104         if (task->ifname == NULL) {
1105                 g_free(task);
1106                 return -ENOMEM;
1107         }
1108
1109         task->created = FALSE;
1110         task->state = STATE_INACTIVE;
1111
1112         task_list = g_slist_append(task_list, task);
1113
1114         return create_interface(task);
1115 }
1116
1117 int supplicant_stop(struct connman_device *device)
1118 {
1119         int index = connman_device_get_index(device);
1120         struct supplicant_task *task;
1121
1122         DBG("device %p", device);
1123
1124         task = find_task_by_index(index);
1125         if (task == NULL)
1126                 return -ENODEV;
1127
1128         task_list = g_slist_remove(task_list, task);
1129
1130         disable_network(task);
1131
1132         remove_network(task);
1133
1134         return remove_interface(task);
1135 }
1136
1137 int supplicant_scan(struct connman_device *device)
1138 {
1139         int index = connman_device_get_index(device);
1140         struct supplicant_task *task;
1141         int err;
1142
1143         DBG("device %p", device);
1144
1145         task = find_task_by_index(index);
1146         if (task == NULL)
1147                 return -ENODEV;
1148
1149         switch (task->state) {
1150         case STATE_SCANNING:
1151                 return -EALREADY;
1152         case STATE_ASSOCIATING:
1153         case STATE_ASSOCIATED:
1154         case STATE_4WAY_HANDSHAKE:
1155         case STATE_GROUP_HANDSHAKE:
1156                 return -EBUSY;
1157         default:
1158                 break;
1159         }
1160
1161         err = initiate_scan(task);
1162
1163         return 0;
1164 }
1165
1166 int supplicant_connect(struct connman_network *network)
1167 {
1168         struct supplicant_task *task;
1169         const char *security, *passphrase;
1170         const void *ssid;
1171         unsigned int ssid_len;
1172         int index;
1173
1174         DBG("network %p", network);
1175
1176         security = connman_network_get_string(network, "WiFi.Security");
1177         passphrase = connman_network_get_string(network, "WiFi.Passphrase");
1178
1179         ssid = connman_network_get_blob(network, "WiFi.SSID", &ssid_len);
1180
1181         index = connman_network_get_index(network);
1182
1183         task = find_task_by_index(index);
1184         if (task == NULL)
1185                 return -ENODEV;
1186
1187         add_network(task);
1188
1189         select_network(task);
1190         disable_network(task);
1191
1192         set_network(task, ssid, ssid_len, security, passphrase);
1193
1194         enable_network(task);
1195
1196         return 0;
1197 }
1198
1199 int supplicant_disconnect(struct connman_network *network)
1200 {
1201         struct supplicant_task *task;
1202         int index;
1203
1204         DBG("network %p", network);
1205
1206         index = connman_network_get_index(network);
1207
1208         task = find_task_by_index(index);
1209         if (task == NULL)
1210                 return -ENODEV;
1211
1212         disable_network(task);
1213
1214         remove_network(task);
1215
1216         return 0;
1217 }
1218
1219 static void supplicant_activate(DBusConnection *conn)
1220 {
1221         DBusMessage *message;
1222
1223         DBG("conn %p", conn);
1224
1225         message = dbus_message_new_method_call(SUPPLICANT_NAME, "/",
1226                                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect");
1227         if (message == NULL)
1228                 return;
1229
1230         dbus_message_set_no_reply(message, TRUE);
1231
1232         dbus_connection_send(conn, message, NULL);
1233
1234         dbus_message_unref(message);
1235 }
1236
1237 static GSList *driver_list = NULL;
1238
1239 static void supplicant_probe(DBusConnection *conn, void *user_data)
1240 {
1241         GSList *list;
1242
1243         DBG("conn %p", conn);
1244
1245         for (list = driver_list; list; list = list->next) {
1246                 struct supplicant_driver *driver = list->data;
1247
1248                 DBG("driver %p name %s", driver, driver->name);
1249
1250                 if (driver->probe)
1251                         driver->probe();
1252         }
1253 }
1254
1255 static void supplicant_remove(DBusConnection *conn, void *user_data)
1256 {
1257         GSList *list;
1258
1259         DBG("conn %p", conn);
1260
1261         for (list = driver_list; list; list = list->next) {
1262                 struct supplicant_driver *driver = list->data;
1263
1264                 DBG("driver %p name %s", driver, driver->name);
1265
1266                 if (driver->remove)
1267                         driver->remove();
1268         }
1269 }
1270
1271 static const char *supplicant_rule = "type=signal,"
1272                                 "interface=" SUPPLICANT_INTF ".Interface";
1273 static guint watch;
1274
1275 static int supplicant_create(void)
1276 {
1277         if (g_slist_length(driver_list) > 0)
1278                 return 0;
1279
1280         connection = connman_dbus_get_connection();
1281         if (connection == NULL)
1282                 return -EIO;
1283
1284         DBG("connection %p", connection);
1285
1286         if (dbus_connection_add_filter(connection,
1287                                 supplicant_filter, NULL, NULL) == FALSE) {
1288                 connection = connman_dbus_get_connection();
1289                 return -EIO;
1290         }
1291
1292         dbus_bus_add_match(connection, supplicant_rule, NULL);
1293         dbus_connection_flush(connection);
1294
1295         watch = g_dbus_add_service_watch(connection, SUPPLICANT_NAME,
1296                         supplicant_probe, supplicant_remove, NULL, NULL);
1297
1298         return 0;
1299 }
1300
1301 static void supplicant_destroy(void)
1302 {
1303         if (g_slist_length(driver_list) > 0)
1304                 return;
1305
1306         DBG("connection %p", connection);
1307
1308         if (watch > 0)
1309                 g_dbus_remove_watch(connection, watch);
1310
1311         dbus_bus_remove_match(connection, supplicant_rule, NULL);
1312         dbus_connection_flush(connection);
1313
1314         dbus_connection_remove_filter(connection, supplicant_filter, NULL);
1315
1316         dbus_connection_unref(connection);
1317         connection = NULL;
1318 }
1319
1320 int supplicant_register(struct supplicant_driver *driver)
1321 {
1322         int err;
1323
1324         DBG("driver %p name %s", driver, driver->name);
1325
1326         err = supplicant_create();
1327         if (err < 0)
1328                 return err;
1329
1330         driver_list = g_slist_append(driver_list, driver);
1331
1332         if (g_dbus_check_service(connection, SUPPLICANT_NAME) == TRUE)
1333                 supplicant_probe(connection, NULL);
1334         else
1335                 supplicant_activate(connection);
1336
1337         return 0;
1338 }
1339
1340 void supplicant_unregister(struct supplicant_driver *driver)
1341 {
1342         DBG("driver %p name %s", driver, driver->name);
1343
1344         supplicant_remove(connection, NULL);
1345
1346         driver_list = g_slist_remove(driver_list, driver);
1347
1348         supplicant_destroy();
1349 }