/**
 * Convenience function to add an entire string array to the dict.
 *
 * @param iter_dict A valid DBusMessageIter returned from
 *                  wpa_dbus_dict_open_write()
 * @param key The key of the dict item
 * @param items The array of strings
 * @param num_items The number of strings in the array
 * @return TRUE on success, FALSE on failure
 *
 */
dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
					      const char *key,
					      const char **items,
					      const dbus_uint32_t num_items)
{
	DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
	dbus_uint32_t i;

	if (!key)
		return FALSE;
	if (!items && (num_items != 0))
		return FALSE;

	if (!wpa_dbus_dict_begin_string_array(iter_dict, key,
					      &iter_dict_entry, &iter_dict_val,
					      &iter_array))
		return FALSE;

	for (i = 0; i < num_items; i++) {
		if (!wpa_dbus_dict_string_array_add_element(&iter_array,
							    items[i]))
			return FALSE;
	}

	if (!wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry,
					    &iter_dict_val, &iter_array))
		return FALSE;

	return TRUE;
}
/**
 * wpas_dbus_iface_capabilities - Return interface capabilities
 * @message: Pointer to incoming dbus message
 * @wpa_s: wpa_supplicant structure for a network interface
 * Returns: A dbus message containing a dict of strings
 *
 * Handler function for "capabilities" method call of an interface.
 */
DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
					   struct wpa_supplicant *wpa_s)
{
	DBusMessage *reply = NULL;
	struct wpa_driver_capa capa;
	int res;
	DBusMessageIter iter, iter_dict;
	char **eap_methods;
	size_t num_items;
	dbus_bool_t strict = FALSE;
	DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;

	if (!dbus_message_get_args(message, NULL,
				   DBUS_TYPE_BOOLEAN, &strict,
				   DBUS_TYPE_INVALID))
		strict = FALSE;

	reply = dbus_message_new_method_return(message);

	dbus_message_iter_init_append(reply, &iter);
	if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
		goto error;

	/* EAP methods */
	eap_methods = eap_get_names_as_string_array(&num_items);
	if (eap_methods) {
		dbus_bool_t success = FALSE;
		size_t i = 0;

		success = wpa_dbus_dict_append_string_array(
			&iter_dict, "eap", (const char **) eap_methods,
			num_items);

		/* free returned method array */
		while (eap_methods[i])
			os_free(eap_methods[i++]);
		os_free(eap_methods);

		if (!success)
			goto error;
	}

	res = wpa_drv_get_capa(wpa_s, &capa);

	/***** pairwise cipher */
	if (res < 0) {
		if (!strict) {
			const char *args[] = {"CCMP", "TKIP", "NONE"};
			if (!wpa_dbus_dict_append_string_array(
				    &iter_dict, "pairwise", args,
				    sizeof(args) / sizeof(char*)))
				goto error;
		}
	} else {
		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise",
						      &iter_dict_entry,
						      &iter_dict_val,
						      &iter_array))
			goto error;

		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "CCMP"))
				goto error;
		}

		if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "TKIP"))
				goto error;
		}

		if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "NONE"))
				goto error;
		}

		if (!wpa_dbus_dict_end_string_array(&iter_dict,
						    &iter_dict_entry,
						    &iter_dict_val,
						    &iter_array))
			goto error;
	}

	/***** group cipher */
	if (res < 0) {
		if (!strict) {
			const char *args[] = {
				"CCMP", "TKIP", "WEP104", "WEP40"
			};
			if (!wpa_dbus_dict_append_string_array(
				    &iter_dict, "group", args,
				    sizeof(args) / sizeof(char*)))
				goto error;
		}
	} else {
		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "group",
						      &iter_dict_entry,
						      &iter_dict_val,
						      &iter_array))
			goto error;

		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "CCMP"))
				goto error;
		}

		if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "TKIP"))
				goto error;
		}

		if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "WEP104"))
				goto error;
		}

		if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "WEP40"))
				goto error;
		}

		if (!wpa_dbus_dict_end_string_array(&iter_dict,
						    &iter_dict_entry,
						    &iter_dict_val,
						    &iter_array))
			goto error;
	}

	/***** key management */
	if (res < 0) {
		if (!strict) {
			const char *args[] = {
				"WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE",
				"NONE"
			};
			if (!wpa_dbus_dict_append_string_array(
				    &iter_dict, "key_mgmt", args,
				    sizeof(args) / sizeof(char*)))
				goto error;
		}
	} else {
		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt",
						      &iter_dict_entry,
						      &iter_dict_val,
						      &iter_array))
			goto error;

		if (!wpa_dbus_dict_string_array_add_element(&iter_array,
							    "NONE"))
			goto error;

		if (!wpa_dbus_dict_string_array_add_element(&iter_array,
							    "IEEE8021X"))
			goto error;

		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "WPA-EAP"))
				goto error;
		}

		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "WPA-PSK"))
				goto error;
		}

		if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "WPA-NONE"))
				goto error;
		}

		if (!wpa_dbus_dict_end_string_array(&iter_dict,
						    &iter_dict_entry,
						    &iter_dict_val,
						    &iter_array))
			goto error;
	}

	/***** WPA protocol */
	if (res < 0) {
		if (!strict) {
			const char *args[] = { "RSN", "WPA" };
			if (!wpa_dbus_dict_append_string_array(
				    &iter_dict, "proto", args,
				    sizeof(args) / sizeof(char*)))
				goto error;
		}
	} else {
		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto",
						      &iter_dict_entry,
						      &iter_dict_val,
						      &iter_array))
			goto error;

		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "RSN"))
				goto error;
		}

		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
				     WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "WPA"))
				goto error;
		}

		if (!wpa_dbus_dict_end_string_array(&iter_dict,
						    &iter_dict_entry,
						    &iter_dict_val,
						    &iter_array))
			goto error;
	}

	/***** auth alg */
	if (res < 0) {
		if (!strict) {
			const char *args[] = { "OPEN", "SHARED", "LEAP" };
			if (!wpa_dbus_dict_append_string_array(
				    &iter_dict, "auth_alg", args,
				    sizeof(args) / sizeof(char*)))
				goto error;
		}
	} else {
		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg",
						      &iter_dict_entry,
						      &iter_dict_val,
						      &iter_array))
			goto error;

		if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "OPEN"))
				goto error;
		}

		if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "SHARED"))
				goto error;
		}

		if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) {
			if (!wpa_dbus_dict_string_array_add_element(
				    &iter_array, "LEAP"))
				goto error;
		}

		if (!wpa_dbus_dict_end_string_array(&iter_dict,
						    &iter_dict_entry,
						    &iter_dict_val,
						    &iter_array))
			goto error;
	}

	if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
		goto error;

	return reply;

error:
	if (reply)
		dbus_message_unref(reply);
	return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR,
				      "an internal error occurred returning "
				      "interface capabilities.");
}