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