Add ready and killed callbacks to supplicant test program
[framework/connectivity/connman.git] / tools / 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 <string.h>
28 #include <stdint.h>
29 #include <syslog.h>
30
31 #include <glib.h>
32 #include <gdbus.h>
33
34 #include "supplicant-dbus.h"
35 #include "supplicant.h"
36
37 #define DBG(fmt, arg...) do { \
38         syslog(LOG_DEBUG, "%s() " fmt, __FUNCTION__ , ## arg); \
39 } while (0)
40
41 #define TIMEOUT 5000
42
43 #define IEEE80211_CAP_ESS       0x0001
44 #define IEEE80211_CAP_IBSS      0x0002
45 #define IEEE80211_CAP_PRIVACY   0x0010
46
47 static DBusConnection *connection;
48
49 static const struct supplicant_callbacks *callbacks_pointer;
50
51 static dbus_bool_t system_available = FALSE;
52
53 static dbus_int32_t debug_level = 0;
54 static dbus_bool_t debug_show_timestamps = FALSE;
55 static dbus_bool_t debug_show_keys = FALSE;
56
57 static unsigned int eap_methods;
58
59 struct strvalmap {
60         const char *str;
61         unsigned int val;
62 };
63
64 static struct strvalmap eap_method_map[] = {
65         { "MD5",        SUPPLICANT_EAP_METHOD_MD5       },
66         { "TLS",        SUPPLICANT_EAP_METHOD_TLS       },
67         { "MSCHAPV2",   SUPPLICANT_EAP_METHOD_MSCHAPV2  },
68         { "PEAP",       SUPPLICANT_EAP_METHOD_PEAP      },
69         { "TTLS",       SUPPLICANT_EAP_METHOD_TTLS      },
70         { "GTC",        SUPPLICANT_EAP_METHOD_GTC       },
71         { "OTP",        SUPPLICANT_EAP_METHOD_OTP       },
72         { "LEAP",       SUPPLICANT_EAP_METHOD_LEAP      },
73         { }
74 };
75
76 static struct strvalmap auth_capa_map[] = {
77         { "open",       SUPPLICANT_CAPABILITY_AUTH_OPEN         },
78         { "shared",     SUPPLICANT_CAPABILITY_AUTH_SHARED       },
79         { "leap",       SUPPLICANT_CAPABILITY_AUTH_LEAP         },
80         { }
81 };
82
83 static struct strvalmap scan_capa_map[] = {
84         { "active",     SUPPLICANT_CAPABILITY_SCAN_ACTIVE       },
85         { "passive",    SUPPLICANT_CAPABILITY_SCAN_PASSIVE      },
86         { "ssid",       SUPPLICANT_CAPABILITY_SCAN_SSID         },
87         { }
88 };
89
90 static struct strvalmap mode_capa_map[] = {
91         { "infrastructure",     SUPPLICANT_CAPABILITY_MODE_INFRA        },
92         { "ad-hoc",             SUPPLICANT_CAPABILITY_MODE_IBSS         },
93         { "ap",                 SUPPLICANT_CAPABILITY_MODE_AP           },
94         { }
95 };
96
97 static GHashTable *interface_table;
98
99 struct supplicant_interface {
100         char *path;
101         unsigned int auth_capa;
102         unsigned int scan_capa;
103         unsigned int mode_capa;
104         enum supplicant_state state;
105         dbus_bool_t scanning;
106         int apscan;
107         char *ifname;
108         char *driver;
109         char *bridge;
110         GHashTable *network_table;
111         GHashTable *bss_mapping;
112 };
113
114 struct supplicant_network {
115         struct supplicant_interface *interface;
116         char *group;
117         char *name;
118         enum supplicant_mode mode;
119         GHashTable *bss_table;
120 };
121
122 struct supplicant_bss {
123         struct supplicant_interface *interface;
124         char *path;
125         unsigned char bssid[6];
126         unsigned char ssid[32];
127         unsigned int ssid_len;
128         unsigned int frequency;
129         enum supplicant_mode mode;
130         enum supplicant_security security;
131         dbus_bool_t privacy;
132         dbus_bool_t psk;
133         dbus_bool_t ieee8021x;
134 };
135
136 static const char *mode2string(enum supplicant_mode mode)
137 {
138         switch (mode) {
139         case SUPPLICANT_MODE_UNKNOWN:
140                 break;
141         case SUPPLICANT_MODE_INFRA:
142                 return "managed";
143         case SUPPLICANT_MODE_IBSS:
144                 return "adhoc";
145         }
146
147         return NULL;
148 }
149
150 static const char *security2string(enum supplicant_security security)
151 {
152         switch (security) {
153         case SUPPLICANT_SECURITY_UNKNOWN:
154                 break;
155         case SUPPLICANT_SECURITY_NONE:
156                 return "none";
157         case SUPPLICANT_SECURITY_WEP:
158                 return "wep";
159         case SUPPLICANT_SECURITY_PSK:
160                 return "psk";
161         case SUPPLICANT_SECURITY_IEEE8021X:
162                 return "ieee8021x";
163         }
164
165         return NULL;
166 }
167
168 static enum supplicant_state string2state(const char *state)
169 {
170         if (state == NULL)
171                 return SUPPLICANT_STATE_UNKNOWN;
172
173         if (g_str_equal(state, "unknown") == TRUE)
174                 return SUPPLICANT_STATE_UNKNOWN;
175         else if (g_str_equal(state, "disconnected") == TRUE)
176                 return SUPPLICANT_STATE_DISCONNECTED;
177         else if (g_str_equal(state, "inactive") == TRUE)
178                 return SUPPLICANT_STATE_INACTIVE;
179         else if (g_str_equal(state, "scanning") == TRUE)
180                 return SUPPLICANT_STATE_SCANNING;
181         else if (g_str_equal(state, "authenticating") == TRUE)
182                 return SUPPLICANT_STATE_AUTHENTICATING;
183         else if (g_str_equal(state, "associating") == TRUE)
184                 return SUPPLICANT_STATE_ASSOCIATING;
185         else if (g_str_equal(state, "associated") == TRUE)
186                 return SUPPLICANT_STATE_ASSOCIATED;
187         else if (g_str_equal(state, "group_handshake") == TRUE)
188                 return SUPPLICANT_STATE_GROUP_HANDSHAKE;
189         else if (g_str_equal(state, "4way_handshake") == TRUE)
190                 return SUPPLICANT_STATE_4WAY_HANDSHAKE;
191         else if (g_str_equal(state, "completed") == TRUE)
192                 return SUPPLICANT_STATE_COMPLETED;
193
194         return SUPPLICANT_STATE_UNKNOWN;
195 }
196
197 static void callback_system_ready(void)
198 {
199         if (callbacks_pointer == NULL)
200                 return;
201
202         if (callbacks_pointer->system_ready == NULL)
203                 return;
204
205         callbacks_pointer->system_ready();
206 }
207
208 static void callback_system_killed(void)
209 {
210         if (callbacks_pointer == NULL)
211                 return;
212
213         if (callbacks_pointer->system_killed == NULL)
214                 return;
215
216         callbacks_pointer->system_killed();
217 }
218
219 static void callback_interface_added(struct supplicant_interface *interface)
220 {
221         if (callbacks_pointer == NULL)
222                 return;
223
224         if (callbacks_pointer->interface_added == NULL)
225                 return;
226
227         callbacks_pointer->interface_added(interface);
228 }
229
230 static void callback_interface_removed(struct supplicant_interface *interface)
231 {
232         if (callbacks_pointer == NULL)
233                 return;
234
235         if (callbacks_pointer->interface_removed == NULL)
236                 return;
237
238         callbacks_pointer->interface_removed(interface);
239 }
240
241 static void callback_network_added(struct supplicant_network *network)
242 {
243         if (callbacks_pointer == NULL)
244                 return;
245
246         if (callbacks_pointer->network_added == NULL)
247                 return;
248
249         callbacks_pointer->network_added(network);
250 }
251
252 static void callback_network_removed(struct supplicant_network *network)
253 {
254         if (callbacks_pointer == NULL)
255                 return;
256
257         if (callbacks_pointer->network_removed == NULL)
258                 return;
259
260         callbacks_pointer->network_removed(network);
261 }
262
263 static void remove_interface(gpointer data)
264 {
265         struct supplicant_interface *interface = data;
266
267         callback_interface_removed(interface);
268
269         g_hash_table_destroy(interface->bss_mapping);
270         g_hash_table_destroy(interface->network_table);
271
272         g_free(interface->path);
273         g_free(interface->ifname);
274         g_free(interface->driver);
275         g_free(interface->bridge);
276         g_free(interface);
277 }
278
279 static void remove_network(gpointer data)
280 {
281         struct supplicant_network *network = data;
282
283         callback_network_removed(network);
284
285         g_free(network->group);
286         g_free(network->name);
287         g_free(network);
288 }
289
290 static void remove_bss(gpointer data)
291 {
292         struct supplicant_bss *bss = data;
293
294         g_free(bss->path);
295         g_free(bss);
296 }
297
298 static void debug_strvalmap(const char *label, struct strvalmap *map,
299                                                         unsigned int val)
300 {
301         int i;
302
303         for (i = 0; map[i].str != NULL; i++) {
304                 if (val & map[i].val)
305                         DBG("%s: %s", label, map[i].str);
306         }
307 }
308
309 static void interface_capability_auth(DBusMessageIter *iter, void *user_data)
310 {
311         struct supplicant_interface *interface = user_data;
312         const char *str = NULL;
313         int i;
314
315         dbus_message_iter_get_basic(iter, &str);
316         if (str == NULL)
317                 return;
318
319         for (i = 0; auth_capa_map[i].str != NULL; i++)
320                 if (strcmp(str, auth_capa_map[i].str) == 0) {
321                         interface->auth_capa |= auth_capa_map[i].val;
322                         break;
323                 }
324 }
325
326 static void interface_capability_scan(DBusMessageIter *iter, void *user_data)
327 {
328         struct supplicant_interface *interface = user_data;
329         const char *str = NULL;
330         int i;
331
332         dbus_message_iter_get_basic(iter, &str);
333         if (str == NULL)
334                 return;
335
336         for (i = 0; scan_capa_map[i].str != NULL; i++)
337                 if (strcmp(str, scan_capa_map[i].str) == 0) {
338                         interface->scan_capa |= scan_capa_map[i].val;
339                         break;
340                 }
341 }
342
343 static void interface_capability_mode(DBusMessageIter *iter, void *user_data)
344 {
345         struct supplicant_interface *interface = user_data;
346         const char *str = NULL;
347         int i;
348
349         dbus_message_iter_get_basic(iter, &str);
350         if (str == NULL)
351                 return;
352
353         for (i = 0; mode_capa_map[i].str != NULL; i++)
354                 if (strcmp(str, mode_capa_map[i].str) == 0) {
355                         interface->mode_capa |= mode_capa_map[i].val;
356                         break;
357                 }
358 }
359
360 static void interface_capability(const char *key, DBusMessageIter *iter,
361                                                         void *user_data)
362 {
363         struct supplicant_interface *interface = user_data;
364
365         if (key == NULL)
366                 return;
367
368         if (g_strcmp0(key, "AuthAlg") == 0)
369                 supplicant_dbus_array_foreach(iter, interface_capability_auth,
370                                                                 interface);
371         else if (g_strcmp0(key, "Scan") == 0)
372                 supplicant_dbus_array_foreach(iter, interface_capability_scan,
373                                                                 interface);
374         else if (g_strcmp0(key, "Modes") == 0)
375                 supplicant_dbus_array_foreach(iter, interface_capability_mode,
376                                                                 interface);
377         else
378                 DBG("key %s type %c",
379                                 key, dbus_message_iter_get_arg_type(iter));
380 }
381
382 const char *supplicant_interface_get_ifname(struct supplicant_interface *interface)
383 {
384         if (interface == NULL)
385                 return NULL;
386
387         return interface->ifname;
388 }
389
390 struct supplicant_interface *supplicant_network_get_interface(struct supplicant_network *network)
391 {
392         if (network == NULL)
393                 return NULL;
394
395         return network->interface;
396 }
397
398 const char *supplicant_network_get_name(struct supplicant_network *network)
399 {
400         if (network == NULL || network->name == NULL)
401                 return "";
402
403         return network->name;
404 }
405
406 const char *supplicant_network_get_identifier(struct supplicant_network *network)
407 {
408         if (network == NULL || network->group == NULL)
409                 return "";
410
411         return network->group;
412 }
413
414 enum supplicant_mode supplicant_network_get_mode(struct supplicant_network *network)
415 {
416         if (network == NULL)
417                 return SUPPLICANT_MODE_UNKNOWN;
418
419         return network->mode;
420 }
421
422 static void network_property(const char *key, DBusMessageIter *iter,
423                                                         void *user_data)
424 {
425         if (key == NULL)
426                 return;
427
428         DBG("key %s type %c", key, dbus_message_iter_get_arg_type(iter));
429 }
430
431 static void interface_network_added(DBusMessageIter *iter, void *user_data)
432 {
433         const char *path = NULL;
434
435         dbus_message_iter_get_basic(iter, &path);
436         if (path == NULL)
437                 return;
438
439         DBG("path %s", path);
440
441         supplicant_dbus_property_get_all(path,
442                                 SUPPLICANT_INTERFACE ".Interface.Network",
443                                                 network_property, NULL);
444 }
445
446 static void interface_network_removed(DBusMessageIter *iter, void *user_data)
447 {
448         const char *path = NULL;
449
450         dbus_message_iter_get_basic(iter, &path);
451         if (path == NULL)
452                 return;
453
454         DBG("path %s", path);
455 }
456
457 static char *create_name(unsigned char *ssid, int ssid_len)
458 {
459         char *name;
460         int i;
461
462         if (ssid_len < 1 || ssid[0] == '\0')
463                 name = NULL;
464         else
465                 name = g_try_malloc0(ssid_len + 1);
466
467         if (name == NULL)
468                 return g_strdup("");
469
470         for (i = 0; i < ssid_len; i++) {
471                 if (g_ascii_isprint(ssid[i]))
472                         name[i] = ssid[i];
473                 else
474                         name[i] = ' ';
475         }
476
477         return name;
478 }
479
480 static char *create_group(struct supplicant_bss *bss)
481 {
482         GString *str;
483         unsigned int i;
484         const char *mode, *security;
485
486         str = g_string_sized_new((bss->ssid_len * 2) + 24);
487         if (str == NULL)
488                 return NULL;
489
490         if (bss->ssid_len > 0 && bss->ssid[0] != '\0') {
491                 for (i = 0; i < bss->ssid_len; i++)
492                         g_string_append_printf(str, "%02x", bss->ssid[i]);
493         } else
494                 g_string_append_printf(str, "hidden");
495
496         mode = mode2string(bss->mode);
497         if (mode != NULL)
498                 g_string_append_printf(str, "_%s", mode);
499
500         security = security2string(bss->security);
501         if (security != NULL)
502                 g_string_append_printf(str, "_%s", security);
503
504         return g_string_free(str, FALSE);
505 }
506
507 static void add_bss_to_network(struct supplicant_bss *bss)
508 {
509         struct supplicant_interface *interface = bss->interface;
510         struct supplicant_network *network;
511         char *group;
512
513         group = create_group(bss);
514         if (group == NULL)
515                 return;
516
517         network = g_hash_table_lookup(interface->network_table, group);
518         if (network != NULL) {
519                 g_free(group);
520                 goto done;
521         }
522
523         network = g_try_new0(struct supplicant_network, 1);
524         if (network == NULL) {
525                 g_free(group);
526                 return;
527         }
528
529         network->group = group;
530         network->name = create_name(bss->ssid, bss->ssid_len);
531         network->mode = bss->mode;
532
533         network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
534                                                         NULL, remove_bss);
535
536         g_hash_table_replace(interface->network_table,
537                                                 network->group, network);
538
539         callback_network_added(network);
540
541 done:
542         g_hash_table_replace(interface->bss_mapping, bss->path, network);
543         g_hash_table_replace(network->bss_table, bss->path, bss);
544 }
545
546 static unsigned char wifi_oui[3]      = { 0x00, 0x50, 0xf2 };
547 static unsigned char ieee80211_oui[3] = { 0x00, 0x0f, 0xac };
548
549 static void extract_rsn(struct supplicant_bss *bss,
550                                         const unsigned char *buf, int len)
551 {
552         uint16_t count;
553         int i;
554
555         /* Version */
556         if (len < 2)
557                 return;
558
559         buf += 2;
560         len -= 2;
561
562         /* Group cipher */
563         if (len < 4)
564                 return;
565
566         buf += 4;
567         len -= 4;
568
569         /* Pairwise cipher */
570         if (len < 2)
571                 return;
572
573         count = buf[0] | (buf[1] << 8);
574         if (2 + (count * 4) > len)
575                 return;
576
577         buf += 2 + (count * 4);
578         len -= 2 + (count * 4);
579
580         /* Authentication */
581         if (len < 2)
582                 return;
583
584         count = buf[0] | (buf[1] << 8);
585         if (2 + (count * 4) > len)
586                 return;
587
588         for (i = 0; i < count; i++) {
589                 const unsigned char *ptr = buf + 2 + (i * 4);
590
591                 if (memcmp(ptr, wifi_oui, 3) == 0) {
592                         switch (ptr[3]) {
593                         case 1:
594                                 bss->ieee8021x = TRUE;
595                                 break;
596                         case 2:
597                                 bss->psk = TRUE;
598                                 break;
599                         }
600                 } else if (memcmp(ptr, ieee80211_oui, 3) == 0) {
601                         switch (ptr[3]) {
602                         case 1:
603                                 bss->ieee8021x = TRUE;
604                                 break;
605                         case 2:
606                                 bss->psk = TRUE;
607                                 break;
608                         }
609                 }
610         }
611
612         buf += 2 + (count * 4);
613         len -= 2 + (count * 4);
614 }
615
616 static void bss_property(const char *key, DBusMessageIter *iter,
617                                                         void *user_data)
618 {
619         struct supplicant_bss *bss = user_data;
620
621         if (bss->interface == NULL)
622                 return;
623
624         if (key == NULL) {
625                 if (bss->ieee8021x == TRUE)
626                         bss->security = SUPPLICANT_SECURITY_IEEE8021X;
627                 else if (bss->psk == TRUE)
628                         bss->security = SUPPLICANT_SECURITY_PSK;
629                 else if (bss->privacy == TRUE)
630                         bss->security = SUPPLICANT_SECURITY_WEP;
631                 else
632                         bss->security = SUPPLICANT_SECURITY_NONE;
633
634                 add_bss_to_network(bss);
635                 return;
636         }
637
638         if (g_strcmp0(key, "BSSID") == 0) {
639                 DBusMessageIter array;
640                 unsigned char *addr;
641                 int addr_len;
642
643                 dbus_message_iter_recurse(iter, &array);
644                 dbus_message_iter_get_fixed_array(&array, &addr, &addr_len);
645
646                 if (addr_len == 6)
647                         memcpy(bss->bssid, addr, addr_len);
648         } else if (g_strcmp0(key, "SSID") == 0) {
649                 DBusMessageIter array;
650                 unsigned char *ssid;
651                 int ssid_len;
652
653                 dbus_message_iter_recurse(iter, &array);
654                 dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
655
656                 if (ssid_len > 0 && ssid_len < 33) {
657                         memcpy(bss->ssid, ssid, ssid_len);
658                         bss->ssid_len = ssid_len;
659                 } else {
660                         memset(bss->ssid, 0, sizeof(bss->ssid));
661                         bss->ssid_len = 0;
662                 }
663         } else if (g_strcmp0(key, "Capabilities") == 0) {
664                 dbus_uint16_t capabilities = 0x0000;
665
666                 dbus_message_iter_get_basic(iter, &capabilities);
667
668                 if (capabilities & IEEE80211_CAP_ESS)
669                         bss->mode = SUPPLICANT_MODE_INFRA;
670                 else if (capabilities & IEEE80211_CAP_IBSS)
671                         bss->mode = SUPPLICANT_MODE_IBSS;
672
673                 if (capabilities & IEEE80211_CAP_PRIVACY)
674                         bss->privacy = TRUE;
675         } else if (g_strcmp0(key, "Frequency") == 0) {
676                 dbus_int32_t frequency = 0;
677
678                 dbus_message_iter_get_basic(iter, &frequency);
679                 bss->frequency = frequency;
680         } else if (g_strcmp0(key, "Level") == 0) {
681                 dbus_int32_t level = 0;
682
683                 dbus_message_iter_get_basic(iter, &level);
684         } else if (g_strcmp0(key, "MaxRate") == 0) {
685                 dbus_int32_t maxrate = 0;
686
687                 dbus_message_iter_get_basic(iter, &maxrate);
688         } else if (g_strcmp0(key, "RSNIE") == 0) {
689                 DBusMessageIter array;
690                 unsigned char *ie;
691                 int ie_len;
692
693                 dbus_message_iter_recurse(iter, &array);
694                 dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
695
696                 if (ie_len > 2)
697                         extract_rsn(bss, ie + 2, ie_len - 2);
698         } else if (g_strcmp0(key, "WPAIE") == 0) {
699                 DBusMessageIter array;
700                 unsigned char *ie;
701                 int ie_len;
702
703                 dbus_message_iter_recurse(iter, &array);
704                 dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
705
706                 if (ie_len > 6)
707                         extract_rsn(bss, ie + 6, ie_len - 6);
708         } else if (g_strcmp0(key, "WPSIE") == 0) {
709                 DBusMessageIter array;
710                 unsigned char *ie;
711                 int ie_len;
712
713                 dbus_message_iter_recurse(iter, &array);
714                 dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
715         } else
716                 DBG("key %s type %c",
717                                 key, dbus_message_iter_get_arg_type(iter));
718 }
719
720 static void interface_bss_added(DBusMessageIter *iter, void *user_data)
721 {
722         struct supplicant_interface *interface = user_data;
723         struct supplicant_network *network;
724         struct supplicant_bss *bss;
725         const char *path = NULL;
726
727         dbus_message_iter_get_basic(iter, &path);
728         if (path == NULL)
729                 return;
730
731         network = g_hash_table_lookup(interface->bss_mapping, path);
732         if (network != NULL) {
733                 bss = g_hash_table_lookup(network->bss_table, path);
734                 if (bss != NULL)
735                         return;
736         }
737
738         bss = g_try_new0(struct supplicant_bss, 1);
739         if (bss == NULL)
740                 return;
741
742         bss->interface = interface;
743         bss->path = g_strdup(path);
744
745         supplicant_dbus_property_get_all(path,
746                                         SUPPLICANT_INTERFACE ".Interface.BSS",
747                                                         bss_property, bss);
748 }
749
750 static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
751 {
752         struct supplicant_interface *interface = user_data;
753         struct supplicant_network *network;
754         const char *path = NULL;
755
756         dbus_message_iter_get_basic(iter, &path);
757         if (path == NULL)
758                 return;
759
760         network = g_hash_table_lookup(interface->bss_mapping, path);
761         if (network == NULL)
762                 return;
763
764         g_hash_table_remove(interface->bss_mapping, path);
765         g_hash_table_remove(network->bss_table, path);
766
767         if (g_hash_table_size(network->bss_table) == 0)
768                 g_hash_table_remove(interface->network_table, network->group);
769 }
770
771 static void interface_property(const char *key, DBusMessageIter *iter,
772                                                         void *user_data)
773 {
774         struct supplicant_interface *interface = user_data;
775
776         if (interface == NULL)
777                 return;
778
779         if (key == NULL) {
780                 debug_strvalmap("Auth capability", auth_capa_map,
781                                                         interface->auth_capa);
782                 debug_strvalmap("Scan capability", scan_capa_map,
783                                                         interface->scan_capa);
784                 debug_strvalmap("Mode capability", mode_capa_map,
785                                                         interface->mode_capa);
786
787                 g_hash_table_replace(interface_table,
788                                         interface->path, interface);
789
790                 callback_interface_added(interface);
791                 return;
792         }
793
794         if (g_strcmp0(key, "Capabilities") == 0) {
795                 supplicant_dbus_property_foreach(iter, interface_capability,
796                                                                 interface);
797         } else if (g_strcmp0(key, "State") == 0) {
798                 const char *str = NULL;
799
800                 dbus_message_iter_get_basic(iter, &str);
801                 if (str != NULL)
802                         interface->state = string2state(str);
803         } else if (g_strcmp0(key, "Scanning") == 0) {
804                 dbus_bool_t scanning = FALSE;
805
806                 dbus_message_iter_get_basic(iter, &scanning);
807                 interface->scanning = scanning;
808         } else if (g_strcmp0(key, "ApScan") == 0) {
809                 int apscan;
810
811                 dbus_message_iter_get_basic(iter, &apscan);
812                 interface->apscan = apscan;
813         } else if (g_strcmp0(key, "Ifname") == 0) {
814                 const char *str = NULL;
815
816                 dbus_message_iter_get_basic(iter, &str);
817                 if (str != NULL)
818                         interface->ifname = g_strdup(str);
819         } else if (g_strcmp0(key, "Driver") == 0) {
820                 const char *str = NULL;
821
822                 dbus_message_iter_get_basic(iter, &str);
823                 if (str != NULL)
824                         interface->driver = g_strdup(str);
825         } else if (g_strcmp0(key, "BridgeIfname") == 0) {
826                 const char *str = NULL;
827
828                 dbus_message_iter_get_basic(iter, &str);
829                 if (str != NULL)
830                         interface->bridge = g_strdup(str);
831         } else if (g_strcmp0(key, "CurrentBSS") == 0) {
832                 interface_bss_added(iter, interface);
833         } else if (g_strcmp0(key, "CurrentNetwork") == 0) {
834                 interface_network_added(iter, interface);
835         } else if (g_strcmp0(key, "BSSs") == 0) {
836                 supplicant_dbus_array_foreach(iter, interface_bss_added,
837                                                                 interface);
838         } else if (g_strcmp0(key, "Blobs") == 0) {
839         } else if (g_strcmp0(key, "Networks") == 0) {
840                 supplicant_dbus_array_foreach(iter, interface_network_added,
841                                                                 interface);
842         } else
843                 DBG("key %s type %c",
844                                 key, dbus_message_iter_get_arg_type(iter));
845 }
846
847 static void interface_added(DBusMessageIter *iter, void *user_data)
848 {
849         struct supplicant_interface *interface;
850         const char *path = NULL;
851
852         dbus_message_iter_get_basic(iter, &path);
853         if (path == NULL)
854                 return;
855
856         interface = g_hash_table_lookup(interface_table, path);
857         if (interface != NULL)
858                 return;
859
860         interface = g_try_new0(struct supplicant_interface, 1);
861         if (interface == NULL)
862                 return;
863
864         interface->path = g_strdup(path);
865
866         interface->network_table = g_hash_table_new_full(g_str_hash, g_str_equal,
867                                                         NULL, remove_network);
868
869         interface->bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
870                                                                 NULL, NULL);
871
872         supplicant_dbus_property_get_all(path,
873                                         SUPPLICANT_INTERFACE ".Interface",
874                                                 interface_property, interface);
875 }
876
877 static void interface_removed(DBusMessageIter *iter, void *user_data)
878 {
879         const char *path = NULL;
880
881         dbus_message_iter_get_basic(iter, &path);
882         if (path == NULL)
883                 return;
884
885         g_hash_table_remove(interface_table, path);
886 }
887
888 static void eap_method(DBusMessageIter *iter, void *user_data)
889 {
890         const char *str = NULL;
891         int i;
892
893         dbus_message_iter_get_basic(iter, &str);
894         if (str == NULL)
895                 return;
896
897         for (i = 0; eap_method_map[i].str != NULL; i++)
898                 if (strcmp(str, eap_method_map[i].str) == 0) {
899                         eap_methods |= eap_method_map[i].val;
900                         break;
901                 }
902 }
903
904 static void service_property(const char *key, DBusMessageIter *iter,
905                                                         void *user_data)
906 {
907         if (key == NULL) {
908                 callback_system_ready();
909                 return;
910         }
911
912         if (g_strcmp0(key, "DebugParams") == 0) {
913                 DBusMessageIter list;
914
915                 dbus_message_iter_recurse(iter, &list);
916                 dbus_message_iter_get_basic(&list, &debug_level);
917
918                 dbus_message_iter_next(&list);
919                 dbus_message_iter_get_basic(&list, &debug_show_timestamps);
920
921                 dbus_message_iter_next(&list);
922                 dbus_message_iter_get_basic(&list, &debug_show_keys);
923
924                 DBG("Debug level %d (timestamps %u keys %u)", debug_level,
925                                 debug_show_timestamps, debug_show_keys);
926         } else if (g_strcmp0(key, "Interfaces") == 0) {
927                 supplicant_dbus_array_foreach(iter, interface_added, user_data);
928         } else if (g_strcmp0(key, "EapMethods") == 0) {
929                 supplicant_dbus_array_foreach(iter, eap_method, user_data);
930                 debug_strvalmap("EAP method", eap_method_map, eap_methods);
931         }
932 }
933
934 static void supplicant_bootstrap(void)
935 {
936         supplicant_dbus_property_get_all(SUPPLICANT_PATH,
937                                                 SUPPLICANT_INTERFACE,
938                                                 service_property, NULL);
939 }
940
941 static void signal_name_owner_changed(const char *path, DBusMessageIter *iter)
942 {
943         const char *name = NULL, *old = NULL, *new = NULL;
944
945         if (g_strcmp0(path, DBUS_PATH_DBUS) != 0)
946                 return;
947
948         dbus_message_iter_get_basic(iter, &name);
949         if (name == NULL)
950                 return;
951
952         if (g_strcmp0(name, SUPPLICANT_SERVICE) != 0)
953                 return;
954
955         dbus_message_iter_next(iter);
956         dbus_message_iter_get_basic(iter, &old);
957         dbus_message_iter_next(iter);
958         dbus_message_iter_get_basic(iter, &new);
959
960         if (old == NULL || new == NULL)
961                 return;
962
963         if (strlen(old) > 0 && strlen(new) == 0) {
964                 system_available = FALSE;
965                 g_hash_table_remove_all(interface_table);
966                 callback_system_killed();
967         }
968
969         if (strlen(new) > 0 && strlen(old) == 0) {
970                 system_available = TRUE;
971                 supplicant_bootstrap();
972         }
973 }
974
975 static void signal_properties_changed(const char *path, DBusMessageIter *iter)
976 {
977         if (g_strcmp0(path, SUPPLICANT_PATH) != 0)
978                 return;
979
980         supplicant_dbus_property_foreach(iter, service_property, NULL);
981 }
982
983 static void signal_interface_added(const char *path, DBusMessageIter *iter)
984 {
985         if (g_strcmp0(path, SUPPLICANT_PATH) == 0)
986                 interface_added(iter, NULL);
987 }
988
989 static void signal_interface_removed(const char *path, DBusMessageIter *iter)
990 {
991         if (g_strcmp0(path, SUPPLICANT_PATH) == 0)
992                 interface_removed(iter, NULL);
993 }
994
995 static void signal_bss_added(const char *path, DBusMessageIter *iter)
996 {
997         struct supplicant_interface *interface;
998
999         interface = g_hash_table_lookup(interface_table, path);
1000         if (interface == NULL)
1001                 return;
1002
1003         interface_bss_added(iter, interface);
1004 }
1005
1006 static void signal_bss_removed(const char *path, DBusMessageIter *iter)
1007 {
1008         struct supplicant_interface *interface;
1009
1010         interface = g_hash_table_lookup(interface_table, path);
1011         if (interface == NULL)
1012                 return;
1013
1014         interface_bss_removed(iter, interface);
1015 }
1016
1017 static void signal_network_added(const char *path, DBusMessageIter *iter)
1018 {
1019         struct supplicant_interface *interface;
1020
1021         interface = g_hash_table_lookup(interface_table, path);
1022         if (interface == NULL)
1023                 return;
1024
1025         interface_network_added(iter, interface);
1026 }
1027
1028 static void signal_network_removed(const char *path, DBusMessageIter *iter)
1029 {
1030         struct supplicant_interface *interface;
1031
1032         interface = g_hash_table_lookup(interface_table, path);
1033         if (interface == NULL)
1034                 return;
1035
1036         interface_network_removed(iter, interface);
1037 }
1038
1039 static struct {
1040         const char *interface;
1041         const char *member;
1042         void (*function) (const char *path, DBusMessageIter *iter);
1043 } signal_map[] = {
1044         { DBUS_INTERFACE_DBUS,  "NameOwnerChanged",  signal_name_owner_changed },
1045
1046         { SUPPLICANT_INTERFACE, "PropertiesChanged", signal_properties_changed },
1047         { SUPPLICANT_INTERFACE, "InterfaceAdded",    signal_interface_added    },
1048         { SUPPLICANT_INTERFACE, "InterfaceCreated",  signal_interface_added    },
1049         { SUPPLICANT_INTERFACE, "InterfaceRemoved",  signal_interface_removed  },
1050
1051         { SUPPLICANT_INTERFACE ".Interface", "BSSAdded",       signal_bss_added       },
1052         { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved",     signal_bss_removed     },
1053         { SUPPLICANT_INTERFACE ".Interface", "NetworkAdded",   signal_network_added   },
1054         { SUPPLICANT_INTERFACE ".Interface", "NetworkRemoved", signal_network_removed },
1055
1056         { }
1057 };
1058
1059 static DBusHandlerResult supplicant_filter(DBusConnection *conn,
1060                                         DBusMessage *message, void *data)
1061 {
1062         DBusMessageIter iter;
1063         const char *path;
1064         int i;
1065
1066         path = dbus_message_get_path(message);
1067         if (path == NULL)
1068                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1069
1070         if (dbus_message_iter_init(message, &iter) == FALSE)
1071                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1072
1073         for (i = 0; signal_map[i].interface != NULL; i++) {
1074                 if (dbus_message_has_interface(message,
1075                                         signal_map[i].interface) == FALSE)
1076                         continue;
1077
1078                 if (dbus_message_has_member(message,
1079                                         signal_map[i].member) == FALSE)
1080                         continue;
1081
1082                 signal_map[i].function(path, &iter);
1083                 break;
1084         }
1085
1086         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1087 }
1088
1089 static const char *supplicant_rule0 = "type=signal,"
1090                                         "path=" DBUS_PATH_DBUS ","
1091                                         "sender=" DBUS_SERVICE_DBUS ","
1092                                         "interface=" DBUS_INTERFACE_DBUS ","
1093                                         "member=NameOwnerChanged,"
1094                                         "arg0=" SUPPLICANT_SERVICE;
1095 static const char *supplicant_rule1 = "type=signal,"
1096                         "interface=" SUPPLICANT_INTERFACE;
1097 static const char *supplicant_rule2 = "type=signal,"
1098                         "interface=" SUPPLICANT_INTERFACE ".Interface";
1099 static const char *supplicant_rule3 = "type=signal,"
1100                         "interface=" SUPPLICANT_INTERFACE ".Interface.WPS";
1101 static const char *supplicant_rule4 = "type=signal,"
1102                         "interface=" SUPPLICANT_INTERFACE ".Interface.BSS";
1103 static const char *supplicant_rule5 = "type=signal,"
1104                         "interface=" SUPPLICANT_INTERFACE ".Interface.Network";
1105 static const char *supplicant_rule6 = "type=signal,"
1106                         "interface=" SUPPLICANT_INTERFACE ".Interface.Blob";
1107
1108 int supplicant_register(const struct supplicant_callbacks *callbacks)
1109 {
1110         connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
1111         if (connection == NULL)
1112                 return -EIO;
1113
1114         if (dbus_connection_add_filter(connection,
1115                                 supplicant_filter, NULL, NULL) == FALSE) {
1116                 dbus_connection_unref(connection);
1117                 connection = NULL;
1118                 return -EIO;
1119         }
1120
1121         callbacks_pointer = callbacks;
1122         eap_methods = 0;
1123
1124         interface_table = g_hash_table_new_full(g_str_hash, g_str_equal,
1125                                                 NULL, remove_interface);
1126
1127         supplicant_dbus_setup(connection);
1128
1129         dbus_bus_add_match(connection, supplicant_rule0, NULL);
1130         dbus_bus_add_match(connection, supplicant_rule1, NULL);
1131         dbus_bus_add_match(connection, supplicant_rule2, NULL);
1132         dbus_bus_add_match(connection, supplicant_rule3, NULL);
1133         dbus_bus_add_match(connection, supplicant_rule4, NULL);
1134         dbus_bus_add_match(connection, supplicant_rule5, NULL);
1135         dbus_bus_add_match(connection, supplicant_rule6, NULL);
1136         dbus_connection_flush(connection);
1137
1138         if (dbus_bus_name_has_owner(connection,
1139                                         SUPPLICANT_SERVICE, NULL) == TRUE) {
1140                 system_available = TRUE;
1141                 supplicant_bootstrap();
1142         }
1143
1144         return 0;
1145 }
1146
1147 void supplicant_unregister(const struct supplicant_callbacks *callbacks)
1148 {
1149         if (connection != NULL) {
1150                 dbus_bus_remove_match(connection, supplicant_rule6, NULL);
1151                 dbus_bus_remove_match(connection, supplicant_rule5, NULL);
1152                 dbus_bus_remove_match(connection, supplicant_rule4, NULL);
1153                 dbus_bus_remove_match(connection, supplicant_rule3, NULL);
1154                 dbus_bus_remove_match(connection, supplicant_rule2, NULL);
1155                 dbus_bus_remove_match(connection, supplicant_rule1, NULL);
1156                 dbus_bus_remove_match(connection, supplicant_rule0, NULL);
1157                 dbus_connection_flush(connection);
1158
1159                 dbus_connection_remove_filter(connection,
1160                                                 supplicant_filter, NULL);
1161         }
1162
1163         if (interface_table != NULL) {
1164                 g_hash_table_destroy(interface_table);
1165                 interface_table = NULL;
1166         }
1167
1168         if (system_available == TRUE)
1169                 callback_system_killed();
1170
1171         if (connection != NULL) {
1172                 dbus_connection_unref(connection);
1173                 connection = NULL;
1174         }
1175
1176         callbacks_pointer = NULL;
1177         eap_methods = 0;
1178 }
1179
1180 static void debug_level_result(const char *error,
1181                                 DBusMessageIter *iter, void *user_data)
1182 {
1183         if (error != NULL)
1184                 DBG("debug level failure: %s", error);
1185 }
1186
1187 static void add_debug_level(DBusMessageIter *iter, void *user_data)
1188 {
1189         dbus_int32_t level = GPOINTER_TO_UINT(user_data);
1190         DBusMessageIter entry;
1191
1192         dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
1193                                                         NULL, &entry);
1194
1195         dbus_message_iter_append_basic(&entry, DBUS_TYPE_INT32, &level);
1196         dbus_message_iter_append_basic(&entry, DBUS_TYPE_BOOLEAN,
1197                                                 &debug_show_timestamps);
1198         dbus_message_iter_append_basic(&entry, DBUS_TYPE_BOOLEAN,
1199                                                 &debug_show_keys);
1200
1201         dbus_message_iter_close_container(iter, &entry);
1202 }
1203
1204 void supplicant_set_debug_level(unsigned int level)
1205 {
1206         supplicant_dbus_property_set(SUPPLICANT_PATH, SUPPLICANT_INTERFACE,
1207                                 "DebugParams", "(ibb)", add_debug_level,
1208                                 debug_level_result, GUINT_TO_POINTER(level));
1209 }
1210
1211 static void add_show_timestamps(DBusMessageIter *iter, void *user_data)
1212 {
1213         dbus_bool_t show_timestamps = GPOINTER_TO_UINT(user_data);
1214         DBusMessageIter entry;
1215
1216         dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
1217                                                         NULL, &entry);
1218
1219         dbus_message_iter_append_basic(&entry, DBUS_TYPE_INT32, &debug_level);
1220         dbus_message_iter_append_basic(&entry, DBUS_TYPE_BOOLEAN,
1221                                                         &show_timestamps);
1222         dbus_message_iter_append_basic(&entry, DBUS_TYPE_BOOLEAN,
1223                                                         &debug_show_keys);
1224
1225         dbus_message_iter_close_container(iter, &entry);
1226 }
1227
1228 void supplicant_set_debug_show_timestamps(dbus_bool_t enabled)
1229 {
1230         supplicant_dbus_property_set(SUPPLICANT_PATH, SUPPLICANT_INTERFACE,
1231                                 "DebugParams", "(ibb)", add_show_timestamps,
1232                                         NULL, GUINT_TO_POINTER(enabled));
1233 }