/*++ 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 #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: // // "
[:[ ]]" // 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; nIndexReplaceRequestHeader( 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; }