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