/*!
 * @file adsi.c
 * @brief Definitions for ADSI functionality.
 */
#include "extapi.h"
#include "wshelpers.h"
#include "adsi.h"
#include "adsi_interface.h"
#include "common_metapi.h"

/*! @brief The default page size to use when no page size is specified */
#define DEFAULT_PAGE_SIZE 1000

/*!
 * @brief Perform an ADSI query against a domain.
 * @param remote Pointer to the \c Remote instance.
 * @param packet Pointer to the incoming \c Packet instance.
 * @returns Indication of success or failure.
 * @remark Real error codes are returned to the caller via a response packet.
 */
DWORD request_adsi_domain_query(Remote *remote, Packet *packet)
{
	DWORD dwResult = ERROR_SUCCESS;
	LPSTR lpValue = NULL;
	LPWSTR lpwDomain = NULL;
	LPWSTR lpwFilter = NULL;
	LPWSTR* lpwFields = NULL;
	DWORD fieldCount = 0;
	DWORD fieldIndex = 0;
	Packet * response = met_api->packet.create_response(packet);
	Tlv fieldTlv;
	DWORD maxResults;
	DWORD pageSize;

	do
	{
		if (!response)
		{
			BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to create response packet", ERROR_OUTOFMEMORY);
		}

		lpValue = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_EXT_ADSI_DOMAIN);
		dprintf("[EXTAPI ADSI] Domain: %s", lpValue);
		dwResult = to_wide_string(lpValue, &lpwDomain);
		if (dwResult != ERROR_SUCCESS)
		{
			dprintf("[EXTAPI ADSI] Failed to get Domain");
			break;
		}

		lpValue = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_EXT_ADSI_FILTER);
		dprintf("[EXTAPI ADSI] Filter: %s", lpValue);
		dwResult = to_wide_string(lpValue, &lpwFilter);
		if (dwResult != ERROR_SUCCESS)
		{
			dprintf("[EXTAPI ADSI] Failed to get Filter");
			break;
		}

		maxResults = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_EXT_ADSI_MAXRESULTS);
		dprintf("[EXTAPI ADSI] Max results will be %u", maxResults);

		pageSize = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_EXT_ADSI_PAGESIZE);
		dprintf("[EXTAPI ADSI] Page size specified as %u", pageSize);

		// Set the page size to something sensible if not given.
		if (pageSize == 0)
		{
			pageSize = DEFAULT_PAGE_SIZE;
		}

		// If max results is given, there's no point in having a page size
		// that's bigger!
		if (maxResults != 0)
		{
			pageSize = min(pageSize, maxResults);
		}
		dprintf("[EXTAPI ADSI] Page size will be %u", pageSize);

		while (met_api->packet.enum_tlv(packet, fieldCount, TLV_TYPE_EXT_ADSI_FIELD, &fieldTlv) == ERROR_SUCCESS)
		{
			lpValue = (char*)fieldTlv.buffer;
			dprintf("[EXTAPI ADSI] Field %u: %s", fieldCount, lpValue);
			lpwFields = (LPWSTR*)realloc(lpwFields, sizeof(LPWSTR) * (fieldCount + 1));

			if (lpwFields == NULL)
			{
				BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to allocate memory", ERROR_OUTOFMEMORY);
			}

			dwResult = to_wide_string(lpValue, &lpwFields[fieldCount]);
			if (dwResult != ERROR_SUCCESS)
			{
				dprintf("[EXTAPI ADSI] Failed to get field as wide string");
				break;
			}
			++fieldCount;
		}

		dprintf("[EXTAPI ADSI] Field count: %u", fieldCount, lpValue);

		if (dwResult == ERROR_SUCCESS)
		{
			dprintf("[EXTAPI ADSI] Beginning user enumeration");
			dwResult = domain_query(lpwDomain, lpwFilter, lpwFields, fieldCount, maxResults, pageSize, response);
			dprintf("[EXTAPI ADSI] Result of processing: %u (0x%x)", dwResult, dwResult);
		}
	} while (0);

	if (lpwFields)
	{
		for (fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex)
		{
			if (lpwFields[fieldIndex])
			{
				free(lpwFields[fieldIndex]);
			}
		}
		free(lpwFields);
	}

	if (lpwFilter)
	{
		free(lpwFilter);
	}

	if (lpwDomain)
	{
		free(lpwDomain);
	}

	dprintf("[EXTAPI ADSI] Transmitting response back to caller.");
	if (response)
	{
		met_api->packet.transmit_response(dwResult, remote, response);
	}

	return dwResult;
}