+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_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;
+ char *security;
+ unsigned char strength;
+
+ 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)
+ * frequency : i (105)
+ * capabilities : q (113)
+ * quality : i (105)
+ * noise : i (105)
+ * level : i (105)
+ * maxrate : i (105)
+ */
+
+ 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, "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.identifier == NULL)
+ goto done;
+
+ if (result.identifier[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";
+
+ network = connman_device_get_network(task->device, result.identifier);
+ if (network == NULL) {
+ const char *mode;
+ int index;
+
+ network = connman_network_create(result.identifier,
+ 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, "Name", result.identifier);
+
+ connman_network_set_blob(network, "WiFi.SSID",
+ result.ssid, result.ssid_len);
+
+ mode = (result.adhoc == TRUE) ? "adhoc" : "managed";
+ connman_network_set_string(network, "WiFi.Mode", mode);
+
+ DBG("%s (%s %s) strength %d", result.identifier, mode,
+ security, strength);
+
+ if (connman_device_add_network(task->device, network) < 0) {
+ connman_network_unref(network);
+ goto done;
+ }
+ }
+
+ 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.identifier);
+ 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;
+ }
+
+ 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);
+
+ 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;
+ default:
+ 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);