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