+ dbus_message_iter_init_append(message, &array);
+
+ dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ if (address == NULL) {
+ dbus_uint32_t scan_ssid = 1;
+ connman_dbus_dict_append_variant(&dict, "scan_ssid",
+ DBUS_TYPE_UINT32, &scan_ssid);
+ } else
+ connman_dbus_dict_append_variant(&dict, "bssid",
+ DBUS_TYPE_STRING, &address);
+
+ connman_dbus_dict_append_array(&dict, "ssid",
+ DBUS_TYPE_BYTE, &network, len);
+
+ if (g_ascii_strcasecmp(security, "wpa") == 0 ||
+ g_ascii_strcasecmp(security, "wpa2") == 0) {
+ const char *key_mgmt = "WPA-PSK";
+ connman_dbus_dict_append_variant(&dict, "key_mgmt",
+ DBUS_TYPE_STRING, &key_mgmt);
+
+ if (passphrase && strlen(passphrase) > 0)
+ connman_dbus_dict_append_variant(&dict, "psk",
+ DBUS_TYPE_STRING, &passphrase);
+ } else if (g_ascii_strcasecmp(security, "wep") == 0) {
+ const char *key_mgmt = "NONE", *index = "0";
+ connman_dbus_dict_append_variant(&dict, "key_mgmt",
+ DBUS_TYPE_STRING, &key_mgmt);
+
+ if (passphrase) {
+ int size = strlen(passphrase);
+ if (size == 10 || size == 26) {
+ unsigned char *key = malloc(13);
+ char tmp[3];
+ int i;
+ memset(tmp, 0, sizeof(tmp));
+ if (key == NULL)
+ size = 0;
+ for (i = 0; i < size / 2; i++) {
+ memcpy(tmp, passphrase + (i * 2), 2);
+ key[i] = (unsigned char) strtol(tmp,
+ NULL, 16);
+ }
+ connman_dbus_dict_append_array(&dict,
+ "wep_key0", DBUS_TYPE_BYTE,
+ &key, size / 2);
+ free(key);
+ } else
+ connman_dbus_dict_append_variant(&dict,
+ "wep_key0", DBUS_TYPE_STRING,
+ &passphrase);
+ connman_dbus_dict_append_variant(&dict, "wep_tx_keyidx",
+ DBUS_TYPE_STRING, &index);
+ }
+ } else {
+ const char *key_mgmt = "NONE";
+ connman_dbus_dict_append_variant(&dict, "key_mgmt",
+ DBUS_TYPE_STRING, &key_mgmt);
+ }
+
+ dbus_message_iter_close_container(&array, &dict);
+
+ dbus_error_init(&error);
+
+ reply = dbus_connection_send_with_reply_and_block(connection,
+ message, -1, &error);
+ if (reply == NULL) {
+ if (dbus_error_is_set(&error) == TRUE) {
+ connman_error("%s", error.message);
+ dbus_error_free(&error);
+ } else
+ connman_error("Failed to set network options");
+ dbus_message_unref(message);
+ return -EIO;
+ }
+
+ dbus_message_unref(message);
+
+ dbus_message_unref(reply);
+
+ return 0;
+}
+
+static int initiate_scan(struct supplicant_task *task)
+{
+ DBusMessage *message;
+ DBusPendingCall *call;
+
+ DBG("task %p", task);
+
+ message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+ SUPPLICANT_INTF ".Interface", "scan");
+ if (message == NULL)
+ return -ENOMEM;
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &call, TIMEOUT) == FALSE) {
+ connman_error("Failed to initiate scan");
+ dbus_message_unref(message);
+ return -EIO;
+ }
+
+ dbus_message_unref(message);
+
+ return 0;
+}
+
+static char *build_group(const char *addr,
+ const unsigned char *ssid, unsigned int ssid_len,
+ const char *mode, const char *security)
+{
+ GString *str;
+ unsigned int i;
+
+ if (ssid_len > 0 && ssid[0] != '\0') {
+ str = g_string_sized_new((ssid_len * 2) + 24);
+ if (str == NULL)
+ return NULL;
+
+ for (i = 0; i < ssid_len; i++)
+ g_string_append_printf(str, "%02x", ssid[i]);
+ } else {
+ if (addr == NULL)
+ return NULL;
+
+ str = g_string_sized_new(15 + 24);
+ if (str == NULL)
+ return NULL;
+
+ g_string_append_printf(str, "hidden_%s", addr);
+ }
+
+ g_string_append_printf(str, "_%s_%s", mode, security);
+
+ return g_string_free(str, FALSE);
+}
+
+static void extract_addr(DBusMessageIter *value,
+ struct supplicant_result *result)
+{
+ DBusMessageIter array;
+ struct ether_addr *eth;
+ unsigned char *addr;
+ int addr_len;
+
+ dbus_message_iter_recurse(value, &array);
+ dbus_message_iter_get_fixed_array(&array, &addr, &addr_len);
+
+ if (addr_len != 6)
+ return;
+
+ eth = (void *) addr;
+
+ result->addr = g_try_malloc0(18);
+ if (result->addr == NULL)
+ return;
+
+ snprintf(result->addr, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
+ eth->ether_addr_octet[0],
+ eth->ether_addr_octet[1],
+ eth->ether_addr_octet[2],
+ eth->ether_addr_octet[3],
+ eth->ether_addr_octet[4],
+ eth->ether_addr_octet[5]);
+
+ result->path = g_try_malloc0(18);
+ if (result->path == NULL)
+ return;
+
+ snprintf(result->path, 18, "%02x%02x%02x%02x%02x%02x",
+ eth->ether_addr_octet[0],
+ eth->ether_addr_octet[1],
+ eth->ether_addr_octet[2],
+ eth->ether_addr_octet[3],
+ eth->ether_addr_octet[4],
+ eth->ether_addr_octet[5]);
+}
+
+static void extract_ssid(DBusMessageIter *value,
+ struct supplicant_result *result)
+{
+ DBusMessageIter array;
+ unsigned char *ssid;
+ int ssid_len;
+
+ dbus_message_iter_recurse(value, &array);
+ dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
+
+ if (ssid_len < 1)
+ return;
+
+ if (ssid[0] == '\0')
+ return;
+
+ result->ssid = g_try_malloc(ssid_len);
+ if (result->ssid == NULL)
+ return;
+
+ memcpy(result->ssid, ssid, ssid_len);
+ result->ssid_len = ssid_len;
+
+ result->name = g_try_malloc0(ssid_len + 1);
+ if (result->name == NULL)
+ return;
+
+ memcpy(result->name, ssid, ssid_len);
+}
+
+static void extract_wpaie(DBusMessageIter *value,
+ struct supplicant_result *result)
+{
+ DBusMessageIter array;
+ unsigned char *ie;
+ int ie_len;
+
+ dbus_message_iter_recurse(value, &array);
+ dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+ if (ie_len > 0)
+ result->has_wpa = TRUE;
+}
+
+static void extract_rsnie(DBusMessageIter *value,
+ struct supplicant_result *result)
+{
+ DBusMessageIter array;
+ unsigned char *ie;
+ int ie_len;
+
+ dbus_message_iter_recurse(value, &array);
+ dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+ if (ie_len > 0)
+ result->has_rsn = TRUE;
+}
+
+static void extract_wpsie(DBusMessageIter *value,
+ struct supplicant_result *result)
+{
+ DBusMessageIter array;
+ unsigned char *ie;
+ int ie_len;
+
+ dbus_message_iter_recurse(value, &array);
+ dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+ if (ie_len > 0)
+ result->has_wps = TRUE;
+}
+
+static void extract_capabilites(DBusMessageIter *value,
+ struct supplicant_result *result)
+{
+ dbus_message_iter_get_basic(value, &result->capabilities);
+
+ if (result->capabilities & IEEE80211_CAP_ESS)
+ result->adhoc = FALSE;
+ else if (result->capabilities & IEEE80211_CAP_IBSS)
+ result->adhoc = TRUE;
+
+ if (result->capabilities & IEEE80211_CAP_PRIVACY)
+ result->has_wep = TRUE;
+}
+
+static void get_properties(struct supplicant_task *task);
+
+static void properties_reply(DBusPendingCall *call, void *user_data)
+{
+ struct supplicant_task *task = user_data;
+ struct supplicant_result result;
+ struct connman_network *network;
+ DBusMessage *reply;
+ DBusMessageIter array, dict;
+ unsigned char strength;
+ const char *mode, *security;
+ char *group;
+
+ DBG("task %p", task);
+
+ reply = dbus_pending_call_steal_reply(call);
+ if (reply == NULL) {
+ get_properties(task);
+ return;
+ }
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+ dbus_message_unref(reply);
+ get_properties(task);
+ return;
+ }
+
+ memset(&result, 0, sizeof(result));
+
+ dbus_message_iter_init(reply, &array);
+
+ dbus_message_iter_recurse(&array, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value;
+ const char *key;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+
+ dbus_message_iter_recurse(&entry, &value);
+
+ //type = dbus_message_iter_get_arg_type(&value);
+ //dbus_message_iter_get_basic(&value, &val);
+
+ /*
+ * bssid : a (97)
+ * ssid : a (97)
+ * wpaie : a (97)
+ * rsnie : a (97)
+ * wpsie : a (97)
+ * frequency : i (105)
+ * capabilities : q (113)
+ * quality : i (105)
+ * noise : i (105)
+ * level : i (105)
+ * maxrate : i (105)
+ */
+
+ if (g_str_equal(key, "bssid") == TRUE)
+ extract_addr(&value, &result);
+ else if (g_str_equal(key, "ssid") == TRUE)
+ extract_ssid(&value, &result);
+ else if (g_str_equal(key, "wpaie") == TRUE)
+ extract_wpaie(&value, &result);
+ else if (g_str_equal(key, "rsnie") == TRUE)
+ extract_rsnie(&value, &result);
+ else if (g_str_equal(key, "wpsie") == TRUE)
+ extract_wpsie(&value, &result);
+ else if (g_str_equal(key, "capabilities") == TRUE)
+ extract_capabilites(&value, &result);
+ else if (g_str_equal(key, "quality") == TRUE)
+ dbus_message_iter_get_basic(&value, &result.quality);
+ else if (g_str_equal(key, "noise") == TRUE)
+ dbus_message_iter_get_basic(&value, &result.noise);
+ else if (g_str_equal(key, "level") == TRUE)
+ dbus_message_iter_get_basic(&value, &result.level);
+ else if (g_str_equal(key, "maxrate") == TRUE)
+ dbus_message_iter_get_basic(&value, &result.maxrate);
+
+ dbus_message_iter_next(&dict);
+ }
+
+ if (result.path == NULL)
+ goto done;
+
+ if (result.path[0] == '\0')
+ goto done;
+
+ strength = result.quality;
+
+ if (result.has_rsn == TRUE)
+ security = "wpa2";
+ else if (result.has_wpa == TRUE)
+ security = "wpa";
+ else if (result.has_wep == TRUE)
+ security = "wep";
+ else
+ security = "none";
+
+ mode = (result.adhoc == TRUE) ? "adhoc" : "managed";
+
+ group = build_group(result.path, result.ssid, result.ssid_len,
+ mode, security);
+
+ network = connman_device_get_network(task->device, result.path);
+ if (network == NULL) {
+ int index;
+
+ network = connman_network_create(result.path,
+ CONNMAN_NETWORK_TYPE_WIFI);
+ if (network == NULL)
+ goto done;
+
+ index = connman_device_get_index(task->device);
+ connman_network_set_index(network, index);
+
+ connman_network_set_protocol(network,
+ CONNMAN_NETWORK_PROTOCOL_IP);
+
+ connman_network_set_string(network, "Address", result.addr);
+
+ connman_network_set_group(network, group);
+
+ if (result.name != NULL && result.name[0] != '\0')
+ connman_network_set_string(network, "Name", result.name);
+
+ connman_network_set_uint8(network, "Strength", strength);
+
+ connman_network_set_string(network, "WiFi.Mode", mode);
+ connman_network_set_string(network, "WiFi.Security", security);
+
+ if (connman_device_add_network(task->device, network) < 0) {
+ connman_network_unref(network);
+ goto done;
+ }
+ }
+
+ connman_network_set_group(network, group);
+
+ g_free(group);
+
+ if (result.name != NULL && result.name[0] != '\0')
+ connman_network_set_string(network, "Name", result.name);
+
+ connman_network_set_blob(network, "WiFi.SSID",
+ result.ssid, result.ssid_len);
+
+ connman_network_set_string(network, "WiFi.Mode", mode);
+
+ DBG("%s (%s %s) strength %d (%s)",
+ result.name, mode, security, strength,
+ (result.has_wps == TRUE) ? "WPS" : "no WPS");
+
+ connman_network_set_available(network, TRUE);
+ connman_network_set_uint8(network, "Strength", strength);
+
+ connman_network_set_string(network, "WiFi.Security", security);
+
+done:
+ g_free(result.path);
+ g_free(result.addr);
+ g_free(result.name);
+ g_free(result.ssid);
+
+ dbus_message_unref(reply);
+
+ get_properties(task);
+}
+
+static void get_properties(struct supplicant_task *task)
+{
+ DBusMessage *message;
+ DBusPendingCall *call;
+ char *path;
+
+ path = g_slist_nth_data(task->scan_results, 0);
+ if (path == NULL)
+ goto noscan;
+
+ message = dbus_message_new_method_call(SUPPLICANT_NAME, path,
+ SUPPLICANT_INTF ".BSSID",
+ "properties");
+
+ task->scan_results = g_slist_remove(task->scan_results, path);
+ g_free(path);
+
+ if (message == NULL)
+ goto noscan;
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &call, TIMEOUT) == FALSE) {
+ connman_error("Failed to get network properties");
+ dbus_message_unref(message);
+ goto noscan;
+ }
+
+ if (call == NULL) {
+ connman_error("D-Bus connection not available");
+ dbus_message_unref(message);
+ goto noscan;
+ }
+
+ dbus_pending_call_set_notify(call, properties_reply, task, NULL);
+
+ dbus_message_unref(message);
+
+ return;
+
+noscan:
+ if (task->noscan == FALSE)
+ connman_device_set_scanning(task->device, FALSE);
+}
+
+static void scan_results_reply(DBusPendingCall *call, void *user_data)
+{
+ struct supplicant_task *task = user_data;
+ DBusMessage *reply;
+ DBusError error;
+ char **results;
+ int i, num_results;
+
+ DBG("task %p", task);
+
+ reply = dbus_pending_call_steal_reply(call);
+ if (reply == NULL)
+ goto noscan;
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+ goto done;
+
+ dbus_error_init(&error);
+
+ if (dbus_message_get_args(reply, &error,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
+ &results, &num_results,
+ DBUS_TYPE_INVALID) == FALSE) {
+ if (dbus_error_is_set(&error) == TRUE) {
+ connman_error("%s", error.message);
+ dbus_error_free(&error);
+ } else
+ connman_error("Wrong arguments for scan result");
+ goto done;
+ }
+
+ if (num_results == 0)
+ goto done;
+
+ for (i = 0; i < num_results; i++) {
+ char *path = g_strdup(results[i]);
+ if (path == NULL)
+ continue;
+
+ task->scan_results = g_slist_append(task->scan_results, path);
+ }
+
+ g_strfreev(results);
+
+ dbus_message_unref(reply);
+
+ get_properties(task);
+
+ return;
+
+done:
+ dbus_message_unref(reply);
+
+noscan:
+ if (task->noscan == FALSE)
+ connman_device_set_scanning(task->device, FALSE);
+}
+
+static void scan_results_available(struct supplicant_task *task)
+{
+ DBusMessage *message;
+ DBusPendingCall *call;
+
+ DBG("task %p", task);
+
+ message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+ SUPPLICANT_INTF ".Interface",
+ "scanResults");
+ if (message == NULL)
+ return;
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &call, TIMEOUT) == FALSE) {
+ connman_error("Failed to request scan result");
+ goto done;
+ }
+
+ if (task->noscan == FALSE)
+ connman_device_set_scanning(task->device, TRUE);
+
+ if (call == NULL) {
+ connman_error("D-Bus connection not available");
+ goto done;
+ }
+
+ dbus_pending_call_set_notify(call, scan_results_reply, task, NULL);
+
+done:
+ dbus_message_unref(message);
+}
+
+static enum supplicant_state string2state(const char *state)
+{
+ if (g_str_equal(state, "INACTIVE") == TRUE)
+ return WPA_INACTIVE;
+ else if (g_str_equal(state, "SCANNING") == TRUE)
+ return WPA_SCANNING;
+ else if (g_str_equal(state, "ASSOCIATING") == TRUE)
+ return WPA_ASSOCIATING;
+ else if (g_str_equal(state, "ASSOCIATED") == TRUE)
+ return WPA_ASSOCIATED;
+ else if (g_str_equal(state, "GROUP_HANDSHAKE") == TRUE)
+ return WPA_GROUP_HANDSHAKE;
+ else if (g_str_equal(state, "4WAY_HANDSHAKE") == TRUE)
+ return WPA_4WAY_HANDSHAKE;
+ else if (g_str_equal(state, "COMPLETED") == TRUE)
+ return WPA_COMPLETED;
+ else if (g_str_equal(state, "DISCONNECTED") == TRUE)
+ return WPA_DISCONNECTED;
+ else
+ return WPA_INVALID;
+}
+
+static void state_change(struct supplicant_task *task, DBusMessage *msg)
+{
+ DBusError error;
+ const char *newstate, *oldstate;
+ enum supplicant_state state;
+
+ dbus_error_init(&error);
+
+ if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &newstate,
+ DBUS_TYPE_STRING, &oldstate,
+ DBUS_TYPE_INVALID) == FALSE) {
+ if (dbus_error_is_set(&error) == TRUE) {
+ connman_error("%s", error.message);
+ dbus_error_free(&error);
+ } else
+ connman_error("Wrong arguments for state change");
+ return;
+ }
+
+ DBG("state %s ==> %s", oldstate, newstate);
+
+ state = string2state(newstate);
+ if (state == WPA_INVALID)
+ return;
+
+ task->state = state;
+
+ switch (task->state) {
+ case WPA_SCANNING:
+ task->noscan = TRUE;
+ connman_device_set_scanning(task->device, TRUE);
+ break;
+ case WPA_ASSOCIATING:
+ case WPA_ASSOCIATED:
+ case WPA_4WAY_HANDSHAKE:
+ case WPA_GROUP_HANDSHAKE:
+ task->noscan = TRUE;
+ break;
+ case WPA_COMPLETED:
+ case WPA_DISCONNECTED:
+ task->noscan = FALSE;
+ break;
+ case WPA_INACTIVE:
+ task->noscan = FALSE;
+ connman_device_set_scanning(task->device, FALSE);
+ break;
+ case WPA_INVALID:
+ break;
+ }
+
+ if (task->network == NULL)
+ return;
+
+ switch (task->state) {
+ case WPA_COMPLETED:
+ /* carrier on */
+ connman_network_set_connected(task->network, TRUE);
+ connman_device_set_scanning(task->device, FALSE);
+ break;
+ case WPA_DISCONNECTED:
+ /* carrier off */
+ connman_network_set_connected(task->network, FALSE);
+ connman_device_set_scanning(task->device, FALSE);
+ break;
+ case WPA_ASSOCIATING:
+ connman_network_set_associating(task->network, TRUE);
+ break;
+ default:
+ connman_network_set_associating(task->network, FALSE);
+ break;
+ }
+}
+
+static DBusHandlerResult supplicant_filter(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct supplicant_task *task;
+ const char *member, *path;
+
+ if (dbus_message_has_interface(msg,
+ SUPPLICANT_INTF ".Interface") == FALSE)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ member = dbus_message_get_member(msg);
+ if (member == NULL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ path = dbus_message_get_path(msg);
+ if (path == NULL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ task = find_task_by_path(path);
+ if (task == NULL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ DBG("task %p member %s", task, member);
+
+ if (g_str_equal(member, "ScanResultsAvailable") == TRUE)
+ scan_results_available(task);
+ else if (g_str_equal(member, "StateChange") == TRUE)
+ state_change(task, msg);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int supplicant_start(struct connman_device *device)
+{
+ struct supplicant_task *task;
+
+ DBG("device %p", device);