686 lines
18 KiB
C++
686 lines
18 KiB
C++
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
add.cxx
|
||
|
||
Abstract:
|
||
|
||
This file contains the implementation of the HttpAddRequestHeadersA API.
|
||
|
||
The following functions are exported by this module:
|
||
|
||
HttpAddRequestHeadersA
|
||
WinHttpAddRequestHeaders
|
||
|
||
Author:
|
||
|
||
Keith Moore (keithmo) 16-Nov-1994
|
||
|
||
Revision History:
|
||
|
||
Modified to make HttpAddRequestHeadersA remotable. madana (2/8/95)
|
||
|
||
--*/
|
||
|
||
#include <wininetp.h>
|
||
#include "httpp.h"
|
||
|
||
//
|
||
// private manifests
|
||
//
|
||
|
||
#define VALID_ADD_FLAGS (HTTP_ADDREQ_FLAG_ADD_IF_NEW \
|
||
| HTTP_ADDREQ_FLAG_ADD \
|
||
| HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA \
|
||
| HTTP_ADDREQ_FLAG_REPLACE \
|
||
| HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON \
|
||
)
|
||
|
||
//
|
||
// functions
|
||
//
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
HttpAddRequestHeadersA(
|
||
IN HINTERNET hRequest,
|
||
IN LPCSTR lpszHeaders,
|
||
IN DWORD dwHeadersLength,
|
||
IN DWORD dwModifiers
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Appends additional header(s) to an HTTP request handle
|
||
|
||
Arguments:
|
||
|
||
hRequest - An open HTTP request handle returned by HttpOpenRequest()
|
||
|
||
lpszHeaders - The headers to append to the request. Each header must be
|
||
terminated by a CR/LF pair.
|
||
|
||
dwHeadersLength - The length (in characters) of the headers. If this is -1L
|
||
then lpszHeaders is assumed to be zero terminated (ASCIIZ)
|
||
|
||
dwModifiers - flags controlling operation. Can be one or more of:
|
||
|
||
HTTP_ADDREQ_FLAG_ADD_IF_NEW
|
||
- add the header, but only if it does not already
|
||
exist. Index must be zero
|
||
|
||
HTTP_ADDREQ_FLAG_ADD
|
||
- if HTTP_ADDREQ_FLAG_REPLACE is set, but the header
|
||
is not found and this flag is set then the header
|
||
is added, so long as there is a valid header-value
|
||
|
||
HTTP_ADDREQ_FLAG_COALESCE
|
||
HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON
|
||
HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA
|
||
- concatenate headers of same name. E.g. if we
|
||
already have "Accept: text/html" then adding
|
||
"Accept: text/*" will create
|
||
"Accept: text/html, text/*"
|
||
|
||
HTTP_ADDREQ_FLAG_REPLACE
|
||
- replaces the named header. Only one header can be
|
||
supplied. If header-value is empty then the header
|
||
is removed
|
||
|
||
Return Value:
|
||
|
||
Success - TRUE
|
||
The header was appended successfully
|
||
|
||
Failure - FALSE
|
||
The operation failed. Error status is available by calling
|
||
GetLastError()
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_API,
|
||
Bool,
|
||
"HttpAddRequestHeadersA",
|
||
"%#x, %.80q, %d, %#x",
|
||
hRequest,
|
||
lpszHeaders,
|
||
dwHeadersLength,
|
||
dwModifiers
|
||
));
|
||
|
||
DWORD error;
|
||
HINTERNET hRequestMapped = NULL;
|
||
DWORD nestingLevel = 0;
|
||
|
||
if (!GlobalDataInitialized) {
|
||
error = ERROR_WINHTTP_NOT_INITIALIZED;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// get the thread info
|
||
//
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
if (lpThreadInfo == NULL) {
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// map the handle
|
||
//
|
||
|
||
error = MapHandleToAddress(hRequest, (LPVOID *)&hRequestMapped, FALSE);
|
||
if (error != ERROR_SUCCESS) {
|
||
goto quit;
|
||
}
|
||
|
||
_InternetIncNestingCount();
|
||
nestingLevel = 1;
|
||
|
||
//
|
||
// validate handle
|
||
//
|
||
|
||
BOOL isLocal;
|
||
BOOL isAsync;
|
||
|
||
error = RIsHandleLocal(hRequestMapped,
|
||
&isLocal,
|
||
&isAsync,
|
||
TypeHttpRequestHandle
|
||
);
|
||
if (error != ERROR_SUCCESS) {
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// validate parameters
|
||
//
|
||
|
||
INET_ASSERT(!(
|
||
(lpszHeaders == NULL)
|
||
|| (*lpszHeaders == '\0')
|
||
|| (dwHeadersLength == 0)
|
||
|| (dwModifiers & (HTTP_ADDREQ_FLAGS_MASK & ~VALID_ADD_FLAGS))) );
|
||
|
||
INET_ASSERT(error == ERROR_SUCCESS);
|
||
|
||
//
|
||
// BUGBUG - we should determine whether the app is trying to give us a bogus
|
||
// header, and whether the header conforms to the format:
|
||
//
|
||
// "<header>[:[ <value>]]"
|
||
//
|
||
|
||
if (dwHeadersLength == (DWORD)-1)
|
||
{
|
||
dwHeadersLength = (DWORD)lstrlen(lpszHeaders);
|
||
}
|
||
|
||
if (error == ERROR_SUCCESS) {
|
||
error = wHttpAddRequestHeaders(hRequestMapped,
|
||
lpszHeaders,
|
||
dwHeadersLength,
|
||
dwModifiers
|
||
);
|
||
}
|
||
|
||
quit:
|
||
|
||
_InternetDecNestingCount(nestingLevel);
|
||
|
||
done:
|
||
|
||
if (error != ERROR_SUCCESS) {
|
||
|
||
DEBUG_ERROR(HTTP, error);
|
||
|
||
SetLastError(error);
|
||
}
|
||
|
||
if (hRequestMapped != NULL) {
|
||
DereferenceObject((LPVOID)hRequestMapped);
|
||
}
|
||
|
||
DEBUG_LEAVE(error == ERROR_SUCCESS);
|
||
|
||
return error == ERROR_SUCCESS;
|
||
}
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
WinHttpAddRequestHeaders(
|
||
IN HINTERNET hRequest,
|
||
IN LPCWSTR lpszHeaders,
|
||
IN DWORD dwHeadersLength,
|
||
IN DWORD dwModifiers
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Appends additional header(s) to an HTTP request handle.
|
||
|
||
Arguments:
|
||
|
||
hHttpRequest - An open HTTP request handle returned by HttpOpenRequest().
|
||
|
||
lpszHeaders - The headers to append to the request. Each header must be
|
||
terminated by a CR/LF pair.
|
||
|
||
dwHeadersLength - The length (in characters) of the headers. If this is
|
||
-1L, then lpszHeaders is assumed to be zero terminated (ASCIIZ).
|
||
|
||
dwModifiers -
|
||
|
||
Return Value:
|
||
|
||
TRUE - The header was appended successfully.
|
||
|
||
FALSE - The operation failed. Error status is available by calling
|
||
GetLastError().
|
||
|
||
Comments:
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Bool,
|
||
"WinHttpAddRequestHeaders",
|
||
"%#x, %.80wq, %d, %#x",
|
||
hRequest,
|
||
lpszHeaders,
|
||
dwHeadersLength,
|
||
dwModifiers
|
||
));
|
||
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
BOOL fResult = FALSE;
|
||
|
||
if (!lpszHeaders
|
||
|| *lpszHeaders==L'\0'
|
||
|| !dwHeadersLength
|
||
|| ((dwHeadersLength == -1)
|
||
? IsBadStringPtrW(lpszHeaders, (UINT_PTR)-1)
|
||
: IsBadReadPtr(lpszHeaders, dwHeadersLength))
|
||
|| (dwModifiers & (HTTP_ADDREQ_FLAGS_MASK & ~VALID_ADD_FLAGS)))
|
||
{
|
||
dwErr = ERROR_INVALID_PARAMETER;
|
||
}
|
||
else
|
||
{
|
||
MEMORYPACKET mpHeaders;
|
||
ALLOC_MB(lpszHeaders, (dwHeadersLength==-1L ? 0 : dwHeadersLength), mpHeaders);
|
||
if (mpHeaders.psStr)
|
||
{
|
||
UNICODE_TO_ANSI(lpszHeaders, mpHeaders);
|
||
fResult = HttpAddRequestHeadersA(hRequest, mpHeaders.psStr, mpHeaders.dwSize, dwModifiers);
|
||
}
|
||
else
|
||
{
|
||
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
}
|
||
|
||
if (dwErr!=ERROR_SUCCESS)
|
||
{
|
||
SetLastError(dwErr);
|
||
DEBUG_ERROR(HTTP, dwErr);
|
||
}
|
||
DEBUG_LEAVE_API(fResult);
|
||
return fResult;
|
||
}
|
||
|
||
#define DEFAULT_BEGIN_SIZE 8
|
||
#define EXPAND_SIZE 8
|
||
|
||
static const CHAR IsValidHeaderNameChar[] =
|
||
{
|
||
// 0 1 2 3 4 5 6 7
|
||
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
|
||
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
|
||
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
|
||
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
|
||
|
||
// ' ' '"'
|
||
FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE,
|
||
// '(' ')' ',' '/'
|
||
FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE,
|
||
TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
|
||
// ':' ';' '<' '=' '>' '?'
|
||
TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
|
||
|
||
// '@'
|
||
FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
|
||
TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
|
||
TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
|
||
// '[' '\' ']'
|
||
TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE,
|
||
|
||
TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
|
||
TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
|
||
TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
|
||
// '{'' '}' DEL
|
||
TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE
|
||
};
|
||
|
||
BOOL IsValidHeaderName(LPCWSTR lpszHeaderName)
|
||
{
|
||
WCHAR wch;
|
||
int nIndex=0;
|
||
|
||
for ( ; (wch=lpszHeaderName[nIndex]) != 0; nIndex++)
|
||
{
|
||
if ((wch > ARRAY_ELEMENTS(IsValidHeaderNameChar))
|
||
|| !IsValidHeaderNameChar[wch])
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
typedef struct _headerrec
|
||
{
|
||
LPSTR lpName;
|
||
int nName;
|
||
LPSTR lpValue;
|
||
int nValue;
|
||
}
|
||
HEADER_REC;
|
||
|
||
|
||
PUBLIC
|
||
DWORD
|
||
wHttpAddRequestHeaders(
|
||
IN HINTERNET hRequest,
|
||
IN LPCSTR lpszHeaders,
|
||
IN DWORD dwHeadersLength,
|
||
IN DWORD dwModifiers
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Worker function to append additional header(s) to an HTTP request handle
|
||
|
||
Arguents:
|
||
|
||
hRequest - handle of HTTP request
|
||
|
||
lpszHeaders - pointer to buffer containing one or more headers
|
||
|
||
dwHeadersLength - length of lpszHeaders. Cannot be -1 at this stage
|
||
|
||
dwModifiers - flags controlling operation
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_INVALID_PARAMETER
|
||
The header string(s) was bad after all
|
||
|
||
ERROR_WINHTTP_INCORRECT_HANDLE_STATE
|
||
We can't add headers to this object at this time
|
||
|
||
ERROR_HTTP_HEADER_NOT_FOUND
|
||
We were asked to replace a header, but couldn't find it
|
||
|
||
ERROR_HTTP_HEADER_ALREADY_EXISTS
|
||
We were asked to add a header, only if one of the same name
|
||
doesn't already exist. It does
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// dwHeadersLength cannot be -1 or 0 at this stage. Nor can lpszHeaders be
|
||
// NULL
|
||
//
|
||
|
||
INET_ASSERT(lpszHeaders != NULL);
|
||
INET_ASSERT(dwHeadersLength != (DWORD)-1);
|
||
INET_ASSERT(dwHeadersLength != 0);
|
||
|
||
DEBUG_ENTER((DBG_HTTP,
|
||
Dword,
|
||
"wHttpAddRequestHeaders",
|
||
"%#x, %#x [%.80q], %d, %#x",
|
||
hRequest,
|
||
lpszHeaders,
|
||
lpszHeaders,
|
||
dwHeadersLength,
|
||
dwModifiers
|
||
));
|
||
|
||
//
|
||
// get the underlying object and check that we can add headers
|
||
//
|
||
|
||
HTTP_REQUEST_HANDLE_OBJECT * pRequest;
|
||
|
||
pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)hRequest;
|
||
HEADER_REC* pHeaders = NULL;
|
||
|
||
DWORD error;
|
||
|
||
if (!IS_VALID_HTTP_STATE(pRequest, ADD, TRUE))
|
||
{
|
||
error = ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
|
||
goto quit;
|
||
}
|
||
|
||
DWORD offset;
|
||
LPSTR header;
|
||
|
||
offset = 0;
|
||
header = (LPSTR)lpszHeaders;
|
||
|
||
int nCount = DEFAULT_BEGIN_SIZE;
|
||
pHeaders = (HEADER_REC *)ALLOCATE_FIXED_MEMORY (sizeof(HEADER_REC)*nCount);
|
||
int nHeader = 0;
|
||
if (!pHeaders)
|
||
{
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto quit;
|
||
}
|
||
|
||
error = ERROR_SUCCESS;
|
||
|
||
do
|
||
{
|
||
//
|
||
// first time: ignore any empty strings; subsequent time: clean off any
|
||
// trailing line termination
|
||
//
|
||
|
||
while ((offset < dwHeadersLength)
|
||
&& ((lpszHeaders[offset] == '\r') || (lpszHeaders[offset] == '\n')))
|
||
{
|
||
++offset;
|
||
}
|
||
|
||
if (offset == dwHeadersLength)
|
||
{
|
||
//
|
||
// even if app tried adding empty line(s), we return success
|
||
//
|
||
|
||
error = ERROR_SUCCESS;
|
||
break;
|
||
}
|
||
|
||
DWORD length;
|
||
DWORD nameLength;
|
||
DWORD valueLength;
|
||
LPSTR value;
|
||
|
||
nameLength = 0;
|
||
valueLength = 0;
|
||
value = NULL;
|
||
|
||
//
|
||
// break the header into header-name, header-value pairs. Exclude CR-LF
|
||
// from the header-value (if present)
|
||
//
|
||
|
||
for (length = 0, header = (LPSTR)&lpszHeaders[offset];
|
||
offset < dwHeadersLength;
|
||
++length, ++offset)
|
||
{
|
||
char ch = header[length];
|
||
|
||
if (ch == '\r')
|
||
{
|
||
//
|
||
// end of this particular header?
|
||
//
|
||
if (((offset+2) < dwHeadersLength)
|
||
&& nameLength
|
||
&& (header[length+1] == '\n')
|
||
&& ((header[length+2] == ' ')
|
||
|| (header[length+2] == '\t'))
|
||
)
|
||
{
|
||
//LWS allowing header to spill over to next line
|
||
length+=2;
|
||
offset+=2;
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
else if (ch == '\n')
|
||
{
|
||
//
|
||
// end of this particular header?
|
||
//
|
||
break;
|
||
}
|
||
else if (ch == ':')
|
||
{
|
||
if (nameLength == 0)
|
||
{
|
||
//
|
||
// found end of header name
|
||
//
|
||
|
||
nameLength = length;
|
||
value = &header[length];
|
||
}
|
||
}
|
||
|
||
if (nameLength == 0)
|
||
{
|
||
if ((ch > ARRAY_ELEMENTS(IsValidHeaderNameChar))
|
||
|| !IsValidHeaderNameChar[ch])
|
||
{
|
||
error = ERROR_INVALID_PARAMETER;
|
||
goto quit;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (length == 0)
|
||
{
|
||
//
|
||
// empty string
|
||
//
|
||
|
||
continue;
|
||
}
|
||
else if (nameLength == 0)
|
||
{
|
||
//
|
||
// entry consists of just header-name (e.g. "Accept[\r\n]")
|
||
//
|
||
|
||
nameLength = length;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// find the start of the header-value
|
||
//
|
||
|
||
valueLength = (DWORD) (header + length - value);
|
||
|
||
//ideally we wouldn't eat through all the leading white space or : in the
|
||
// header value because they may be significant.
|
||
// Don't know of any examples though, so can keep it as such.
|
||
//
|
||
// N.B. We are allowing any mixture of ':' and ' ' between header
|
||
// name and value, but this is probably not a big deal...
|
||
//
|
||
|
||
while ((*value == ':') || (*value == ' ') && (valueLength != 0))
|
||
{
|
||
++value;
|
||
--valueLength;
|
||
}
|
||
}
|
||
|
||
if (!value
|
||
&& !(dwModifiers & (HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD_IF_NEW)))
|
||
{
|
||
error = ERROR_INVALID_PARAMETER;
|
||
goto quit;
|
||
}
|
||
|
||
pHeaders[nHeader].lpName = header;
|
||
pHeaders[nHeader].nName = nameLength;
|
||
pHeaders[nHeader].lpValue = value;
|
||
pHeaders[nHeader].nValue = valueLength;
|
||
|
||
if (++nHeader >= nCount)
|
||
{
|
||
nCount += EXPAND_SIZE;
|
||
|
||
HEADER_REC* pNewHeaders = (HEADER_REC *)REALLOCATE_MEMORY(pHeaders, sizeof(HEADER_REC)*nCount);
|
||
|
||
if (pNewHeaders)
|
||
pHeaders = pNewHeaders;
|
||
else
|
||
{
|
||
FREE_MEMORY(pHeaders);
|
||
pHeaders = NULL;
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto quit;
|
||
}
|
||
}
|
||
}
|
||
while (error == ERROR_SUCCESS);
|
||
|
||
for (int nIndex=0; nIndex<nHeader; nIndex++)
|
||
{
|
||
if (dwModifiers
|
||
& (HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD_IF_NEW))
|
||
{
|
||
//
|
||
// replace or remove the header
|
||
//
|
||
|
||
error = pRequest->ReplaceRequestHeader(
|
||
pHeaders[nIndex].lpName,
|
||
pHeaders[nIndex].nName,
|
||
pHeaders[nIndex].lpValue,
|
||
pHeaders[nIndex].nValue,
|
||
dwModifiers & HTTP_ADDREQ_INDEX_MASK,
|
||
dwModifiers & HTTP_ADDREQ_FLAGS_MASK
|
||
);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// add a single, unterminated header string to the request headers.
|
||
// Since these headers came from the app, we don't trust it to get
|
||
// the header termination right (number & type of line terminators)
|
||
// so we add it ourselves
|
||
//
|
||
|
||
error = pRequest->AddRequestHeader(
|
||
pHeaders[nIndex].lpName,
|
||
pHeaders[nIndex].nName,
|
||
pHeaders[nIndex].lpValue,
|
||
pHeaders[nIndex].nValue,
|
||
dwModifiers & HTTP_ADDREQ_INDEX_MASK,
|
||
dwModifiers & HTTP_ADDREQ_FLAGS_MASK
|
||
);
|
||
}
|
||
|
||
if (error != ERROR_SUCCESS)
|
||
break;
|
||
}
|
||
|
||
quit:
|
||
|
||
if (pHeaders)
|
||
{
|
||
FREE_MEMORY(pHeaders);
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
return error;
|
||
}
|