/**
 * wpas_dbus_iface_select_network - Attempt association with a configured network
 * @message: Pointer to incoming dbus message
 * @wpa_s: wpa_supplicant structure for a network interface
 * Returns: A dbus message containing a UINT32 indicating success (1) or
 *          failure (0)
 *
 * Handler function for "selectNetwork" method call of network interface.
 */
DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message,
					     struct wpa_supplicant *wpa_s)
{
	DBusMessage *reply = NULL;
	const char *op;
	struct wpa_ssid *ssid;
	char *iface_obj_path = NULL;
	char *network = NULL;

	if (os_strlen(dbus_message_get_signature(message)) == 0) {
		/* Any network */
		ssid = NULL;
	} else {
		int nid;

		if (!dbus_message_get_args(message, NULL,
					   DBUS_TYPE_OBJECT_PATH, &op,
					   DBUS_TYPE_INVALID)) {
			reply = wpas_dbus_new_invalid_opts_error(message,
								 NULL);
			goto out;
		}

		/* Extract the network number */
		iface_obj_path = wpas_dbus_decompose_object_path(op,
								 &network,
								 NULL);
		if (iface_obj_path == NULL) {
			reply = wpas_dbus_new_invalid_iface_error(message);
			goto out;
		}
		/* Ensure the object path really points to this interface */
		if (os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) {
			reply = wpas_dbus_new_invalid_network_error(message);
			goto out;
		}

		nid = strtoul(network, NULL, 10);
		if (errno == EINVAL) {
			reply = wpas_dbus_new_invalid_network_error(message);
			goto out;
		}

		ssid = wpa_config_get_network(wpa_s->conf, nid);
		if (ssid == NULL) {
			reply = wpas_dbus_new_invalid_network_error(message);
			goto out;
		}
	}

	/* Finally, associate with the network */
	wpa_supplicant_select_network(wpa_s, ssid);

	reply = wpas_dbus_new_success_reply(message);

out:
	os_free(iface_obj_path);
	os_free(network);
	return reply;
}
/**
 * wpas_dbus_iface_remove_network - Remove a configured network
 * @message: Pointer to incoming dbus message
 * @wpa_s: wpa_supplicant structure for a network interface
 * Returns: A dbus message containing a UINT32 indicating success (1) or
 *          failure (0)
 *
 * Handler function for "removeNetwork" method call of a network interface.
 */
DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message,
					     struct wpa_supplicant *wpa_s)
{
	DBusMessage *reply = NULL;
	const char *op;
	char *iface = NULL, *net_id = NULL;
	int id;
	struct wpa_ssid *ssid;

	if (!dbus_message_get_args(message, NULL,
	                           DBUS_TYPE_OBJECT_PATH, &op,
	                           DBUS_TYPE_INVALID)) {
		reply = wpas_dbus_new_invalid_opts_error(message, NULL);
		goto out;
	}

	/* Extract the network ID */
	iface = wpas_dbus_decompose_object_path(op, &net_id, NULL);
	if (iface == NULL) {
		reply = wpas_dbus_new_invalid_network_error(message);
		goto out;
	}

	/* Ensure the network is actually a child of this interface */
	if (os_strcmp(iface, wpa_s->dbus_path) != 0) {
		reply = wpas_dbus_new_invalid_network_error(message);
		goto out;
	}

	id = strtoul(net_id, NULL, 10);
	ssid = wpa_config_get_network(wpa_s->conf, id);
	if (ssid == NULL) {
		reply = wpas_dbus_new_invalid_network_error(message);
		goto out;
	}

	wpas_notify_network_removed(wpa_s, ssid);

	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
		reply = dbus_message_new_error(message,
					       WPAS_ERROR_REMOVE_NETWORK_ERROR,
					       "error removing the specified "
					       "on this interface.");
		goto out;
	}

	if (ssid == wpa_s->current_ssid)
		wpa_supplicant_deauthenticate(wpa_s,
					      WLAN_REASON_DEAUTH_LEAVING);
	reply = wpas_dbus_new_success_reply(message);

out:
	os_free(iface);
	os_free(net_id);
	return reply;
}
/**
 * wpas_dispatch_network_method - dispatch messages for configured networks
 * @message: the incoming dbus message
 * @wpa_s: a network interface's data
 * @network_id: id of the configured network we're interested in
 * Returns: a reply dbus message, or a dbus error message
 *
 * This function dispatches all incoming dbus messages for configured networks.
 */
static DBusMessage * wpas_dispatch_network_method(DBusMessage *message,
						  struct wpa_supplicant *wpa_s,
						  int network_id)
{
	DBusMessage *reply = NULL;
	const char *method = dbus_message_get_member(message);
	struct wpa_ssid *ssid;

	ssid = wpa_config_get_network(wpa_s->conf, network_id);
	if (ssid == NULL)
		return wpas_dbus_new_invalid_network_error(message);

	if (!strcmp(method, "set"))
		reply = wpas_dbus_iface_set_network(message, wpa_s, ssid);
	else if (!strcmp(method, "enable"))
		reply = wpas_dbus_iface_enable_network(message, wpa_s, ssid);
	else if (!strcmp(method, "disable"))
		reply = wpas_dbus_iface_disable_network(message, wpa_s, ssid);

	return reply;
}
/**
 * wpas_iface_message_handler - Dispatch messages for interfaces or networks
 * @connection: Connection to the system message bus
 * @message: An incoming dbus message
 * @user_data: A pointer to a dbus control interface data structure
 * Returns: Whether or not the message was handled
 *
 * This function dispatches all incoming dbus messages for network interfaces,
 * or objects owned by them, such as scanned BSSIDs and configured networks.
 */
static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection,
						    DBusMessage *message,
						    void *user_data)
{
	struct wpa_supplicant *wpa_s = user_data;
	const char *method = dbus_message_get_member(message);
	const char *path = dbus_message_get_path(message);
	const char *msg_interface = dbus_message_get_interface(message);
	char *iface_obj_path = NULL;
	char *network = NULL;
	char *bssid = NULL;
	DBusMessage *reply = NULL;

	/* Caller must specify a message interface */
	if (!msg_interface)
		goto out;

	iface_obj_path = wpas_dbus_decompose_object_path(path, &network,
	                                                 &bssid);
	if (iface_obj_path == NULL) {
		reply = wpas_dbus_new_invalid_iface_error(message);
		goto out;
	}

	/* Make sure the message's object path actually refers to the
	 * wpa_supplicant structure it's supposed to (which is wpa_s)
	 */
	if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global,
	                                          iface_obj_path) != wpa_s) {
		reply = wpas_dbus_new_invalid_iface_error(message);
		goto out;
	}

	if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) {
		/* A method for one of this interface's configured networks */
		int nid = strtoul(network, NULL, 10);
		if (errno != EINVAL)
			reply = wpas_dispatch_network_method(message, wpa_s,
							     nid);
		else
			reply = wpas_dbus_new_invalid_network_error(message);
	} else if (bssid && !strcmp(msg_interface, WPAS_DBUS_IFACE_BSSID)) {
		/* A method for one of this interface's scanned BSSIDs */
		reply = wpas_dispatch_bssid_method(message, wpa_s, bssid);
	} else if (!strcmp(msg_interface, WPAS_DBUS_IFACE_INTERFACE)) {
		/* A method for an interface only. */
		if (!strcmp(method, "scan"))
			reply = wpas_dbus_iface_scan(message, wpa_s);
		else if (!strcmp(method, "scanResults"))
			reply = wpas_dbus_iface_scan_results(message, wpa_s);
		else if (!strcmp(method, "addNetwork"))
			reply = wpas_dbus_iface_add_network(message, wpa_s);
		else if (!strcmp(method, "removeNetwork"))
			reply = wpas_dbus_iface_remove_network(message, wpa_s);
		else if (!strcmp(method, "selectNetwork"))
			reply = wpas_dbus_iface_select_network(message, wpa_s);
		else if (!strcmp(method, "capabilities"))
			reply = wpas_dbus_iface_capabilities(message, wpa_s);
		else if (!strcmp(method, "disconnect"))
			reply = wpas_dbus_iface_disconnect(message, wpa_s);
		else if (!strcmp(method, "setAPScan"))
			reply = wpas_dbus_iface_set_ap_scan(message, wpa_s);
		else if (!strcmp(method, "setSmartcardModules"))
			reply = wpas_dbus_iface_set_smartcard_modules(message,
								      wpa_s);
		else if (!strcmp(method, "state"))
			reply = wpas_dbus_iface_get_state(message, wpa_s);
		else if (!strcmp(method, "scanning"))
			reply = wpas_dbus_iface_get_scanning(message, wpa_s);
		else if (!strcmp(method, "setBlobs"))
			reply = wpas_dbus_iface_set_blobs(message, wpa_s);
		else if (!strcmp(method, "removeBlobs"))
			reply = wpas_dbus_iface_remove_blobs(message, wpa_s);
#ifdef CONFIG_WPS
		else if (!os_strcmp(method, "wpsPbc"))
			reply = wpas_dbus_iface_wps_pbc(message, wpa_s);
		else if (!os_strcmp(method, "wpsPin"))
			reply = wpas_dbus_iface_wps_pin(message, wpa_s);
		else if (!os_strcmp(method, "wpsReg"))
			reply = wpas_dbus_iface_wps_reg(message, wpa_s);
#endif /* CONFIG_WPS */
		else if (!os_strcmp(method, "flush"))
			reply = wpas_dbus_iface_flush(message, wpa_s);
	}

	/* If the message was handled, send back the reply */
	if (reply) {
		if (!dbus_message_get_no_reply(message))
			dbus_connection_send(connection, reply, NULL);
		dbus_message_unref(reply);
	}

out:
	os_free(iface_obj_path);
	os_free(network);
	os_free(bssid);
	return reply ? DBUS_HANDLER_RESULT_HANDLED :
		DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
/**
 * wpas_dbus_iface_select_network - Attempt association with a configured network
 * @message: Pointer to incoming dbus message
 * @wpa_s: wpa_supplicant structure for a network interface
 * Returns: A dbus message containing a UINT32 indicating success (1) or
 *          failure (0)
 *
 * Handler function for "selectNetwork" method call of network interface.
 */
DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message,
					     struct wpa_supplicant *wpa_s)
{
	DBusMessage *reply = NULL;
	const char *op;
	struct wpa_ssid *ssid;
	char *iface_obj_path = NULL;
	char *network = NULL;

	if (strlen(dbus_message_get_signature(message)) == 0) {
		/* Any network */
		ssid = wpa_s->conf->ssid;
		while (ssid) {
			ssid->disabled = 0;
			ssid = ssid->next;
		}
		wpa_s->reassociate = 1;
		wpa_supplicant_req_scan(wpa_s, 0, 0);
	} else {
		const char *obj_path;
		int nid;

		if (!dbus_message_get_args(message, NULL,
					   DBUS_TYPE_OBJECT_PATH, &op,
					   DBUS_TYPE_INVALID)) {
			reply = wpas_dbus_new_invalid_opts_error(message,
								 NULL);
			goto out;
		}

		/* Extract the network number */
		iface_obj_path = wpas_dbus_decompose_object_path(op,
								 &network,
								 NULL);
		if (iface_obj_path == NULL) {
			reply = wpas_dbus_new_invalid_iface_error(message);
			goto out;
		}
		/* Ensure the object path really points to this interface */
		obj_path = wpa_supplicant_get_dbus_path(wpa_s);
		if (strcmp(iface_obj_path, obj_path) != 0) {
			reply = wpas_dbus_new_invalid_network_error(message);
			goto out;
		}

		nid = strtoul(network, NULL, 10);
		if (errno == EINVAL) {
			reply = wpas_dbus_new_invalid_network_error(message);
			goto out;
		}

		ssid = wpa_config_get_network(wpa_s->conf, nid);
		if (ssid == NULL) {
			reply = wpas_dbus_new_invalid_network_error(message);
			goto out;
		}

		/* Finally, associate with the network */
		if (ssid != wpa_s->current_ssid && wpa_s->current_ssid)
			wpa_supplicant_disassociate(
				wpa_s, WLAN_REASON_DEAUTH_LEAVING);

		/* Mark all other networks disabled and trigger reassociation
		 */
		ssid = wpa_s->conf->ssid;
		while (ssid) {
			ssid->disabled = (nid != ssid->id);
			ssid = ssid->next;
		}
		wpa_s->disconnected = 0;
		wpa_s->reassociate = 1;
		wpa_supplicant_req_scan(wpa_s, 0, 0);
	}

	reply = wpas_dbus_new_success_reply(message);

out:
	free(iface_obj_path);
	free(network);
	return reply;
}