gsupplicant: WPS event signal support
[platform/upstream/connman.git] / gsupplicant / supplicant.c
index c24e4dc..a3349b3 100644 (file)
@@ -136,8 +136,15 @@ static struct strvalmap mode_capa_map[] = {
 static GHashTable *interface_table;
 static GHashTable *bss_mapping;
 
+struct _GSupplicantWpsCredentials {
+       unsigned char ssid[32];
+       unsigned int ssid_len;
+       char *key;
+};
+
 struct _GSupplicantInterface {
        char *path;
+       char *network_path;
        unsigned int keymgmt_capa;
        unsigned int authalg_capa;
        unsigned int proto_capa;
@@ -154,6 +161,8 @@ struct _GSupplicantInterface {
        char *ifname;
        char *driver;
        char *bridge;
+       struct _GSupplicantWpsCredentials wps_cred;
+       GSupplicantWpsState wps_state;
        GHashTable *network_table;
        GHashTable *net_mapping;
        GHashTable *bss_mapping;
@@ -170,6 +179,7 @@ struct _GSupplicantNetwork {
        dbus_int16_t signal;
        GSupplicantMode mode;
        GSupplicantSecurity security;
+       dbus_bool_t wps;
        GHashTable *bss_table;
        GHashTable *config_table;
 };
@@ -209,6 +219,9 @@ static inline void debug(const char *format, ...)
        va_end(ap);
 }
 
+#define SUPPLICANT_DBG(fmt, arg...) \
+       debug("%s:%s() " fmt, __FILE__, __FUNCTION__ , ## arg);
+
 static GSupplicantMode string2mode(const char *mode)
 {
        if (mode == NULL)
@@ -228,7 +241,7 @@ static const char *mode2string(GSupplicantMode mode)
        case G_SUPPLICANT_MODE_UNKNOWN:
                break;
        case G_SUPPLICANT_MODE_INFRA:
-               return "infra";
+               return "managed";
        case G_SUPPLICANT_MODE_IBSS:
                return "adhoc";
        }
@@ -314,7 +327,7 @@ static void callback_system_killed(void)
 
 static void callback_interface_added(GSupplicantInterface *interface)
 {
-       debug("");
+       SUPPLICANT_DBG("");
 
        if (callbacks_pointer == NULL)
                return;
@@ -401,7 +414,9 @@ static void remove_interface(gpointer data)
 
        callback_interface_removed(interface);
 
+       g_free(interface->wps_cred.key);
        g_free(interface->path);
+       g_free(interface->network_path);
        g_free(interface->ifname);
        g_free(interface->driver);
        g_free(interface->bridge);
@@ -418,6 +433,7 @@ static void remove_network(gpointer data)
 
        g_hash_table_destroy(network->config_table);
 
+       g_free(network->path);
        g_free(network->group);
        g_free(network->name);
        g_free(network);
@@ -438,7 +454,7 @@ static void debug_strvalmap(const char *label, struct strvalmap *map,
 
        for (i = 0; map[i].str != NULL; i++) {
                if (val & map[i].val)
-                       debug("%s: %s", label, map[i].str);
+                       SUPPLICANT_DBG("%s: %s", label, map[i].str);
        }
 }
 
@@ -592,7 +608,7 @@ static void interface_capability(const char *key, DBusMessageIter *iter,
                supplicant_dbus_array_foreach(iter,
                                interface_capability_mode, interface);
        else
-               debug("key %s type %c",
+               SUPPLICANT_DBG("key %s type %c",
                                key, dbus_message_iter_get_arg_type(iter));
 }
 
@@ -605,7 +621,7 @@ void g_supplicant_interface_set_data(GSupplicantInterface *interface,
        interface->data = data;
 }
 
-const void *g_supplicant_interface_get_data(GSupplicantInterface *interface)
+void *g_supplicant_interface_get_data(GSupplicantInterface *interface)
 {
        if (interface == NULL)
                return NULL;
@@ -638,6 +654,38 @@ GSupplicantState g_supplicant_interface_get_state(
        return interface->state;
 }
 
+const char *g_supplicant_interface_get_wps_key(GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return NULL;
+
+       return (const char *)interface->wps_cred.key;
+}
+
+const void *g_supplicant_interface_get_wps_ssid(GSupplicantInterface *interface,
+                                                       unsigned int *ssid_len)
+{
+       if (ssid_len == NULL)
+               return NULL;
+
+       if (interface == NULL || interface->wps_cred.ssid == NULL) {
+               *ssid_len = 0;
+               return NULL;
+       }
+
+       *ssid_len = interface->wps_cred.ssid_len;
+       return interface->wps_cred.ssid;
+}
+
+GSupplicantWpsState g_supplicant_interface_get_wps_state(
+                                       GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return G_SUPPLICANT_WPS_STATE_UNKNOWN;
+
+       return interface->wps_state;
+}
+
 GSupplicantInterface *g_supplicant_network_get_interface(
                                        GSupplicantNetwork *network)
 {
@@ -707,6 +755,14 @@ dbus_int16_t g_supplicant_network_get_signal(GSupplicantNetwork *network)
        return network->signal;
 }
 
+dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return FALSE;
+
+       return network->wps;
+}
+
 static void merge_network(GSupplicantNetwork *network)
 {
        GString *str;
@@ -718,7 +774,7 @@ static void merge_network(GSupplicantNetwork *network)
        mode = g_hash_table_lookup(network->config_table, "mode");
        key_mgmt = g_hash_table_lookup(network->config_table, "key_mgmt");
 
-       debug("ssid %s mode %s", ssid, mode);
+       SUPPLICANT_DBG("ssid %s mode %s", ssid, mode);
 
        if (ssid != NULL)
                ssid_len = strlen(ssid);
@@ -733,7 +789,7 @@ static void merge_network(GSupplicantNetwork *network)
                g_string_append_printf(str, "%02x", ssid[i]);
 
        if (g_strcmp0(mode, "0") == 0)
-               g_string_append_printf(str, "_infra");
+               g_string_append_printf(str, "_managed");
        else if (g_strcmp0(mode, "1") == 0)
                g_string_append_printf(str, "_adhoc");
 
@@ -742,7 +798,7 @@ static void merge_network(GSupplicantNetwork *network)
 
        group = g_string_free(str, FALSE);
 
-       debug("%s", group);
+       SUPPLICANT_DBG("%s", group);
 
        g_free(group);
 
@@ -778,7 +834,7 @@ static void network_property(const char *key, DBusMessageIter *iter,
                                                g_strdup(key), g_strdup(str));
                }
        } else
-               debug("key %s type %c",
+               SUPPLICANT_DBG("key %s type %c",
                                key, dbus_message_iter_get_arg_type(iter));
 }
 
@@ -788,7 +844,7 @@ static void interface_network_added(DBusMessageIter *iter, void *user_data)
        GSupplicantNetwork *network;
        const char *path = NULL;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        dbus_message_iter_get_basic(iter, &path);
        if (path == NULL)
@@ -820,7 +876,7 @@ static void interface_network_added(DBusMessageIter *iter, void *user_data)
        }
 
        supplicant_dbus_property_get_all(path,
-                               SUPPLICANT_INTERFACE ".Interface.Network",
+                               SUPPLICANT_INTERFACE ".Network",
                                                network_property, network);
 }
 
@@ -924,6 +980,10 @@ static void add_bss_to_network(struct g_supplicant_bss *bss)
        memcpy(network->ssid, bss->ssid, bss->ssid_len);
        network->signal = bss->signal;
 
+       network->wps = FALSE;
+       if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0)
+               network->wps = TRUE;
+
        network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                        NULL, remove_bss);
 
@@ -1018,6 +1078,82 @@ static void bss_wpa(const char *key, DBusMessageIter *iter,
 
 }
 
+static unsigned int get_tlv(unsigned char *ie, unsigned int ie_size,
+                                                       unsigned int type)
+{
+       unsigned int len = 0;
+
+       while (len + 4 < ie_size) {
+               unsigned int hi = ie[len];
+               unsigned int lo = ie[len + 1];
+               unsigned int tmp_type = (hi << 8) + lo;
+               unsigned int v_len = 0;
+
+               /* hi and lo are used to recreate an unsigned int
+                * based on 2 8bits length unsigned int. */
+
+               hi = ie[len + 2];
+               lo = ie[len + 3];
+               v_len = (hi << 8) + lo;
+
+               if (tmp_type == type) {
+                       unsigned int ret_value = 0;
+                       unsigned char *value = (unsigned char *)&ret_value;
+
+                       SUPPLICANT_DBG("IE: match type 0x%x", type);
+
+                       /* Verifying length relevance */
+                       if (v_len > sizeof(unsigned int) ||
+                               len + 4 + v_len > ie_size)
+                               break;
+
+                       memcpy(value, ie + len + 4, v_len);
+
+                       SUPPLICANT_DBG("returning 0x%x", ret_value);
+                       return ret_value;
+               }
+
+               len += v_len + 4;
+       }
+
+       SUPPLICANT_DBG("returning 0");
+       return 0;
+}
+
+static void bss_process_ies(DBusMessageIter *iter, void *user_data)
+{
+       struct g_supplicant_bss *bss = user_data;
+       const unsigned char WPS_OUI[] = { 0x00, 0x50, 0xf2, 0x04 };
+       unsigned char *ie, *ie_end;
+       DBusMessageIter array;
+       int ie_len;
+
+#define WMM_WPA1_WPS_INFO 221
+#define WPS_INFO_MIN_LEN  6
+#define WPS_VERSION_TLV   0x104A
+#define WPS_STATE_TLV     0x1044
+#define WPS_VERSION       0x10
+
+       dbus_message_iter_recurse(iter, &array);
+       dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+       if (ie == NULL || ie_len < 2)
+               return;
+
+       for (ie_end = ie+ie_len; ie+ie[1]+1 <= ie_end; ie += ie[1]+2) {
+               if (ie[0] != WMM_WPA1_WPS_INFO || ie[1] < WPS_INFO_MIN_LEN ||
+                       memcmp(ie+2, WPS_OUI, sizeof(WPS_OUI)) != 0)
+                       continue;
+
+               SUPPLICANT_DBG("IE: match WPS_OUI");
+
+               if (get_tlv(&ie[6], ie[1],
+                               WPS_VERSION_TLV) == WPS_VERSION &&
+                       get_tlv(&ie[6], ie[1],
+                               WPS_STATE_TLV) != 0)
+                       bss->keymgmt |= G_SUPPLICANT_KEYMGMT_WPS;
+       }
+}
 
 static void bss_property(const char *key, DBusMessageIter *iter,
                                                        void *user_data)
@@ -1027,7 +1163,7 @@ static void bss_property(const char *key, DBusMessageIter *iter,
        if (bss->interface == NULL)
                return;
 
-       debug("key %s", key);
+       SUPPLICANT_DBG("key %s", key);
 
        if (key == NULL) {
                if (bss->ieee8021x == TRUE)
@@ -1128,52 +1264,82 @@ static void bss_property(const char *key, DBusMessageIter *iter,
                                G_SUPPLICANT_KEYMGMT_WPA_FT_PSK |
                                G_SUPPLICANT_KEYMGMT_WPA_PSK_256))
                        bss->psk = TRUE;
-       } else
-               debug("key %s type %c",
+       } else if (g_strcmp0(key, "IEs") == 0)
+               bss_process_ies(iter, bss);
+       else
+               SUPPLICANT_DBG("key %s type %c",
                                key, dbus_message_iter_get_arg_type(iter));
 }
 
-static void interface_bss_added(DBusMessageIter *iter, void *user_data)
+static struct g_supplicant_bss *interface_bss_added(DBusMessageIter *iter,
+                                                       void *user_data)
 {
        GSupplicantInterface *interface = user_data;
        GSupplicantNetwork *network;
        struct g_supplicant_bss *bss;
        const char *path = NULL;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        dbus_message_iter_get_basic(iter, &path);
        if (path == NULL)
-               return;
+               return NULL;
 
        if (g_strcmp0(path, "/") == 0)
-               return;
+               return NULL;
 
-       debug("%s", path);
+       SUPPLICANT_DBG("%s", path);
 
        network = g_hash_table_lookup(interface->bss_mapping, path);
        if (network != NULL) {
                bss = g_hash_table_lookup(network->bss_table, path);
                if (bss != NULL)
-                       return;
+                       return NULL;
        }
 
        bss = g_try_new0(struct g_supplicant_bss, 1);
        if (bss == NULL)
-               return;
+               return NULL;
 
        bss->interface = interface;
        bss->path = g_strdup(path);
 
+       return bss;
+}
+
+static void interface_bss_added_with_keys(DBusMessageIter *iter,
+                                               void *user_data)
+{
+       struct g_supplicant_bss *bss;
+
+       SUPPLICANT_DBG("");
+
+       bss = interface_bss_added(iter, user_data);
+       if (bss == NULL)
+               return;
+
        dbus_message_iter_next(iter);
-       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
-               supplicant_dbus_property_foreach(iter, bss_property, bss);
-               bss_property(NULL, NULL, bss);
+
+       if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_INVALID)
                return;
-       }
 
-       supplicant_dbus_property_get_all(path,
-                                       SUPPLICANT_INTERFACE ".Interface.BSS",
+       supplicant_dbus_property_foreach(iter, bss_property, bss);
+       bss_property(NULL, NULL, bss);
+}
+
+static void interface_bss_added_without_keys(DBusMessageIter *iter,
+                                               void *user_data)
+{
+       struct g_supplicant_bss *bss;
+
+       SUPPLICANT_DBG("");
+
+       bss = interface_bss_added(iter, user_data);
+       if (bss == NULL)
+               return;
+
+       supplicant_dbus_property_get_all(bss->path,
+                                       SUPPLICANT_INTERFACE ".BSS",
                                                        bss_property, bss);
 }
 
@@ -1208,7 +1374,7 @@ static void interface_property(const char *key, DBusMessageIter *iter,
        if (interface == NULL)
                return;
 
-       debug("%s", key);
+       SUPPLICANT_DBG("%s", key);
 
        if (key == NULL) {
                debug_strvalmap("KeyMgmt capability", keymgmt_map,
@@ -1244,7 +1410,7 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                                callback_interface_state(interface);
                        }
 
-               debug("state %s (%d)", str, interface->state);
+               SUPPLICANT_DBG("state %s (%d)", str, interface->state);
        } else if (g_strcmp0(key, "Scanning") == 0) {
                dbus_bool_t scanning = FALSE;
 
@@ -1266,26 +1432,32 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                const char *str = NULL;
 
                dbus_message_iter_get_basic(iter, &str);
-               if (str != NULL)
+               if (str != NULL) {
+                       g_free(interface->ifname);
                        interface->ifname = g_strdup(str);
+               }
        } else if (g_strcmp0(key, "Driver") == 0) {
                const char *str = NULL;
 
                dbus_message_iter_get_basic(iter, &str);
-               if (str != NULL)
+               if (str != NULL) {
+                       g_free(interface->driver);
                        interface->driver = g_strdup(str);
+               }
        } else if (g_strcmp0(key, "BridgeIfname") == 0) {
                const char *str = NULL;
 
                dbus_message_iter_get_basic(iter, &str);
-               if (str != NULL)
+               if (str != NULL) {
+                       g_free(interface->bridge);
                        interface->bridge = g_strdup(str);
+               }
        } else if (g_strcmp0(key, "CurrentBSS") == 0) {
-               interface_bss_added(iter, interface);
+               interface_bss_added_without_keys(iter, interface);
        } else if (g_strcmp0(key, "CurrentNetwork") == 0) {
                interface_network_added(iter, interface);
        } else if (g_strcmp0(key, "BSSs") == 0) {
-               supplicant_dbus_array_foreach(iter, interface_bss_added,
+               supplicant_dbus_array_foreach(iter, interface_bss_added_without_keys,
                                                                interface);
        } else if (g_strcmp0(key, "Blobs") == 0) {
                /* Nothing */
@@ -1293,10 +1465,49 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                supplicant_dbus_array_foreach(iter, interface_network_added,
                                                                interface);
        } else
-               debug("key %s type %c",
+               SUPPLICANT_DBG("key %s type %c",
                                key, dbus_message_iter_get_arg_type(iter));
 }
 
+static void scan_network_update(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       GSupplicantNetwork *network;
+       char *path;
+
+       if (iter == NULL)
+               return;
+
+       dbus_message_iter_get_basic(iter, &path);
+
+       if (path == NULL)
+               return;
+
+       if (g_strcmp0(path, "/") == 0)
+               return;
+
+       /* Update the network details based on scan BSS data */
+       network = g_hash_table_lookup(interface->bss_mapping, path);
+       if (network != NULL)
+               callback_network_added(network);
+}
+
+static void scan_bss_data(const char *key, DBusMessageIter *iter,
+                               void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+
+       if (iter)
+               supplicant_dbus_array_foreach(iter, scan_network_update,
+                                               interface);
+
+       if (interface->scan_callback != NULL)
+               interface->scan_callback(0, interface, interface->scan_data);
+
+       interface->scan_callback = NULL;
+       interface->scan_data = NULL;
+}
+
 static GSupplicantInterface *interface_alloc(const char *path)
 {
        GSupplicantInterface *interface;
@@ -1325,7 +1536,7 @@ static void interface_added(DBusMessageIter *iter, void *user_data)
        GSupplicantInterface *interface;
        const char *path = NULL;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        dbus_message_iter_get_basic(iter, &path);
        if (path == NULL)
@@ -1400,20 +1611,25 @@ static void service_property(const char *key, DBusMessageIter *iter,
                                debug_level = i;
                                break;
                        }
-               debug("Debug level %d", debug_level);
+               SUPPLICANT_DBG("Debug level %d", debug_level);
        } else if (g_strcmp0(key, "DebugTimestamp") == 0) {
                dbus_message_iter_get_basic(iter, &debug_timestamp);
-               debug("Debug timestamp %u", debug_timestamp);
+               SUPPLICANT_DBG("Debug timestamp %u", debug_timestamp);
        } else if (g_strcmp0(key, "DebugShowKeys") == 0) {
                dbus_message_iter_get_basic(iter, &debug_showkeys);
-               debug("Debug show keys %u", debug_showkeys);
+               SUPPLICANT_DBG("Debug show keys %u", debug_showkeys);
        } else if (g_strcmp0(key, "Interfaces") == 0) {
                supplicant_dbus_array_foreach(iter, interface_added, NULL);
        } else if (g_strcmp0(key, "EapMethods") == 0) {
                supplicant_dbus_array_foreach(iter, eap_method, NULL);
                debug_strvalmap("EAP method", eap_method_map, eap_methods);
+       } else if (g_strcmp0(key, "Country") == 0) {
+               const char *country = NULL;
+
+               dbus_message_iter_get_basic(iter, &country);
+               SUPPLICANT_DBG("Country %s", country);
        } else
-               debug("key %s type %c",
+               SUPPLICANT_DBG("key %s type %c",
                                key, dbus_message_iter_get_arg_type(iter));
 }
 
@@ -1421,7 +1637,7 @@ static void signal_name_owner_changed(const char *path, DBusMessageIter *iter)
 {
        const char *name = NULL, *old = NULL, *new = NULL;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        if (g_strcmp0(path, DBUS_PATH_DBUS) != 0)
                return;
@@ -1458,7 +1674,7 @@ static void signal_name_owner_changed(const char *path, DBusMessageIter *iter)
 
 static void signal_properties_changed(const char *path, DBusMessageIter *iter)
 {
-       debug("");
+       SUPPLICANT_DBG("");
 
        if (g_strcmp0(path, SUPPLICANT_PATH) != 0)
                return;
@@ -1468,7 +1684,7 @@ static void signal_properties_changed(const char *path, DBusMessageIter *iter)
 
 static void signal_interface_added(const char *path, DBusMessageIter *iter)
 {
-       debug("path %s %s", path, SUPPLICANT_PATH);
+       SUPPLICANT_DBG("path %s %s", path, SUPPLICANT_PATH);
 
        if (g_strcmp0(path, SUPPLICANT_PATH) == 0)
                interface_added(iter, NULL);
@@ -1476,7 +1692,7 @@ static void signal_interface_added(const char *path, DBusMessageIter *iter)
 
 static void signal_interface_removed(const char *path, DBusMessageIter *iter)
 {
-       debug("");
+       SUPPLICANT_DBG("");
 
        if (g_strcmp0(path, SUPPLICANT_PATH) == 0)
                interface_removed(iter, NULL);
@@ -1486,7 +1702,7 @@ static void signal_interface_changed(const char *path, DBusMessageIter *iter)
 {
        GSupplicantInterface *interface;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        interface = g_hash_table_lookup(interface_table, path);
        if (interface == NULL)
@@ -1500,7 +1716,7 @@ static void signal_scan_done(const char *path, DBusMessageIter *iter)
        GSupplicantInterface *interface;
        dbus_bool_t success = FALSE;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        interface = g_hash_table_lookup(interface_table, path);
        if (interface == NULL)
@@ -1508,38 +1724,43 @@ static void signal_scan_done(const char *path, DBusMessageIter *iter)
 
        dbus_message_iter_get_basic(iter, &success);
 
-       if (interface->scan_callback != NULL) {
-               int result = 0;
+       /*
+        * If scan is unsuccessful return -EIO else get the scanned BSSs
+        * and update the network details accordingly
+        */
+       if (success == FALSE) {
+               if (interface->scan_callback != NULL)
+                       interface->scan_callback(-EIO, interface,
+                                               interface->scan_data);
 
-               if (success == FALSE)
-                       result = -EIO;
+               interface->scan_callback = NULL;
+               interface->scan_data = NULL;
 
-               interface->scan_callback(result, interface,
-                                               interface->scan_data);
+               return;
        }
 
-       interface->scan_callback = NULL;
-       interface->scan_data = NULL;
+       supplicant_dbus_property_get(path, SUPPLICANT_INTERFACE ".Interface",
+                                       "BSSs", scan_bss_data, interface);
 }
 
 static void signal_bss_added(const char *path, DBusMessageIter *iter)
 {
        GSupplicantInterface *interface;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        interface = g_hash_table_lookup(interface_table, path);
        if (interface == NULL)
                return;
 
-       interface_bss_added(iter, interface);
+       interface_bss_added_with_keys(iter, interface);
 }
 
 static void signal_bss_removed(const char *path, DBusMessageIter *iter)
 {
        GSupplicantInterface *interface;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        interface = g_hash_table_lookup(interface_table, path);
        if (interface == NULL)
@@ -1552,7 +1773,7 @@ static void signal_network_added(const char *path, DBusMessageIter *iter)
 {
        GSupplicantInterface *interface;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        interface = g_hash_table_lookup(interface_table, path);
        if (interface == NULL)
@@ -1565,7 +1786,7 @@ static void signal_network_removed(const char *path, DBusMessageIter *iter)
 {
        GSupplicantInterface *interface;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        interface = g_hash_table_lookup(interface_table, path);
        if (interface == NULL)
@@ -1580,7 +1801,7 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter)
        GSupplicantNetwork *network;
        struct g_supplicant_bss *bss;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        interface = g_hash_table_lookup(bss_mapping, path);
        if (interface == NULL)
@@ -1597,6 +1818,106 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter)
        supplicant_dbus_property_foreach(iter, bss_property, bss);
 }
 
+static void wps_credentials(const char *key, DBusMessageIter *iter,
+                       void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+
+       if (key == NULL)
+               return;
+
+       SUPPLICANT_DBG("key %s", key);
+
+       if (g_strcmp0(key, "Key") == 0) {
+               DBusMessageIter array;
+               unsigned char *key;
+               int key_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &key, &key_len);
+
+               g_free(interface->wps_cred.key);
+               interface->wps_cred.key = g_try_malloc0(
+                                               sizeof(char) * key_len+1);
+
+               if (interface->wps_cred.key == NULL)
+                       return;
+
+               memcpy(interface->wps_cred.key, key, sizeof(char) * key_len);
+
+               SUPPLICANT_DBG("WPS key present");
+       } else if (g_strcmp0(key, "SSID") == 0) {
+               DBusMessageIter array;
+               unsigned char *ssid;
+               int ssid_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
+
+               if (ssid_len > 0 && ssid_len < 33) {
+                       memcpy(interface->wps_cred.ssid, ssid, ssid_len);
+                       interface->wps_cred.ssid_len = ssid_len;
+               } else {
+                       memset(interface->wps_cred.ssid, 0, 32);
+                       interface->wps_cred.ssid_len = 0;
+               }
+       }
+}
+
+static void signal_wps_credentials(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       supplicant_dbus_property_foreach(iter, wps_credentials, interface);
+}
+
+static void wps_event_args(const char *key, DBusMessageIter *iter,
+                       void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+
+       if (key == NULL || interface == NULL)
+               return;
+
+       SUPPLICANT_DBG("Arg Key %s", key);
+}
+
+static void signal_wps_event(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+       const char *name = NULL;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       dbus_message_iter_get_basic(iter, &name);
+
+       SUPPLICANT_DBG("Name: %s", name);
+
+       if (g_strcmp0(name, "success") == 0)
+               interface->wps_state = G_SUPPLICANT_WPS_STATE_SUCCESS;
+       else if (g_strcmp0(name, "failed") == 0)
+               interface->wps_state = G_SUPPLICANT_WPS_STATE_FAIL;
+       else
+               interface->wps_state = G_SUPPLICANT_WPS_STATE_UNKNOWN;
+
+       if (!dbus_message_iter_has_next(iter))
+               return;
+
+       dbus_message_iter_next(iter);
+
+       supplicant_dbus_property_foreach(iter, wps_event_args, interface);
+}
+
 static struct {
        const char *interface;
        const char *member;
@@ -1616,7 +1937,10 @@ static struct {
        { SUPPLICANT_INTERFACE ".Interface", "NetworkAdded",      signal_network_added     },
        { SUPPLICANT_INTERFACE ".Interface", "NetworkRemoved",    signal_network_removed   },
 
-       { SUPPLICANT_INTERFACE ".Interface.BSS", "PropertiesChanged", signal_bss_changed   },
+       { SUPPLICANT_INTERFACE ".BSS", "PropertiesChanged", signal_bss_changed   },
+
+       { SUPPLICANT_INTERFACE ".Interface.WPS", "Credentials", signal_wps_credentials },
+       { SUPPLICANT_INTERFACE ".Interface.WPS", "Event",       signal_wps_event       },
 
        { }
 };
@@ -1651,6 +1975,69 @@ static DBusHandlerResult g_supplicant_filter(DBusConnection *conn,
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
+struct supplicant_regdom {
+       GSupplicantCountryCallback callback;
+       const void *user_data;
+};
+
+static void country_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_regdom *regdom = user_data;
+       char *alpha2;
+
+       SUPPLICANT_DBG("Country setting result");
+
+       if (user_data == NULL)
+               return;
+
+       if (error == NULL) {
+               alpha2 = (char *)regdom->user_data;
+       } else {
+               SUPPLICANT_DBG("Country setting failure %s", error);
+               alpha2 = NULL;
+       }
+
+       if (regdom->callback)
+               regdom->callback(alpha2);
+
+       g_free(regdom);
+}
+
+static void country_params(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_regdom *regdom = user_data;
+       const char *country;
+
+       country = regdom->user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &country);
+}
+
+int g_supplicant_set_country(const char *alpha2,
+                               GSupplicantCountryCallback callback,
+                                       const void *user_data)
+{
+       struct supplicant_regdom *regdom;
+
+       SUPPLICANT_DBG("Country setting %s", alpha2);
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       regdom = dbus_malloc0(sizeof(*regdom));
+       if (regdom == NULL)
+               return -ENOMEM;
+
+       regdom->callback = callback;
+       regdom->user_data = user_data;
+
+       return supplicant_dbus_property_set(SUPPLICANT_PATH, SUPPLICANT_INTERFACE,
+                                       "Country", DBUS_TYPE_STRING_AS_STRING,
+                                       country_params, country_result,
+                                               regdom);
+}
+
 struct interface_data {
        GSupplicantInterface *interface;
        GSupplicantInterfaceCallback callback;
@@ -1695,7 +2082,7 @@ static void interface_create_result(const char *error,
        const char *path = NULL;
        int err;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        if (error != NULL) {
                g_critical("error %s", error);
@@ -1741,7 +2128,7 @@ static void interface_create_params(DBusMessageIter *iter, void *user_data)
        struct interface_create_data *data = user_data;
        DBusMessageIter dict;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        supplicant_dbus_dict_open(iter, &dict);
 
@@ -1763,10 +2150,10 @@ static void interface_get_result(const char *error,
        const char *path = NULL;
        int err;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        if (error != NULL) {
-               g_warning("error %s", error);
+               SUPPLICANT_DBG("Interface not created yet");
                err = -EIO;
                goto create;
        }
@@ -1796,7 +2183,7 @@ create:
                goto done;
        }
 
-       debug("Creating interface");
+       SUPPLICANT_DBG("Creating interface");
 
        err = supplicant_dbus_method_call(SUPPLICANT_PATH,
                                                SUPPLICANT_INTERFACE,
@@ -1817,7 +2204,7 @@ static void interface_get_params(DBusMessageIter *iter, void *user_data)
 {
        struct interface_create_data *data = user_data;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->ifname);
 }
@@ -1828,7 +2215,7 @@ int g_supplicant_interface_create(const char *ifname, const char *driver,
 {
        struct interface_create_data *data;
 
-       debug("ifname %s", ifname);
+       SUPPLICANT_DBG("ifname %s", ifname);
 
        if (ifname == NULL)
                return -EINVAL;
@@ -1868,12 +2255,15 @@ static void interface_remove_result(const char *error,
                goto done;
        }
 
-       g_hash_table_remove(interface_table, data->interface->path);
+       /*
+        * The gsupplicant interface is already freed by the InterfaceRemoved
+        * signal callback. Simply invoke the interface_data callback.
+        */
        err = 0;
 
 done:
        if (data->callback != NULL)
-               data->callback(err, data->interface, data->user_data);
+               data->callback(err, NULL, data->user_data);
 
        dbus_free(data);
 }
@@ -1959,6 +2349,21 @@ int g_supplicant_interface_scan(GSupplicantInterface *interface,
        if (interface->scanning == TRUE)
                return -EALREADY;
 
+       switch (interface->state) {
+       case G_SUPPLICANT_STATE_AUTHENTICATING:
+       case G_SUPPLICANT_STATE_ASSOCIATING:
+       case G_SUPPLICANT_STATE_ASSOCIATED:
+       case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
+       case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
+               return -EBUSY;
+       case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISCONNECTED:
+       case G_SUPPLICANT_STATE_INACTIVE:
+       case G_SUPPLICANT_STATE_SCANNING:
+       case G_SUPPLICANT_STATE_COMPLETED:
+               break;
+       }
+
        data = dbus_malloc0(sizeof(*data));
        if (data == NULL)
                return -ENOMEM;
@@ -1975,39 +2380,55 @@ int g_supplicant_interface_scan(GSupplicantInterface *interface,
 static void interface_select_network_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
-       debug("");
+       struct interface_connect_data *data = user_data;
+
+       SUPPLICANT_DBG("");
+
+       g_free(data->ssid);
+       dbus_free(data);
 }
 
 static void interface_select_network_params(DBusMessageIter *iter,
                                                        void *user_data)
 {
-       char *path = user_data;
+       struct interface_connect_data *data = user_data;
+       GSupplicantInterface *interface = data->interface;
 
-       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                       &interface->network_path);
 }
 
 static void interface_add_network_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
        struct interface_connect_data *data = user_data;
-       char *path = NULL;
+       GSupplicantInterface *interface = data->interface;
+       const char *path;
 
        if (error != NULL)
-               goto done;
+               goto error;
 
        dbus_message_iter_get_basic(iter, &path);
        if (path == NULL)
-               goto done;
+               goto error;
+
+       SUPPLICANT_DBG("PATH: %s", path);
 
-       debug("PATH: %s", path);
+       g_free(interface->network_path);
+       interface->network_path = g_strdup(path);
 
        supplicant_dbus_method_call(data->interface->path,
                        SUPPLICANT_INTERFACE ".Interface", "SelectNetwork",
                        interface_select_network_params,
-                       interface_select_network_result, path);
+                       interface_select_network_result, data);
 
-done:
-       dbus_free(data);
+       return;
+
+error:
+       g_free(interface->network_path);
+       interface->network_path = NULL;
+       g_free(data->ssid);
+       g_free(data);
 }
 
 static void add_network_security_wep(DBusMessageIter *dict,
@@ -2156,7 +2577,11 @@ static void add_network_security_peap(DBusMessageIter *dict,
 
        }
 
-       phase2_auth = g_strdup_printf("\"auth=%s\"", ssid->phase2_auth);
+       if (g_str_has_prefix(ssid->phase2_auth, "EAP-") == TRUE) {
+               phase2_auth = g_strdup_printf("autheap=%s",
+                                       ssid->phase2_auth + strlen("EAP-"));
+       } else
+               phase2_auth = g_strdup_printf("auth=%s", ssid->phase2_auth);
 
        supplicant_dbus_dict_append_basic(dict, "password",
                                                DBUS_TYPE_STRING,
@@ -2168,7 +2593,7 @@ static void add_network_security_peap(DBusMessageIter *dict,
 
        supplicant_dbus_dict_append_basic(dict, "phase2",
                                                DBUS_TYPE_STRING,
-                                               &ssid->phase2_auth);
+                                               &phase2_auth);
 
        g_free(phase2_auth);
 }
@@ -2176,6 +2601,8 @@ static void add_network_security_peap(DBusMessageIter *dict,
 static void add_network_security_eap(DBusMessageIter *dict,
                                        GSupplicantSSID *ssid)
 {
+       char *eap_value;
+
        if (ssid->eap == NULL || ssid->identity == NULL)
                return;
 
@@ -2187,12 +2614,16 @@ static void add_network_security_eap(DBusMessageIter *dict,
        } else
                return;
 
+       eap_value = g_ascii_strup(ssid->eap, -1);
+
        supplicant_dbus_dict_append_basic(dict, "eap",
                                                DBUS_TYPE_STRING,
-                                               &ssid->eap);
+                                               &eap_value);
        supplicant_dbus_dict_append_basic(dict, "identity",
                                                DBUS_TYPE_STRING,
                                                &ssid->identity);
+
+       g_free(eap_value);
 }
 
 static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
@@ -2276,13 +2707,13 @@ int g_supplicant_interface_connect(GSupplicantInterface *interface,
        return -EINPROGRESS;
 }
 
-static void interface_disconnect_result(const char *error,
+static void network_remove_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
        struct interface_data *data = user_data;
        int result = 0;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        if (error != NULL)
                result = -EIO;
@@ -2293,13 +2724,47 @@ static void interface_disconnect_result(const char *error,
        dbus_free(data);
 }
 
+static void network_remove_params(DBusMessageIter *iter, void *user_data)
+{
+       struct interface_data *data = user_data;
+       const char *path = data->interface->network_path;
+
+       SUPPLICANT_DBG("path %s", path);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static int network_remove(struct interface_data *data)
+{
+       GSupplicantInterface *interface = data->interface;
+
+       SUPPLICANT_DBG("");
+
+       return supplicant_dbus_method_call(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface", "RemoveNetwork",
+                       network_remove_params, network_remove_result, data);
+}
+
+static void interface_disconnect_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_data *data = user_data;
+
+       SUPPLICANT_DBG("");
+
+       if (error != NULL && data->callback != NULL)
+               data->callback(-EIO, data->interface, data->user_data);
+
+       network_remove(data);
+}
+
 int g_supplicant_interface_disconnect(GSupplicantInterface *interface,
                                        GSupplicantInterfaceCallback callback,
                                                        void *user_data)
 {
        struct interface_data *data;
 
-       debug("");
+       SUPPLICANT_DBG("");
 
        if (interface == NULL)
                return -EINVAL;
@@ -2334,11 +2799,26 @@ static const char *g_supplicant_rule2 = "type=signal,"
 static const char *g_supplicant_rule3 = "type=signal,"
                        "interface=" SUPPLICANT_INTERFACE ".Interface.WPS";
 static const char *g_supplicant_rule4 = "type=signal,"
-                       "interface=" SUPPLICANT_INTERFACE ".Interface.BSS";
+                       "interface=" SUPPLICANT_INTERFACE ".BSS";
 static const char *g_supplicant_rule5 = "type=signal,"
-                       "interface=" SUPPLICANT_INTERFACE ".Interface.Network";
-static const char *g_supplicant_rule6 = "type=signal,"
-                       "interface=" SUPPLICANT_INTERFACE ".Interface.Blob";
+                       "interface=" SUPPLICANT_INTERFACE ".Network";
+
+static void invoke_introspect_method(void)
+{
+       DBusMessage *message;
+
+       message = dbus_message_new_method_call(SUPPLICANT_SERVICE,
+                                       SUPPLICANT_PATH,
+                                       DBUS_INTERFACE_INTROSPECTABLE,
+                                       "Introspect");
+
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+       dbus_connection_send(connection, message, NULL);
+       dbus_message_unref(message);
+}
 
 int g_supplicant_register(const GSupplicantCallbacks *callbacks)
 {
@@ -2370,7 +2850,6 @@ int g_supplicant_register(const GSupplicantCallbacks *callbacks)
        dbus_bus_add_match(connection, g_supplicant_rule3, NULL);
        dbus_bus_add_match(connection, g_supplicant_rule4, NULL);
        dbus_bus_add_match(connection, g_supplicant_rule5, NULL);
-       dbus_bus_add_match(connection, g_supplicant_rule6, NULL);
        dbus_connection_flush(connection);
 
        if (dbus_bus_name_has_owner(connection,
@@ -2379,7 +2858,8 @@ int g_supplicant_register(const GSupplicantCallbacks *callbacks)
                supplicant_dbus_property_get_all(SUPPLICANT_PATH,
                                                SUPPLICANT_INTERFACE,
                                                service_property, NULL);
-       }
+       } else
+               invoke_introspect_method();
 
        return 0;
 }
@@ -2408,10 +2888,9 @@ static void unregister_remove_interface(gpointer key, gpointer value,
 
 void g_supplicant_unregister(const GSupplicantCallbacks *callbacks)
 {
-       debug("");
+       SUPPLICANT_DBG("");
 
        if (connection != NULL) {
-               dbus_bus_remove_match(connection, g_supplicant_rule6, NULL);
                dbus_bus_remove_match(connection, g_supplicant_rule5, NULL);
                dbus_bus_remove_match(connection, g_supplicant_rule4, NULL);
                dbus_bus_remove_match(connection, g_supplicant_rule3, NULL);
@@ -2429,6 +2908,9 @@ void g_supplicant_unregister(const GSupplicantCallbacks *callbacks)
                bss_mapping = NULL;
        }
 
+       if (system_available == TRUE)
+               callback_system_killed();
+
        if (interface_table != NULL) {
                g_hash_table_foreach(interface_table,   
                                        unregister_remove_interface, NULL);
@@ -2436,9 +2918,6 @@ void g_supplicant_unregister(const GSupplicantCallbacks *callbacks)
                interface_table = NULL;
        }
 
-       if (system_available == TRUE)
-               callback_system_killed();
-
        if (connection != NULL) {
                dbus_connection_unref(connection);
                connection = NULL;