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