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