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