2009-01-11 21:54:15 +01:00
|
|
|
/*
|
|
|
|
* HTTPAPI implementation
|
|
|
|
*
|
|
|
|
* Copyright 2009 Austin English
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2019-08-23 05:35:25 +02:00
|
|
|
#include "wine/http.h"
|
2019-08-29 03:45:29 +02:00
|
|
|
#include "winsvc.h"
|
2019-08-22 06:30:14 +02:00
|
|
|
#include "winternl.h"
|
2009-01-11 21:54:15 +01:00
|
|
|
#include "wine/debug.h"
|
2019-08-23 05:35:25 +02:00
|
|
|
#include "wine/heap.h"
|
2019-08-29 03:45:30 +02:00
|
|
|
#include "wine/list.h"
|
2009-01-11 21:54:15 +01:00
|
|
|
|
2020-02-29 04:45:29 +01:00
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(http);
|
2009-01-11 21:54:15 +01:00
|
|
|
|
2019-08-30 02:58:06 +02:00
|
|
|
static const WCHAR device_nameW[] = {'\\','D','e','v','i','c','e','\\','H','t','t','p','\\','R','e','q','Q','u','e','u','e',0};
|
|
|
|
|
2019-08-30 02:58:05 +02:00
|
|
|
static WCHAR *heap_strdupW(const WCHAR *str)
|
|
|
|
{
|
|
|
|
int len = wcslen(str) + 1;
|
|
|
|
WCHAR *ret = heap_alloc(len * sizeof(WCHAR));
|
|
|
|
wcscpy(ret, str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-01-12 19:01:27 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* HttpInitialize (HTTPAPI.@)
|
|
|
|
*
|
|
|
|
* Initializes HTTP Server API engine
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* version [ I] HTTP API version which caller will use
|
|
|
|
* flags [ I] initialization options which specify parts of API what will be used
|
|
|
|
* reserved [IO] reserved, must be NULL
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* NO_ERROR if function succeeds, or error code if function fails
|
|
|
|
*
|
|
|
|
*/
|
2019-08-29 03:45:29 +02:00
|
|
|
ULONG WINAPI HttpInitialize(HTTPAPI_VERSION version, ULONG flags, void *reserved)
|
2009-01-12 19:01:27 +01:00
|
|
|
{
|
2019-08-29 03:45:29 +02:00
|
|
|
static const WCHAR httpW[] = {'h','t','t','p',0};
|
|
|
|
SC_HANDLE manager, service;
|
|
|
|
|
|
|
|
TRACE("version %u.%u, flags %#x, reserved %p.\n", version.HttpApiMajorVersion,
|
|
|
|
version.HttpApiMinorVersion, flags, reserved);
|
|
|
|
|
|
|
|
if (flags & ~HTTP_INITIALIZE_SERVER)
|
|
|
|
{
|
|
|
|
FIXME("Unhandled flags %#x.\n", flags);
|
2019-12-09 06:11:27 +01:00
|
|
|
return ERROR_SUCCESS;
|
2019-08-29 03:45:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(manager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT)))
|
|
|
|
return GetLastError();
|
|
|
|
|
|
|
|
if (!(service = OpenServiceW(manager, httpW, SERVICE_START)))
|
|
|
|
{
|
|
|
|
ERR("Failed to open HTTP service, error %u.\n", GetLastError());
|
|
|
|
CloseServiceHandle(manager);
|
|
|
|
return GetLastError();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!StartServiceW(service, 0, NULL) && GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
|
|
|
|
{
|
|
|
|
ERR("Failed to start HTTP service, error %u.\n", GetLastError());
|
|
|
|
CloseServiceHandle(service);
|
|
|
|
CloseServiceHandle(manager);
|
|
|
|
return GetLastError();
|
|
|
|
}
|
|
|
|
|
|
|
|
CloseServiceHandle(service);
|
|
|
|
CloseServiceHandle(manager);
|
|
|
|
return ERROR_SUCCESS;
|
2009-01-12 19:01:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpTerminate (HTTPAPI.@)
|
|
|
|
*
|
|
|
|
* Cleans up HTTP Server API engine resources allocated by HttpInitialize
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* flags [ I] options which specify parts of API what should be released
|
|
|
|
* reserved [IO] reserved, must be NULL
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* NO_ERROR if function succeeds, or error code if function fails
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpTerminate( ULONG flags, PVOID reserved )
|
|
|
|
{
|
|
|
|
FIXME( "(0x%x, %p): stub!\n", flags, reserved );
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
2009-01-12 19:01:57 +01:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpDeleteServiceConfiguration (HTTPAPI.@)
|
|
|
|
*
|
|
|
|
* Remove configuration record from HTTP Server API configuration store
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* handle [I] reserved, must be 0
|
|
|
|
* type [I] configuration record type
|
|
|
|
* config [I] buffer which contains configuration record information
|
|
|
|
* length [I] length of configuration record buffer
|
|
|
|
* overlapped [I] reserved, must be NULL
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* NO_ERROR if function succeeds, or error code if function fails
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpDeleteServiceConfiguration( HANDLE handle, HTTP_SERVICE_CONFIG_ID type,
|
|
|
|
PVOID config, ULONG length, LPOVERLAPPED overlapped )
|
|
|
|
{
|
|
|
|
FIXME( "(%p, %d, %p, %d, %p): stub!\n", handle, type, config, length, overlapped );
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpQueryServiceConfiguration (HTTPAPI.@)
|
|
|
|
*
|
|
|
|
* Retrieves configuration records from HTTP Server API configuration store
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* handle [ I] reserved, must be 0
|
|
|
|
* type [ I] configuration records type
|
|
|
|
* query [ I] buffer which contains query data used to retrieve records
|
|
|
|
* query_len [ I] length of query buffer
|
|
|
|
* buffer [IO] buffer to store query results
|
|
|
|
* buffer_len [ I] length of output buffer
|
|
|
|
* data_len [ O] optional pointer to a buffer which receives query result length
|
|
|
|
* overlapped [ I] reserved, must be NULL
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* NO_ERROR if function succeeds, or error code if function fails
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpQueryServiceConfiguration( HANDLE handle, HTTP_SERVICE_CONFIG_ID type,
|
|
|
|
PVOID query, ULONG query_len, PVOID buffer, ULONG buffer_len,
|
|
|
|
PULONG data_len, LPOVERLAPPED overlapped )
|
|
|
|
{
|
|
|
|
FIXME( "(%p, %d, %p, %d, %p, %d, %p, %p): stub!\n", handle, type, query, query_len,
|
|
|
|
buffer, buffer_len, data_len, overlapped );
|
|
|
|
return ERROR_FILE_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpSetServiceConfiguration (HTTPAPI.@)
|
|
|
|
*
|
|
|
|
* Add configuration record to HTTP Server API configuration store
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* handle [I] reserved, must be 0
|
|
|
|
* type [I] configuration record type
|
|
|
|
* config [I] buffer which contains configuration record information
|
|
|
|
* length [I] length of configuration record buffer
|
|
|
|
* overlapped [I] reserved, must be NULL
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* NO_ERROR if function succeeds, or error code if function fails
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpSetServiceConfiguration( HANDLE handle, HTTP_SERVICE_CONFIG_ID type,
|
|
|
|
PVOID config, ULONG length, LPOVERLAPPED overlapped )
|
|
|
|
{
|
|
|
|
FIXME( "(%p, %d, %p, %d, %p): stub!\n", handle, type, config, length, overlapped );
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
2011-06-25 13:14:20 +02:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpCreateHttpHandle (HTTPAPI.@)
|
|
|
|
*
|
|
|
|
* Creates a handle to the HTTP request queue
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* handle [O] handle to request queue
|
|
|
|
* reserved [I] reserved, must be NULL
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* NO_ERROR if function succeeds, or error code if function fails
|
|
|
|
*
|
|
|
|
*/
|
2019-08-22 06:30:14 +02:00
|
|
|
ULONG WINAPI HttpCreateHttpHandle(HANDLE *handle, ULONG reserved)
|
2011-06-25 13:14:20 +02:00
|
|
|
{
|
2019-08-22 06:30:14 +02:00
|
|
|
OBJECT_ATTRIBUTES attr = {sizeof(attr)};
|
|
|
|
UNICODE_STRING string;
|
|
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
|
|
|
|
TRACE("handle %p, reserved %#x.\n", handle, reserved);
|
|
|
|
|
|
|
|
if (!handle)
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
RtlInitUnicodeString(&string, device_nameW);
|
|
|
|
attr.ObjectName = &string;
|
|
|
|
return RtlNtStatusToDosError(NtCreateFile(handle, 0, &attr, &iosb, NULL,
|
|
|
|
FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_NON_DIRECTORY_FILE, NULL, 0));
|
2011-06-25 13:14:20 +02:00
|
|
|
}
|
2012-09-26 09:37:55 +02:00
|
|
|
|
2019-08-23 05:35:25 +02:00
|
|
|
static ULONG add_url(HANDLE queue, const WCHAR *urlW, HTTP_URL_CONTEXT context)
|
|
|
|
{
|
|
|
|
struct http_add_url_params *params;
|
|
|
|
ULONG ret = ERROR_SUCCESS;
|
|
|
|
OVERLAPPED ovl;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = WideCharToMultiByte(CP_ACP, 0, urlW, -1, NULL, 0, NULL, NULL);
|
|
|
|
if (!(params = heap_alloc(offsetof(struct http_add_url_params, url[len]))))
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, urlW, -1, params->url, len, NULL, NULL);
|
|
|
|
params->context = context;
|
|
|
|
|
|
|
|
ovl.hEvent = (HANDLE)((ULONG_PTR)CreateEventW(NULL, TRUE, FALSE, NULL) | 1);
|
|
|
|
|
|
|
|
if (!DeviceIoControl(queue, IOCTL_HTTP_ADD_URL, params,
|
|
|
|
offsetof(struct http_add_url_params, url[len]), NULL, 0, NULL, &ovl))
|
|
|
|
ret = GetLastError();
|
|
|
|
CloseHandle(ovl.hEvent);
|
2019-09-26 14:18:25 +02:00
|
|
|
heap_free(params);
|
2019-08-23 05:35:25 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-09-26 09:37:55 +02:00
|
|
|
/***********************************************************************
|
|
|
|
* HttpAddUrl (HTTPAPI.@)
|
|
|
|
*/
|
2019-08-23 05:35:25 +02:00
|
|
|
ULONG WINAPI HttpAddUrl(HANDLE queue, const WCHAR *url, void *reserved)
|
2012-09-26 09:37:55 +02:00
|
|
|
{
|
2019-08-23 05:35:25 +02:00
|
|
|
TRACE("queue %p, url %s, reserved %p.\n", queue, debugstr_w(url), reserved);
|
|
|
|
|
|
|
|
return add_url(queue, url, 0);
|
2012-09-26 09:37:55 +02:00
|
|
|
}
|
2017-02-21 13:47:30 +01:00
|
|
|
|
2019-08-30 02:58:05 +02:00
|
|
|
static ULONG remove_url(HANDLE queue, const WCHAR *urlW)
|
2019-08-08 04:10:15 +02:00
|
|
|
{
|
2019-08-23 05:35:26 +02:00
|
|
|
ULONG ret = ERROR_SUCCESS;
|
|
|
|
OVERLAPPED ovl = {};
|
|
|
|
char *url;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = WideCharToMultiByte(CP_ACP, 0, urlW, -1, NULL, 0, NULL, NULL);
|
|
|
|
if (!(url = heap_alloc(len)))
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, urlW, -1, url, len, NULL, NULL);
|
|
|
|
|
|
|
|
ovl.hEvent = (HANDLE)((ULONG_PTR)CreateEventW(NULL, TRUE, FALSE, NULL) | 1);
|
|
|
|
|
|
|
|
if (!DeviceIoControl(queue, IOCTL_HTTP_REMOVE_URL, url, len, NULL, 0, NULL, &ovl))
|
|
|
|
ret = GetLastError();
|
|
|
|
CloseHandle(ovl.hEvent);
|
|
|
|
heap_free(url);
|
|
|
|
return ret;
|
2019-08-08 04:10:15 +02:00
|
|
|
}
|
|
|
|
|
2019-08-30 02:58:05 +02:00
|
|
|
/***********************************************************************
|
|
|
|
* HttpRemoveUrl (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpRemoveUrl(HANDLE queue, const WCHAR *url)
|
|
|
|
{
|
|
|
|
TRACE("queue %p, url %s.\n", queue, debugstr_w(url));
|
|
|
|
|
|
|
|
if (!queue)
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
return remove_url(queue, url);
|
|
|
|
}
|
|
|
|
|
2019-10-31 13:37:42 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* HttpReceiveRequestEntityBody (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpReceiveRequestEntityBody(HANDLE queue, HTTP_REQUEST_ID id, ULONG flags,
|
|
|
|
void *buffer, ULONG size, ULONG *ret_size, OVERLAPPED *ovl)
|
|
|
|
{
|
2019-11-04 03:20:38 +01:00
|
|
|
struct http_receive_body_params params =
|
|
|
|
{
|
|
|
|
.id = id,
|
|
|
|
.bits = sizeof(void *) * 8,
|
|
|
|
};
|
|
|
|
ULONG ret = ERROR_SUCCESS;
|
|
|
|
OVERLAPPED sync_ovl;
|
2019-10-31 13:37:42 +01:00
|
|
|
|
2019-11-04 03:20:38 +01:00
|
|
|
TRACE("queue %p, id %s, flags %#x, buffer %p, size %#x, ret_size %p, ovl %p.\n",
|
2019-10-31 13:37:42 +01:00
|
|
|
queue, wine_dbgstr_longlong(id), flags, buffer, size, ret_size, ovl);
|
|
|
|
|
2019-11-04 03:20:38 +01:00
|
|
|
if (flags)
|
|
|
|
FIXME("Ignoring flags %#x.\n", flags);
|
|
|
|
|
|
|
|
if (!ovl)
|
|
|
|
{
|
|
|
|
sync_ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
|
|
ovl = &sync_ovl;
|
|
|
|
}
|
|
|
|
|
2020-02-29 04:45:31 +01:00
|
|
|
if (!DeviceIoControl(queue, IOCTL_HTTP_RECEIVE_BODY, ¶ms, sizeof(params), buffer, size, ret_size, ovl))
|
2019-11-04 03:20:38 +01:00
|
|
|
ret = GetLastError();
|
|
|
|
|
|
|
|
if (ovl == &sync_ovl)
|
|
|
|
{
|
2020-02-29 04:45:31 +01:00
|
|
|
if (ret == ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
ret = ERROR_SUCCESS;
|
|
|
|
if (!GetOverlappedResult(queue, ovl, ret_size, TRUE))
|
|
|
|
ret = GetLastError();
|
|
|
|
}
|
2019-11-04 03:20:38 +01:00
|
|
|
CloseHandle(sync_ovl.hEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2019-10-31 13:37:42 +01:00
|
|
|
}
|
|
|
|
|
2019-08-08 04:10:13 +02:00
|
|
|
/***********************************************************************
|
|
|
|
* HttpReceiveHttpRequest (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpReceiveHttpRequest(HANDLE queue, HTTP_REQUEST_ID id, ULONG flags,
|
|
|
|
HTTP_REQUEST *request, ULONG size, ULONG *ret_size, OVERLAPPED *ovl)
|
|
|
|
{
|
2019-08-27 23:43:01 +02:00
|
|
|
struct http_receive_request_params params =
|
|
|
|
{
|
|
|
|
.addr = (ULONG_PTR)request,
|
|
|
|
.id = id,
|
|
|
|
.flags = flags,
|
|
|
|
.bits = sizeof(void *) * 8,
|
|
|
|
};
|
|
|
|
ULONG ret = ERROR_SUCCESS;
|
|
|
|
OVERLAPPED sync_ovl;
|
|
|
|
|
|
|
|
TRACE("queue %p, id %s, flags %#x, request %p, size %#x, ret_size %p, ovl %p.\n",
|
|
|
|
queue, wine_dbgstr_longlong(id), flags, request, size, ret_size, ovl);
|
|
|
|
|
|
|
|
if (flags & ~HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY)
|
|
|
|
FIXME("Ignoring flags %#x.\n", flags & ~HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY);
|
|
|
|
|
|
|
|
if (size < sizeof(HTTP_REQUEST_V1))
|
|
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
|
|
|
|
if (!ovl)
|
|
|
|
{
|
|
|
|
sync_ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
|
|
ovl = &sync_ovl;
|
|
|
|
}
|
|
|
|
|
2020-02-29 04:45:31 +01:00
|
|
|
if (!DeviceIoControl(queue, IOCTL_HTTP_RECEIVE_REQUEST, ¶ms, sizeof(params), request, size, ret_size, ovl))
|
2019-08-27 23:43:01 +02:00
|
|
|
ret = GetLastError();
|
|
|
|
|
|
|
|
if (ovl == &sync_ovl)
|
|
|
|
{
|
2020-02-29 04:45:31 +01:00
|
|
|
if (ret == ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
ret = ERROR_SUCCESS;
|
|
|
|
if (!GetOverlappedResult(queue, ovl, ret_size, TRUE))
|
|
|
|
ret = GetLastError();
|
|
|
|
}
|
2019-08-27 23:43:01 +02:00
|
|
|
CloseHandle(sync_ovl.hEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2019-08-08 04:10:13 +02:00
|
|
|
}
|
|
|
|
|
2019-08-29 03:45:28 +02:00
|
|
|
static void format_date(char *buffer)
|
|
|
|
{
|
|
|
|
static const char day_names[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
|
|
|
static const char month_names[12][4] =
|
|
|
|
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
|
|
SYSTEMTIME date;
|
|
|
|
GetSystemTime(&date);
|
|
|
|
sprintf(buffer + strlen(buffer), "Date: %s, %02u %s %u %02u:%02u:%02u GMT\r\n",
|
|
|
|
day_names[date.wDayOfWeek], date.wDay, month_names[date.wMonth - 1],
|
|
|
|
date.wYear, date.wHour, date.wMinute, date.wSecond);
|
|
|
|
}
|
|
|
|
|
2019-08-08 04:10:14 +02:00
|
|
|
/***********************************************************************
|
|
|
|
* HttpSendHttpResponse (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpSendHttpResponse(HANDLE queue, HTTP_REQUEST_ID id, ULONG flags,
|
|
|
|
HTTP_RESPONSE *response, HTTP_CACHE_POLICY *cache_policy, ULONG *ret_size,
|
|
|
|
void *reserved1, ULONG reserved2, OVERLAPPED *ovl, HTTP_LOG_DATA *log_data)
|
|
|
|
{
|
2019-08-29 03:45:28 +02:00
|
|
|
static const char *const header_names[] =
|
|
|
|
{
|
|
|
|
"Cache-Control",
|
|
|
|
"Connection",
|
|
|
|
"Date",
|
|
|
|
"Keep-Alive",
|
|
|
|
"Pragma",
|
|
|
|
"Trailer",
|
|
|
|
"Transfer-Encoding",
|
|
|
|
"Upgrade",
|
|
|
|
"Via",
|
|
|
|
"Warning",
|
|
|
|
"Allow",
|
|
|
|
"Content-Length",
|
|
|
|
"Content-Type",
|
|
|
|
"Content-Encoding",
|
|
|
|
"Content-Language",
|
|
|
|
"Content-Location",
|
|
|
|
"Content-MD5",
|
|
|
|
"Content-Range",
|
|
|
|
"Expires",
|
|
|
|
"Last-Modified",
|
|
|
|
"Accept-Ranges",
|
|
|
|
"Age",
|
|
|
|
"ETag",
|
|
|
|
"Location",
|
|
|
|
"Proxy-Authenticate",
|
|
|
|
"Retry-After",
|
|
|
|
"Server",
|
|
|
|
"Set-Cookie",
|
|
|
|
"Vary",
|
|
|
|
"WWW-Authenticate",
|
|
|
|
};
|
|
|
|
|
|
|
|
struct http_response *buffer;
|
|
|
|
OVERLAPPED dummy_ovl = {};
|
|
|
|
ULONG ret = ERROR_SUCCESS;
|
|
|
|
int len, body_len = 0;
|
|
|
|
char *p, dummy[12];
|
|
|
|
USHORT i;
|
|
|
|
|
|
|
|
TRACE("queue %p, id %s, flags %#x, response %p, cache_policy %p, "
|
|
|
|
"ret_size %p, reserved1 %p, reserved2 %#x, ovl %p, log_data %p.\n",
|
|
|
|
queue, wine_dbgstr_longlong(id), flags, response, cache_policy,
|
|
|
|
ret_size, reserved1, reserved2, ovl, log_data);
|
|
|
|
|
|
|
|
if (flags)
|
|
|
|
FIXME("Unhandled flags %#x.\n", flags);
|
|
|
|
if (response->s.Flags)
|
|
|
|
FIXME("Unhandled response flags %#x.\n", response->s.Flags);
|
|
|
|
if (cache_policy)
|
|
|
|
WARN("Ignoring cache_policy.\n");
|
|
|
|
if (log_data)
|
|
|
|
WARN("Ignoring log_data.\n");
|
|
|
|
|
|
|
|
len = 12 + sprintf(dummy, "%hu", response->s.StatusCode) + response->s.ReasonLength;
|
|
|
|
for (i = 0; i < response->s.EntityChunkCount; ++i)
|
|
|
|
{
|
|
|
|
if (response->s.pEntityChunks[i].DataChunkType != HttpDataChunkFromMemory)
|
|
|
|
{
|
|
|
|
FIXME("Unhandled data chunk type %u.\n", response->s.pEntityChunks[i].DataChunkType);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
body_len += response->s.pEntityChunks[i].FromMemory.BufferLength;
|
|
|
|
}
|
|
|
|
len += body_len;
|
|
|
|
for (i = 0; i < HttpHeaderResponseMaximum; ++i)
|
|
|
|
{
|
|
|
|
if (i == HttpHeaderDate)
|
|
|
|
len += 37;
|
|
|
|
else if (response->s.Headers.KnownHeaders[i].RawValueLength)
|
|
|
|
len += strlen(header_names[i]) + 2 + response->s.Headers.KnownHeaders[i].RawValueLength + 2;
|
|
|
|
else if (i == HttpHeaderContentLength)
|
|
|
|
{
|
|
|
|
char dummy[12];
|
|
|
|
len += strlen(header_names[i]) + 2 + sprintf(dummy, "%d", body_len) + 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < response->s.Headers.UnknownHeaderCount; ++i)
|
|
|
|
{
|
|
|
|
len += response->s.Headers.pUnknownHeaders[i].NameLength + 2;
|
|
|
|
len += response->s.Headers.pUnknownHeaders[i].RawValueLength + 2;
|
|
|
|
}
|
|
|
|
len += 2;
|
|
|
|
|
|
|
|
if (!(buffer = heap_alloc(offsetof(struct http_response, buffer[len]))))
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
buffer->id = id;
|
|
|
|
buffer->len = len;
|
|
|
|
sprintf(buffer->buffer, "HTTP/1.1 %u %.*s\r\n", response->s.StatusCode,
|
|
|
|
response->s.ReasonLength, response->s.pReason);
|
|
|
|
|
|
|
|
for (i = 0; i < HttpHeaderResponseMaximum; ++i)
|
|
|
|
{
|
|
|
|
const HTTP_KNOWN_HEADER *header = &response->s.Headers.KnownHeaders[i];
|
|
|
|
if (i == HttpHeaderDate)
|
|
|
|
format_date(buffer->buffer);
|
|
|
|
else if (header->RawValueLength)
|
|
|
|
sprintf(buffer->buffer + strlen(buffer->buffer), "%s: %.*s\r\n",
|
|
|
|
header_names[i], header->RawValueLength, header->pRawValue);
|
|
|
|
else if (i == HttpHeaderContentLength)
|
|
|
|
sprintf(buffer->buffer + strlen(buffer->buffer), "Content-Length: %d\r\n", body_len);
|
|
|
|
}
|
|
|
|
for (i = 0; i < response->s.Headers.UnknownHeaderCount; ++i)
|
|
|
|
{
|
|
|
|
const HTTP_UNKNOWN_HEADER *header = &response->s.Headers.pUnknownHeaders[i];
|
|
|
|
sprintf(buffer->buffer + strlen(buffer->buffer), "%.*s: %.*s\r\n", header->NameLength,
|
|
|
|
header->pName, header->RawValueLength, header->pRawValue);
|
|
|
|
}
|
|
|
|
p = buffer->buffer + strlen(buffer->buffer);
|
|
|
|
/* Don't use strcat, because this might be the end of the buffer. */
|
|
|
|
memcpy(p, "\r\n", 2);
|
|
|
|
p += 2;
|
|
|
|
for (i = 0; i < response->s.EntityChunkCount; ++i)
|
|
|
|
{
|
|
|
|
const HTTP_DATA_CHUNK *chunk = &response->s.pEntityChunks[i];
|
|
|
|
memcpy(p, chunk->FromMemory.pBuffer, chunk->FromMemory.BufferLength);
|
|
|
|
p += chunk->FromMemory.BufferLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ovl)
|
|
|
|
ovl = &dummy_ovl;
|
|
|
|
|
|
|
|
if (!DeviceIoControl(queue, IOCTL_HTTP_SEND_RESPONSE, buffer,
|
|
|
|
offsetof(struct http_response, buffer[len]), NULL, 0, NULL, ovl))
|
|
|
|
ret = GetLastError();
|
|
|
|
|
|
|
|
heap_free(buffer);
|
|
|
|
return ret;
|
2019-08-08 04:10:14 +02:00
|
|
|
}
|
|
|
|
|
2019-08-29 03:45:31 +02:00
|
|
|
struct url_group
|
|
|
|
{
|
|
|
|
struct list entry, session_entry;
|
2019-08-30 02:58:04 +02:00
|
|
|
HANDLE queue;
|
2019-08-30 02:58:05 +02:00
|
|
|
WCHAR *url;
|
|
|
|
HTTP_URL_CONTEXT context;
|
2019-08-29 03:45:31 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct list url_groups = LIST_INIT(url_groups);
|
|
|
|
|
|
|
|
static struct url_group *get_url_group(HTTP_URL_GROUP_ID id)
|
|
|
|
{
|
|
|
|
struct url_group *group;
|
|
|
|
LIST_FOR_EACH_ENTRY(group, &url_groups, struct url_group, entry)
|
|
|
|
{
|
|
|
|
if ((HTTP_URL_GROUP_ID)(ULONG_PTR)group == id)
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-08-29 03:45:30 +02:00
|
|
|
struct server_session
|
|
|
|
{
|
|
|
|
struct list entry;
|
2019-08-29 03:45:31 +02:00
|
|
|
struct list groups;
|
2019-08-29 03:45:30 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct list server_sessions = LIST_INIT(server_sessions);
|
|
|
|
|
|
|
|
static struct server_session *get_server_session(HTTP_SERVER_SESSION_ID id)
|
|
|
|
{
|
|
|
|
struct server_session *session;
|
|
|
|
LIST_FOR_EACH_ENTRY(session, &server_sessions, struct server_session, entry)
|
|
|
|
{
|
|
|
|
if ((HTTP_SERVER_SESSION_ID)(ULONG_PTR)session == id)
|
|
|
|
return session;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-21 13:47:30 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* HttpCreateServerSession (HTTPAPI.@)
|
|
|
|
*/
|
2019-08-29 03:45:30 +02:00
|
|
|
ULONG WINAPI HttpCreateServerSession(HTTPAPI_VERSION version, HTTP_SERVER_SESSION_ID *id, ULONG reserved)
|
2017-02-21 13:47:30 +01:00
|
|
|
{
|
2019-08-29 03:45:30 +02:00
|
|
|
struct server_session *session;
|
|
|
|
|
|
|
|
TRACE("version %u.%u, id %p, reserved %u.\n", version.HttpApiMajorVersion,
|
|
|
|
version.HttpApiMinorVersion, id, reserved);
|
|
|
|
|
|
|
|
if (!id)
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
if ((version.HttpApiMajorVersion != 1 && version.HttpApiMajorVersion != 2)
|
|
|
|
|| version.HttpApiMinorVersion)
|
|
|
|
return ERROR_REVISION_MISMATCH;
|
|
|
|
|
|
|
|
if (!(session = heap_alloc(sizeof(*session))))
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
|
|
|
|
list_add_tail(&server_sessions, &session->entry);
|
2019-08-29 03:45:31 +02:00
|
|
|
list_init(&session->groups);
|
2019-08-29 03:45:30 +02:00
|
|
|
|
|
|
|
*id = (ULONG_PTR)session;
|
|
|
|
return ERROR_SUCCESS;
|
2017-02-21 13:47:30 +01:00
|
|
|
}
|
2019-02-11 11:07:31 +01:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpCloseServerSession (HTTPAPI.@)
|
|
|
|
*/
|
2019-08-29 03:45:30 +02:00
|
|
|
ULONG WINAPI HttpCloseServerSession(HTTP_SERVER_SESSION_ID id)
|
2019-02-11 11:07:31 +01:00
|
|
|
{
|
2019-08-29 03:45:31 +02:00
|
|
|
struct url_group *group, *group_next;
|
2019-08-29 03:45:30 +02:00
|
|
|
struct server_session *session;
|
|
|
|
|
|
|
|
TRACE("id %s.\n", wine_dbgstr_longlong(id));
|
|
|
|
|
|
|
|
if (!(session = get_server_session(id)))
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
|
2019-08-29 03:45:31 +02:00
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(group, group_next, &session->groups, struct url_group, session_entry)
|
|
|
|
{
|
|
|
|
HttpCloseUrlGroup((ULONG_PTR)group);
|
|
|
|
}
|
2019-08-29 03:45:30 +02:00
|
|
|
list_remove(&session->entry);
|
|
|
|
heap_free(session);
|
|
|
|
return ERROR_SUCCESS;
|
2019-02-11 11:07:31 +01:00
|
|
|
}
|
2019-08-29 03:45:31 +02:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpCreateUrlGroup (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpCreateUrlGroup(HTTP_SERVER_SESSION_ID session_id, HTTP_URL_GROUP_ID *group_id, ULONG reserved)
|
|
|
|
{
|
|
|
|
struct server_session *session;
|
|
|
|
struct url_group *group;
|
|
|
|
|
2019-08-30 02:58:04 +02:00
|
|
|
TRACE("session_id %s, group_id %p, reserved %#x.\n",
|
|
|
|
wine_dbgstr_longlong(session_id), group_id, reserved);
|
2019-08-29 03:45:31 +02:00
|
|
|
|
|
|
|
if (!(session = get_server_session(session_id)))
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
if (!(group = heap_alloc_zero(sizeof(*group))))
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
list_add_tail(&url_groups, &group->entry);
|
|
|
|
list_add_tail(&session->groups, &group->session_entry);
|
|
|
|
|
|
|
|
*group_id = (ULONG_PTR)group;
|
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpCloseUrlGroup (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpCloseUrlGroup(HTTP_URL_GROUP_ID id)
|
|
|
|
{
|
|
|
|
struct url_group *group;
|
|
|
|
|
2019-08-30 02:58:04 +02:00
|
|
|
TRACE("id %s.\n", wine_dbgstr_longlong(id));
|
2019-08-29 03:45:31 +02:00
|
|
|
|
|
|
|
if (!(group = get_url_group(id)))
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
list_remove(&group->session_entry);
|
|
|
|
list_remove(&group->entry);
|
|
|
|
heap_free(group);
|
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2019-08-30 02:58:04 +02:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpSetUrlGroupProperty (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpSetUrlGroupProperty(HTTP_URL_GROUP_ID id, HTTP_SERVER_PROPERTY property, void *value, ULONG length)
|
|
|
|
{
|
|
|
|
struct url_group *group = get_url_group(id);
|
|
|
|
|
|
|
|
TRACE("id %s, property %u, value %p, length %u.\n",
|
|
|
|
wine_dbgstr_longlong(id), property, value, length);
|
|
|
|
|
2020-02-29 04:45:32 +01:00
|
|
|
switch (property)
|
2019-08-30 02:58:04 +02:00
|
|
|
{
|
2020-02-29 04:45:32 +01:00
|
|
|
case HttpServerBindingProperty:
|
|
|
|
{
|
|
|
|
const HTTP_BINDING_INFO *info = value;
|
2019-08-30 02:58:05 +02:00
|
|
|
|
2020-02-29 04:45:32 +01:00
|
|
|
TRACE("Binding to queue %p.\n", info->RequestQueueHandle);
|
|
|
|
group->queue = info->RequestQueueHandle;
|
|
|
|
if (group->url)
|
|
|
|
add_url(group->queue, group->url, group->context);
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
case HttpServerLoggingProperty:
|
|
|
|
WARN("Ignoring logging property.\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
default:
|
|
|
|
FIXME("Unhandled property %u.\n", property);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
|
|
}
|
2019-08-30 02:58:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpAddUrlToUrlGroup (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpAddUrlToUrlGroup(HTTP_URL_GROUP_ID id, const WCHAR *url,
|
|
|
|
HTTP_URL_CONTEXT context, ULONG reserved)
|
|
|
|
{
|
|
|
|
struct url_group *group = get_url_group(id);
|
|
|
|
|
|
|
|
TRACE("id %s, url %s, context %s, reserved %#x.\n", wine_dbgstr_longlong(id),
|
|
|
|
debugstr_w(url), wine_dbgstr_longlong(context), reserved);
|
|
|
|
|
|
|
|
if (group->url)
|
|
|
|
{
|
|
|
|
FIXME("Multiple URLs are not handled!\n");
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(group->url = heap_strdupW(url)))
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
group->context = context;
|
|
|
|
|
|
|
|
if (group->queue)
|
|
|
|
return add_url(group->queue, url, context);
|
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpRemoveUrlFromUrlGroup (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpRemoveUrlFromUrlGroup(HTTP_URL_GROUP_ID id, const WCHAR *url, ULONG flags)
|
|
|
|
{
|
|
|
|
struct url_group *group = get_url_group(id);
|
|
|
|
|
|
|
|
TRACE("id %s, url %s, flags %#x.\n", wine_dbgstr_longlong(id), debugstr_w(url), flags);
|
|
|
|
|
|
|
|
if (!group->url)
|
|
|
|
return ERROR_FILE_NOT_FOUND;
|
|
|
|
|
|
|
|
if (flags)
|
|
|
|
FIXME("Ignoring flags %#x.\n", flags);
|
|
|
|
|
|
|
|
heap_free(group->url);
|
|
|
|
group->url = NULL;
|
|
|
|
|
|
|
|
if (group->queue)
|
|
|
|
return remove_url(group->queue, url);
|
|
|
|
|
2019-08-30 02:58:04 +02:00
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2019-08-30 02:58:06 +02:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpCreateRequestQueue (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpCreateRequestQueue(HTTPAPI_VERSION version, const WCHAR *name,
|
|
|
|
SECURITY_ATTRIBUTES *sa, ULONG flags, HANDLE *handle)
|
|
|
|
{
|
|
|
|
OBJECT_ATTRIBUTES attr = {sizeof(attr)};
|
|
|
|
UNICODE_STRING string;
|
|
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
|
|
|
|
TRACE("version %u.%u, name %s, sa %p, flags %#x, handle %p.\n",
|
|
|
|
version.HttpApiMajorVersion, version.HttpApiMinorVersion,
|
|
|
|
debugstr_w(name), sa, flags, handle);
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
FIXME("Unhandled name %s.\n", debugstr_w(name));
|
|
|
|
if (flags)
|
|
|
|
FIXME("Unhandled flags %#x.\n", flags);
|
|
|
|
|
|
|
|
RtlInitUnicodeString(&string, device_nameW);
|
|
|
|
attr.ObjectName = &string;
|
|
|
|
if (sa && sa->bInheritHandle)
|
|
|
|
attr.Attributes |= OBJ_INHERIT;
|
|
|
|
attr.SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL;
|
|
|
|
return RtlNtStatusToDosError(NtCreateFile(handle, 0, &attr, &iosb, NULL,
|
|
|
|
FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_NON_DIRECTORY_FILE, NULL, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpCloseRequestQueue (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpCloseRequestQueue(HANDLE handle)
|
|
|
|
{
|
|
|
|
TRACE("handle %p.\n", handle);
|
|
|
|
if (!CloseHandle(handle))
|
|
|
|
return GetLastError();
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2020-01-27 17:56:39 +01:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpSetRequestQueueProperty (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpSetRequestQueueProperty(HANDLE queue, HTTP_SERVER_PROPERTY property,
|
|
|
|
void *value, ULONG length, ULONG reserved1, void *reserved2)
|
|
|
|
{
|
|
|
|
FIXME("queue %p, property %u, value %p, length %u, reserved1 %#x, reserved2 %p, stub!\n",
|
|
|
|
queue, property, value, length, reserved1, reserved2);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
|
|
}
|
2020-02-29 04:45:30 +01:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* HttpSetServerSessionProperty (HTTPAPI.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI HttpSetServerSessionProperty(HTTP_SERVER_SESSION_ID id,
|
|
|
|
HTTP_SERVER_PROPERTY property, void *value, ULONG length)
|
|
|
|
{
|
|
|
|
TRACE("id %s, property %u, value %p, length %u.\n",
|
|
|
|
wine_dbgstr_longlong(id), property, value, length);
|
|
|
|
|
|
|
|
switch (property)
|
|
|
|
{
|
|
|
|
case HttpServerQosProperty:
|
|
|
|
{
|
|
|
|
const HTTP_QOS_SETTING_INFO *info = value;
|
|
|
|
FIXME("Ignoring QoS setting %u.\n", info->QosType);
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
FIXME("Unhandled property %u.\n", property);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
}
|