2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 * Copyright 2011 Jacek Caban for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
70 #include "wine/debug.h"
71 #include "wine/exception.h"
72 #include "wine/unicode.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
76 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR szOK[] = {'O','K',0};
79 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
80 static const WCHAR hostW[] = { 'H','o','s','t',0 };
81 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
84 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
85 static const WCHAR szGET[] = { 'G','E','T', 0 };
86 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
87 static const WCHAR szCrLf[] = {'\r','\n', 0};
89 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
90 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
91 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
92 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
94 static const WCHAR szAge[] = { 'A','g','e',0 };
95 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
96 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
97 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
98 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
99 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
101 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
102 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
103 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
104 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
105 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
106 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
107 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
108 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
109 static const WCHAR szDate[] = { 'D','a','t','e',0 };
110 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
111 static const WCHAR szETag[] = { 'E','T','a','g',0 };
112 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
113 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
114 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
116 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
118 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
120 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
121 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
122 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
123 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
124 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
125 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
126 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
127 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
128 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
129 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
130 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
131 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
132 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
133 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
134 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
135 static const WCHAR szURI[] = { 'U','R','I',0 };
136 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
137 static const WCHAR szVary[] = { 'V','a','r','y',0 };
138 static const WCHAR szVia[] = { 'V','i','a',0 };
139 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
140 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
142 #define MAXHOSTNAME 100
143 #define MAX_FIELD_VALUE_LEN 256
144 #define MAX_FIELD_LEN 256
146 #define HTTP_REFERER szReferer
147 #define HTTP_ACCEPT szAccept
148 #define HTTP_USERAGENT szUser_Agent
150 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
151 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
152 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
154 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
155 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
156 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
169 unsigned int auth_data_len;
170 BOOL finished; /* finished authenticating */
174 typedef struct _basicAuthorizationData
181 UINT authorizationLen;
182 } basicAuthorizationData;
184 typedef struct _authorizationData
198 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
199 static struct list authorizationCache = LIST_INIT(authorizationCache);
201 static CRITICAL_SECTION authcache_cs;
202 static CRITICAL_SECTION_DEBUG critsect_debug =
205 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
206 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
208 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
210 static DWORD HTTP_OpenConnection(http_request_t *req);
211 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
212 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
213 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
214 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
215 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
216 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
217 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
218 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
219 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
220 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
221 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
222 static void HTTP_DrainContent(http_request_t *req);
223 static BOOL HTTP_FinishedReading(http_request_t *req);
225 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
228 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
229 if (HeaderIndex == -1)
232 return &req->custHeaders[HeaderIndex];
241 struct data_stream_vtbl_t {
242 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
243 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
244 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
245 void (*destroy)(data_stream_t*);
249 data_stream_t data_stream;
251 BYTE buf[READ_BUFFER_SIZE];
257 static inline void destroy_data_stream(data_stream_t *stream)
259 stream->vtbl->destroy(stream);
262 static void reset_data_stream(http_request_t *req)
264 destroy_data_stream(req->data_stream);
265 req->data_stream = &req->netconn_stream.data_stream;
266 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
267 req->read_chunked = req->read_gzip = FALSE;
273 data_stream_t stream;
274 data_stream_t *parent_stream;
276 BYTE buf[READ_BUFFER_SIZE];
282 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
284 /* Allow reading only from read buffer */
288 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
290 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
291 return gzip_stream->end_of_data;
294 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
295 DWORD *read, read_mode_t read_mode)
297 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
298 z_stream *zstream = &gzip_stream->zstream;
299 DWORD current_read, ret_read = 0;
302 DWORD res = ERROR_SUCCESS;
304 while(size && !gzip_stream->end_of_data) {
305 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
307 if(gzip_stream->buf_size <= 64 && !end) {
308 if(gzip_stream->buf_pos) {
309 if(gzip_stream->buf_size)
310 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
311 gzip_stream->buf_pos = 0;
313 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
314 sizeof(gzip_stream->buf)-gzip_stream->buf_size, ¤t_read, read_mode);
315 gzip_stream->buf_size += current_read;
316 if(res != ERROR_SUCCESS)
318 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
319 if(!current_read && !end) {
320 if(read_mode != READMODE_NOBLOCK) {
321 WARN("unexpected end of data\n");
322 gzip_stream->end_of_data = TRUE;
326 if(gzip_stream->buf_size <= 64 && !end)
330 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
331 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
332 zstream->next_out = buf+ret_read;
333 zstream->avail_out = size;
334 zres = inflate(&gzip_stream->zstream, 0);
335 current_read = size - zstream->avail_out;
336 size -= current_read;
337 ret_read += current_read;
338 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
339 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
340 if(zres == Z_STREAM_END) {
341 TRACE("end of data\n");
342 gzip_stream->end_of_data = TRUE;
344 }else if(zres != Z_OK) {
345 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
347 res = ERROR_INTERNET_DECODING_FAILED;
351 if(ret_read && read_mode == READMODE_ASYNC)
352 read_mode = READMODE_NOBLOCK;
355 TRACE("read %u bytes\n", ret_read);
360 static void gzip_destroy(data_stream_t *stream)
362 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
364 destroy_data_stream(gzip_stream->parent_stream);
366 if(!gzip_stream->end_of_data)
367 inflateEnd(&gzip_stream->zstream);
368 heap_free(gzip_stream);
371 static const data_stream_vtbl_t gzip_stream_vtbl = {
378 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
380 return heap_alloc(items*size);
383 static void wininet_zfree(voidpf opaque, voidpf address)
385 HeapFree(GetProcessHeap(), 0, address);
388 static DWORD init_gzip_stream(http_request_t *req)
390 gzip_stream_t *gzip_stream;
393 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
395 return ERROR_OUTOFMEMORY;
397 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
398 gzip_stream->zstream.zalloc = wininet_zalloc;
399 gzip_stream->zstream.zfree = wininet_zfree;
401 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
403 ERR("inflateInit failed: %d\n", zres);
404 HeapFree(GetProcessHeap(), 0, gzip_stream);
405 return ERROR_OUTOFMEMORY;
408 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
410 HTTP_DeleteCustomHeader(req, index);
413 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
414 gzip_stream->buf_size = req->read_size;
415 req->read_pos = req->read_size = 0;
418 req->read_gzip = TRUE;
419 gzip_stream->parent_stream = req->data_stream;
420 req->data_stream = &gzip_stream->stream;
421 return ERROR_SUCCESS;
426 static DWORD init_gzip_stream(http_request_t *req)
428 ERR("gzip stream not supported, missing zlib.\n");
429 return ERROR_SUCCESS;
434 /***********************************************************************
435 * HTTP_Tokenize (internal)
437 * Tokenize a string, allocating memory for the tokens.
439 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
441 LPWSTR * token_array;
448 /* empty string has no tokens */
452 for (i = 0; string[i]; i++)
454 if (!strncmpW(string+i, token_string, strlenW(token_string)))
458 /* we want to skip over separators, but not the null terminator */
459 for (j = 0; j < strlenW(token_string) - 1; j++)
467 /* add 1 for terminating NULL */
468 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
469 token_array[tokens] = NULL;
472 for (i = 0; i < tokens; i++)
475 next_token = strstrW(string, token_string);
476 if (!next_token) next_token = string+strlenW(string);
477 len = next_token - string;
478 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
479 memcpy(token_array[i], string, len*sizeof(WCHAR));
480 token_array[i][len] = '\0';
481 string = next_token+strlenW(token_string);
486 /***********************************************************************
487 * HTTP_FreeTokens (internal)
489 * Frees memory returned from HTTP_Tokenize.
491 static void HTTP_FreeTokens(LPWSTR * token_array)
494 for (i = 0; token_array[i]; i++)
495 HeapFree(GetProcessHeap(), 0, token_array[i]);
496 HeapFree(GetProcessHeap(), 0, token_array);
499 static void HTTP_FixURL(http_request_t *request)
501 static const WCHAR szSlash[] = { '/',0 };
502 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
504 /* If we don't have a path we set it to root */
505 if (NULL == request->path)
506 request->path = heap_strdupW(szSlash);
507 else /* remove \r and \n*/
509 int nLen = strlenW(request->path);
510 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
513 request->path[nLen]='\0';
515 /* Replace '\' with '/' */
518 if (request->path[nLen] == '\\') request->path[nLen]='/';
522 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
523 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
524 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
526 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
528 strcpyW(fixurl + 1, request->path);
529 HeapFree( GetProcessHeap(), 0, request->path );
530 request->path = fixurl;
534 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
536 LPWSTR requestString;
542 static const WCHAR szSpace[] = { ' ',0 };
543 static const WCHAR szColon[] = { ':',' ',0 };
544 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
546 /* allocate space for an array of all the string pointers to be added */
547 len = (request->nCustHeaders)*4 + 10;
548 req = heap_alloc(len*sizeof(LPCWSTR));
550 /* add the verb, path and HTTP version string */
558 /* Append custom request headers */
559 for (i = 0; i < request->nCustHeaders; i++)
561 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
564 req[n++] = request->custHeaders[i].lpszField;
566 req[n++] = request->custHeaders[i].lpszValue;
568 TRACE("Adding custom header %s (%s)\n",
569 debugstr_w(request->custHeaders[i].lpszField),
570 debugstr_w(request->custHeaders[i].lpszValue));
575 ERR("oops. buffer overrun\n");
578 requestString = HTTP_build_req( req, 4 );
579 HeapFree( GetProcessHeap(), 0, req );
582 * Set (header) termination string for request
583 * Make sure there's exactly two new lines at the end of the request
585 p = &requestString[strlenW(requestString)-1];
586 while ( (*p == '\n') || (*p == '\r') )
588 strcpyW( p+1, sztwocrlf );
590 return requestString;
593 static void HTTP_ProcessCookies( http_request_t *request )
597 LPHTTPHEADERW setCookieHeader;
599 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies, FALSE)) != -1)
601 setCookieHeader = &request->custHeaders[HeaderIndex];
603 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
606 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
610 Host = HTTP_GetHeader(request, hostW);
611 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(request->path);
612 buf_url = heap_alloc(len*sizeof(WCHAR));
613 sprintfW(buf_url, szFmt, Host->lpszValue, request->path);
614 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
616 HeapFree(GetProcessHeap(), 0, buf_url);
622 static void strip_spaces(LPWSTR start)
627 while (*str == ' ' && *str != '\0')
631 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
633 end = start + strlenW(start) - 1;
634 while (end >= start && *end == ' ')
641 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
643 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
644 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
646 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
647 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
648 if (is_basic && pszRealm)
651 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
655 token = strchrW(ptr,'=');
659 while (*realm == ' ' && *realm != '\0')
661 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
662 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
665 while (*token == ' ' && *token != '\0')
669 *pszRealm = heap_strdupW(token);
670 strip_spaces(*pszRealm);
677 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
679 if (!authinfo) return;
681 if (SecIsValidHandle(&authinfo->ctx))
682 DeleteSecurityContext(&authinfo->ctx);
683 if (SecIsValidHandle(&authinfo->cred))
684 FreeCredentialsHandle(&authinfo->cred);
686 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
687 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
688 HeapFree(GetProcessHeap(), 0, authinfo);
691 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
693 basicAuthorizationData *ad;
696 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
698 EnterCriticalSection(&authcache_cs);
699 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
701 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
703 TRACE("Authorization found in cache\n");
704 *auth_data = heap_alloc(ad->authorizationLen);
705 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
706 rc = ad->authorizationLen;
710 LeaveCriticalSection(&authcache_cs);
714 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
717 basicAuthorizationData* ad = NULL;
719 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
721 EnterCriticalSection(&authcache_cs);
722 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
724 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
725 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
734 TRACE("Found match in cache, replacing\n");
735 HeapFree(GetProcessHeap(),0,ad->authorization);
736 ad->authorization = heap_alloc(auth_data_len);
737 memcpy(ad->authorization, auth_data, auth_data_len);
738 ad->authorizationLen = auth_data_len;
742 ad = heap_alloc(sizeof(basicAuthorizationData));
743 ad->host = heap_strdupW(host);
744 ad->host = heap_strdupW(realm);
745 ad->authorization = heap_alloc(auth_data_len);
746 memcpy(ad->authorization, auth_data, auth_data_len);
747 ad->authorizationLen = auth_data_len;
748 list_add_head(&basicAuthorizationCache,&ad->entry);
749 TRACE("authorization cached\n");
751 LeaveCriticalSection(&authcache_cs);
754 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
755 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
757 authorizationData *ad;
759 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
761 EnterCriticalSection(&authcache_cs);
762 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
763 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
764 TRACE("Authorization found in cache\n");
766 nt_auth_identity->User = heap_strdupW(ad->user);
767 nt_auth_identity->Password = heap_strdupW(ad->password);
768 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
769 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
770 (!nt_auth_identity->Domain && ad->domain_len)) {
771 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
772 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
773 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
777 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
778 nt_auth_identity->UserLength = ad->user_len;
779 nt_auth_identity->PasswordLength = ad->password_len;
780 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
781 nt_auth_identity->DomainLength = ad->domain_len;
782 LeaveCriticalSection(&authcache_cs);
786 LeaveCriticalSection(&authcache_cs);
791 static void cache_authorization(LPWSTR host, LPWSTR scheme,
792 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
794 authorizationData *ad;
797 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
799 EnterCriticalSection(&authcache_cs);
800 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
801 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
807 HeapFree(GetProcessHeap(), 0, ad->user);
808 HeapFree(GetProcessHeap(), 0, ad->password);
809 HeapFree(GetProcessHeap(), 0, ad->domain);
811 ad = heap_alloc(sizeof(authorizationData));
813 LeaveCriticalSection(&authcache_cs);
817 ad->host = heap_strdupW(host);
818 ad->scheme = heap_strdupW(scheme);
819 list_add_head(&authorizationCache, &ad->entry);
822 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
823 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
824 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
825 ad->user_len = nt_auth_identity->UserLength;
826 ad->password_len = nt_auth_identity->PasswordLength;
827 ad->domain_len = nt_auth_identity->DomainLength;
829 if(!ad->host || !ad->scheme || !ad->user || !ad->password
830 || (nt_auth_identity->Domain && !ad->domain)) {
831 HeapFree(GetProcessHeap(), 0, ad->host);
832 HeapFree(GetProcessHeap(), 0, ad->scheme);
833 HeapFree(GetProcessHeap(), 0, ad->user);
834 HeapFree(GetProcessHeap(), 0, ad->password);
835 HeapFree(GetProcessHeap(), 0, ad->domain);
836 list_remove(&ad->entry);
837 HeapFree(GetProcessHeap(), 0, ad);
840 LeaveCriticalSection(&authcache_cs);
843 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
844 struct HttpAuthInfo **ppAuthInfo,
845 LPWSTR domain_and_username, LPWSTR password,
848 SECURITY_STATUS sec_status;
849 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
851 LPWSTR szRealm = NULL;
853 TRACE("%s\n", debugstr_w(pszAuthValue));
860 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
864 SecInvalidateHandle(&pAuthInfo->cred);
865 SecInvalidateHandle(&pAuthInfo->ctx);
866 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
868 pAuthInfo->auth_data = NULL;
869 pAuthInfo->auth_data_len = 0;
870 pAuthInfo->finished = FALSE;
872 if (is_basic_auth_value(pszAuthValue,NULL))
874 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
875 pAuthInfo->scheme = heap_strdupW(szBasic);
876 if (!pAuthInfo->scheme)
878 HeapFree(GetProcessHeap(), 0, pAuthInfo);
885 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
887 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
888 if (!pAuthInfo->scheme)
890 HeapFree(GetProcessHeap(), 0, pAuthInfo);
894 if (domain_and_username)
896 WCHAR *user = strchrW(domain_and_username, '\\');
897 WCHAR *domain = domain_and_username;
899 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
901 pAuthData = &nt_auth_identity;
906 user = domain_and_username;
910 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
911 nt_auth_identity.User = user;
912 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
913 nt_auth_identity.Domain = domain;
914 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
915 nt_auth_identity.Password = password;
916 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
918 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
920 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
921 pAuthData = &nt_auth_identity;
923 /* use default credentials */
926 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
927 SECPKG_CRED_OUTBOUND, NULL,
929 NULL, &pAuthInfo->cred,
932 if(pAuthData && !domain_and_username) {
933 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
934 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
935 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
938 if (sec_status == SEC_E_OK)
940 PSecPkgInfoW sec_pkg_info;
941 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
942 if (sec_status == SEC_E_OK)
944 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
945 FreeContextBuffer(sec_pkg_info);
948 if (sec_status != SEC_E_OK)
950 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
951 debugstr_w(pAuthInfo->scheme), sec_status);
952 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
953 HeapFree(GetProcessHeap(), 0, pAuthInfo);
957 *ppAuthInfo = pAuthInfo;
959 else if (pAuthInfo->finished)
962 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
963 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
965 ERR("authentication scheme changed from %s to %s\n",
966 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
970 if (is_basic_auth_value(pszAuthValue,&szRealm))
974 char *auth_data = NULL;
975 UINT auth_data_len = 0;
977 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
979 if (!domain_and_username)
982 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
983 if (auth_data_len == 0)
985 HeapFree(GetProcessHeap(),0,szRealm);
991 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
992 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
994 /* length includes a nul terminator, which will be re-used for the ':' */
995 auth_data = heap_alloc(userlen + 1 + passlen);
998 HeapFree(GetProcessHeap(),0,szRealm);
1002 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1003 auth_data[userlen] = ':';
1004 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1005 auth_data_len = userlen + 1 + passlen;
1006 if (host && szRealm)
1007 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1010 pAuthInfo->auth_data = auth_data;
1011 pAuthInfo->auth_data_len = auth_data_len;
1012 pAuthInfo->finished = TRUE;
1013 HeapFree(GetProcessHeap(),0,szRealm);
1019 LPCWSTR pszAuthData;
1020 SecBufferDesc out_desc, in_desc;
1022 unsigned char *buffer;
1023 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1024 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1026 in.BufferType = SECBUFFER_TOKEN;
1030 in_desc.ulVersion = 0;
1031 in_desc.cBuffers = 1;
1032 in_desc.pBuffers = ∈
1034 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1035 if (*pszAuthData == ' ')
1038 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1039 in.pvBuffer = heap_alloc(in.cbBuffer);
1040 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1043 buffer = heap_alloc(pAuthInfo->max_token);
1045 out.BufferType = SECBUFFER_TOKEN;
1046 out.cbBuffer = pAuthInfo->max_token;
1047 out.pvBuffer = buffer;
1049 out_desc.ulVersion = 0;
1050 out_desc.cBuffers = 1;
1051 out_desc.pBuffers = &out;
1053 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1054 first ? NULL : &pAuthInfo->ctx,
1055 first ? request->session->serverName : NULL,
1056 context_req, 0, SECURITY_NETWORK_DREP,
1057 in.pvBuffer ? &in_desc : NULL,
1058 0, &pAuthInfo->ctx, &out_desc,
1059 &pAuthInfo->attr, &pAuthInfo->exp);
1060 if (sec_status == SEC_E_OK)
1062 pAuthInfo->finished = TRUE;
1063 pAuthInfo->auth_data = out.pvBuffer;
1064 pAuthInfo->auth_data_len = out.cbBuffer;
1065 TRACE("sending last auth packet\n");
1067 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1069 pAuthInfo->auth_data = out.pvBuffer;
1070 pAuthInfo->auth_data_len = out.cbBuffer;
1071 TRACE("sending next auth packet\n");
1075 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1076 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
1077 destroy_authinfo(pAuthInfo);
1086 /***********************************************************************
1087 * HTTP_HttpAddRequestHeadersW (internal)
1089 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1090 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1095 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1097 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1099 if( dwHeaderLength == ~0U )
1100 len = strlenW(lpszHeader);
1102 len = dwHeaderLength;
1103 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1104 lstrcpynW( buffer, lpszHeader, len + 1);
1110 LPWSTR * pFieldAndValue;
1112 lpszEnd = lpszStart;
1114 while (*lpszEnd != '\0')
1116 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1121 if (*lpszStart == '\0')
1124 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1127 lpszEnd++; /* Jump over newline */
1129 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1130 if (*lpszStart == '\0')
1132 /* Skip 0-length headers */
1133 lpszStart = lpszEnd;
1134 res = ERROR_SUCCESS;
1137 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1140 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1141 if (res == ERROR_SUCCESS)
1142 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1143 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1144 HTTP_FreeTokens(pFieldAndValue);
1147 lpszStart = lpszEnd;
1148 } while (res == ERROR_SUCCESS);
1150 HeapFree(GetProcessHeap(), 0, buffer);
1155 /***********************************************************************
1156 * HttpAddRequestHeadersW (WININET.@)
1158 * Adds one or more HTTP header to the request handler
1161 * On Windows if dwHeaderLength includes the trailing '\0', then
1162 * HttpAddRequestHeadersW() adds it too. However this results in an
1163 * invalid Http header which is rejected by some servers so we probably
1164 * don't need to match Windows on that point.
1171 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1172 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1174 http_request_t *request;
1175 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1177 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1182 request = (http_request_t*) get_handle_object( hHttpRequest );
1183 if (request && request->hdr.htype == WH_HHTTPREQ)
1184 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1186 WININET_Release( &request->hdr );
1188 if(res != ERROR_SUCCESS)
1190 return res == ERROR_SUCCESS;
1193 /***********************************************************************
1194 * HttpAddRequestHeadersA (WININET.@)
1196 * Adds one or more HTTP header to the request handler
1203 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1204 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1210 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1212 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1213 hdr = heap_alloc(len*sizeof(WCHAR));
1214 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1215 if( dwHeaderLength != ~0U )
1216 dwHeaderLength = len;
1218 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1220 HeapFree( GetProcessHeap(), 0, hdr );
1225 /***********************************************************************
1226 * HttpOpenRequestA (WININET.@)
1228 * Open a HTTP request handle
1231 * HINTERNET a HTTP request handle on success
1235 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1236 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1237 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1238 DWORD dwFlags, DWORD_PTR dwContext)
1240 LPWSTR szVerb = NULL, szObjectName = NULL;
1241 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1242 INT acceptTypesCount;
1243 HINTERNET rc = FALSE;
1246 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1247 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1248 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1249 dwFlags, dwContext);
1253 szVerb = heap_strdupAtoW(lpszVerb);
1260 szObjectName = heap_strdupAtoW(lpszObjectName);
1261 if ( !szObjectName )
1267 szVersion = heap_strdupAtoW(lpszVersion);
1274 szReferrer = heap_strdupAtoW(lpszReferrer);
1279 if (lpszAcceptTypes)
1281 acceptTypesCount = 0;
1282 types = lpszAcceptTypes;
1287 /* find out how many there are */
1288 if (*types && **types)
1290 TRACE("accept type: %s\n", debugstr_a(*types));
1296 WARN("invalid accept type pointer\n");
1301 szAcceptTypes = heap_alloc(sizeof(WCHAR *) * (acceptTypesCount+1));
1302 if (!szAcceptTypes) goto end;
1304 acceptTypesCount = 0;
1305 types = lpszAcceptTypes;
1310 if (*types && **types)
1311 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1315 /* ignore invalid pointer */
1320 szAcceptTypes[acceptTypesCount] = NULL;
1323 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1324 szVersion, szReferrer,
1325 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1330 acceptTypesCount = 0;
1331 while (szAcceptTypes[acceptTypesCount])
1333 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1336 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1338 HeapFree(GetProcessHeap(), 0, szReferrer);
1339 HeapFree(GetProcessHeap(), 0, szVersion);
1340 HeapFree(GetProcessHeap(), 0, szObjectName);
1341 HeapFree(GetProcessHeap(), 0, szVerb);
1346 /***********************************************************************
1349 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1352 static const CHAR HTTP_Base64Enc[] =
1353 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1357 /* first 6 bits, all from bin[0] */
1358 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1359 x = (bin[0] & 3) << 4;
1361 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1364 base64[n++] = HTTP_Base64Enc[x];
1369 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1370 x = ( bin[1] & 0x0f ) << 2;
1372 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1375 base64[n++] = HTTP_Base64Enc[x];
1379 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1381 /* last 6 bits, all from bin [2] */
1382 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1390 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1391 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1392 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1393 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1394 static const signed char HTTP_Base64Dec[256] =
1396 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1397 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1398 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1399 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1400 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1401 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1402 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1403 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1404 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1405 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1406 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1407 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1408 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1409 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1410 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1411 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1412 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1413 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1414 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1415 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1416 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1417 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1418 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1419 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1420 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1421 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1425 /***********************************************************************
1428 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1436 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1437 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1438 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1439 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1441 WARN("invalid base64: %s\n", debugstr_w(base64));
1445 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1448 if ((base64[2] == '=') && (base64[3] == '='))
1450 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1451 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1453 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1457 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1460 if (base64[3] == '=')
1462 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1463 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1465 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1469 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1478 /***********************************************************************
1479 * HTTP_InsertAuthorization
1481 * Insert or delete the authorization field in the request header.
1483 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1487 static const WCHAR wszSpace[] = {' ',0};
1488 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1490 WCHAR *authorization = NULL;
1492 if (pAuthInfo->auth_data_len)
1494 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1495 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1496 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1500 strcpyW(authorization, pAuthInfo->scheme);
1501 strcatW(authorization, wszSpace);
1502 HTTP_EncodeBase64(pAuthInfo->auth_data,
1503 pAuthInfo->auth_data_len,
1504 authorization+strlenW(authorization));
1506 /* clear the data as it isn't valid now that it has been sent to the
1507 * server, unless it's Basic authentication which doesn't do
1508 * connection tracking */
1509 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1511 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1512 pAuthInfo->auth_data = NULL;
1513 pAuthInfo->auth_data_len = 0;
1517 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1519 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1521 HeapFree(GetProcessHeap(), 0, authorization);
1526 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1528 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1531 size = sizeof(new_location);
1532 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1534 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1535 strcpyW( url, new_location );
1539 static const WCHAR slash[] = { '/',0 };
1540 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1541 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1542 http_session_t *session = req->session;
1544 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1545 size += strlenW( session->hostName ) + strlenW( req->path );
1547 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1549 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1550 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1552 sprintfW( url, format, session->hostName, session->hostPort );
1553 if (req->path[0] != '/') strcatW( url, slash );
1554 strcatW( url, req->path );
1556 TRACE("url=%s\n", debugstr_w(url));
1560 /***********************************************************************
1561 * HTTP_DealWithProxy
1563 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1565 WCHAR buf[MAXHOSTNAME];
1566 WCHAR protoProxy[MAXHOSTNAME + 15];
1567 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1568 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1569 static WCHAR szNul[] = { 0 };
1570 URL_COMPONENTSW UrlComponents;
1571 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1572 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1573 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1575 memset( &UrlComponents, 0, sizeof UrlComponents );
1576 UrlComponents.dwStructSize = sizeof UrlComponents;
1577 UrlComponents.lpszHostName = buf;
1578 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1580 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1582 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1583 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1584 sprintfW(proxy, szFormat, protoProxy);
1586 strcpyW(proxy, protoProxy);
1587 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1589 if( UrlComponents.dwHostNameLength == 0 )
1592 if( !request->path )
1593 request->path = szNul;
1595 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1596 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1598 HeapFree(GetProcessHeap(), 0, session->serverName);
1599 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1600 session->serverPort = UrlComponents.nPort;
1602 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1606 #ifndef INET6_ADDRSTRLEN
1607 #define INET6_ADDRSTRLEN 46
1610 static DWORD HTTP_ResolveName(http_request_t *request)
1612 char szaddr[INET6_ADDRSTRLEN];
1613 http_session_t *session = request->session;
1616 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1617 INTERNET_STATUS_RESOLVING_NAME,
1618 session->serverName,
1619 (strlenW(session->serverName)+1) * sizeof(WCHAR));
1621 session->sa_len = sizeof(session->socketAddress);
1622 if (!GetAddress(session->serverName, session->serverPort,
1623 (struct sockaddr *)&session->socketAddress, &session->sa_len))
1624 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1626 switch (session->socketAddress.ss_family)
1629 addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
1632 addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
1635 WARN("unsupported family %d\n", session->socketAddress.ss_family);
1636 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1638 inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1639 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1640 INTERNET_STATUS_NAME_RESOLVED,
1641 szaddr, strlen(szaddr)+1);
1643 TRACE("resolved %s to %s\n", debugstr_w(session->serverName), szaddr);
1644 return ERROR_SUCCESS;
1647 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1649 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1650 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1651 static const WCHAR slash[] = { '/',0 };
1652 LPHTTPHEADERW host_header;
1655 host_header = HTTP_GetHeader(req, hostW);
1659 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1663 strcpyW(buf, scheme);
1664 strcatW(buf, host_header->lpszValue);
1665 if (req->path[0] != '/')
1666 strcatW(buf, slash);
1667 strcatW(buf, req->path);
1672 /***********************************************************************
1673 * HTTPREQ_Destroy (internal)
1675 * Deallocate request handle
1678 static void HTTPREQ_Destroy(object_header_t *hdr)
1680 http_request_t *request = (http_request_t*) hdr;
1685 if(request->hCacheFile) {
1686 WCHAR url[INTERNET_MAX_URL_LENGTH];
1688 CloseHandle(request->hCacheFile);
1690 if(HTTP_GetRequestURL(request, url)) {
1693 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1694 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1695 request->last_modified, NORMAL_CACHE_ENTRY,
1696 request->rawHeaders, headersLen, NULL, 0);
1700 HeapFree(GetProcessHeap(), 0, request->cacheFile);
1702 DeleteCriticalSection( &request->read_section );
1703 WININET_Release(&request->session->hdr);
1705 destroy_authinfo(request->authInfo);
1706 destroy_authinfo(request->proxyAuthInfo);
1708 HeapFree(GetProcessHeap(), 0, request->path);
1709 HeapFree(GetProcessHeap(), 0, request->verb);
1710 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
1711 HeapFree(GetProcessHeap(), 0, request->version);
1712 HeapFree(GetProcessHeap(), 0, request->statusText);
1714 for (i = 0; i < request->nCustHeaders; i++)
1716 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszField);
1717 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszValue);
1720 destroy_data_stream(request->data_stream);
1721 HeapFree(GetProcessHeap(), 0, request->custHeaders);
1724 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1726 http_request_t *request = (http_request_t*) hdr;
1728 TRACE("%p\n",request);
1730 if (!NETCON_connected(&request->netConnection))
1733 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1734 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1736 NETCON_close(&request->netConnection);
1738 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1739 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1742 static BOOL HTTP_KeepAlive(http_request_t *request)
1744 WCHAR szVersion[10];
1745 WCHAR szConnectionResponse[20];
1746 DWORD dwBufferSize = sizeof(szVersion);
1747 BOOL keepalive = FALSE;
1749 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1750 * the connection is keep-alive by default */
1751 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1752 && !strcmpiW(szVersion, g_szHttp1_1))
1757 dwBufferSize = sizeof(szConnectionResponse);
1758 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1759 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1761 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1767 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1769 http_request_t *req = (http_request_t*)hdr;
1772 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1774 http_session_t *session = req->session;
1775 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1777 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1779 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1780 return ERROR_INSUFFICIENT_BUFFER;
1781 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1782 /* FIXME: can't get a SOCKET from our connection since we don't use
1786 /* FIXME: get source port from req->netConnection */
1787 info->SourcePort = 0;
1788 info->DestPort = session->hostPort;
1790 if (HTTP_KeepAlive(req))
1791 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1792 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1793 info->Flags |= IDSI_FLAG_PROXY;
1794 if (req->netConnection.useSSL)
1795 info->Flags |= IDSI_FLAG_SECURE;
1797 return ERROR_SUCCESS;
1800 case INTERNET_OPTION_SECURITY_FLAGS:
1805 if (*size < sizeof(ULONG))
1806 return ERROR_INSUFFICIENT_BUFFER;
1808 *size = sizeof(DWORD);
1810 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1811 flags |= SECURITY_FLAG_SECURE;
1812 flags |= req->netConnection.security_flags;
1813 bits = NETCON_GetCipherStrength(&req->netConnection);
1815 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1816 else if (bits >= 56)
1817 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1819 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1820 *(DWORD *)buffer = flags;
1821 return ERROR_SUCCESS;
1824 case INTERNET_OPTION_HANDLE_TYPE:
1825 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1827 if (*size < sizeof(ULONG))
1828 return ERROR_INSUFFICIENT_BUFFER;
1830 *size = sizeof(DWORD);
1831 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1832 return ERROR_SUCCESS;
1834 case INTERNET_OPTION_URL: {
1835 WCHAR url[INTERNET_MAX_URL_LENGTH];
1840 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1842 TRACE("INTERNET_OPTION_URL\n");
1844 host = HTTP_GetHeader(req, hostW);
1845 strcpyW(url, httpW);
1846 strcatW(url, host->lpszValue);
1847 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1849 strcatW(url, req->path);
1851 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1854 len = (strlenW(url)+1) * sizeof(WCHAR);
1856 return ERROR_INSUFFICIENT_BUFFER;
1859 strcpyW(buffer, url);
1860 return ERROR_SUCCESS;
1862 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1864 return ERROR_INSUFFICIENT_BUFFER;
1867 return ERROR_SUCCESS;
1871 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1872 INTERNET_CACHE_ENTRY_INFOW *info;
1873 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1874 WCHAR url[INTERNET_MAX_URL_LENGTH];
1875 DWORD nbytes, error;
1878 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1880 if (*size < sizeof(*ts))
1882 *size = sizeof(*ts);
1883 return ERROR_INSUFFICIENT_BUFFER;
1886 HTTP_GetRequestURL(req, url);
1887 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1888 error = GetLastError();
1889 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1891 if (!(info = heap_alloc(nbytes)))
1892 return ERROR_OUTOFMEMORY;
1894 GetUrlCacheEntryInfoW(url, info, &nbytes);
1896 ts->ftExpires = info->ExpireTime;
1897 ts->ftLastModified = info->LastModifiedTime;
1899 HeapFree(GetProcessHeap(), 0, info);
1900 *size = sizeof(*ts);
1901 return ERROR_SUCCESS;
1906 case INTERNET_OPTION_DATAFILE_NAME: {
1909 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1911 if(!req->cacheFile) {
1913 return ERROR_INTERNET_ITEM_NOT_FOUND;
1917 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
1918 if(*size < req_size)
1919 return ERROR_INSUFFICIENT_BUFFER;
1922 memcpy(buffer, req->cacheFile, *size);
1923 return ERROR_SUCCESS;
1925 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
1926 if (req_size > *size)
1927 return ERROR_INSUFFICIENT_BUFFER;
1929 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
1930 -1, buffer, *size, NULL, NULL);
1931 return ERROR_SUCCESS;
1935 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1936 PCCERT_CONTEXT context;
1938 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
1939 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
1940 return ERROR_INSUFFICIENT_BUFFER;
1943 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1945 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
1948 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1949 info->ftExpiry = context->pCertInfo->NotAfter;
1950 info->ftStart = context->pCertInfo->NotBefore;
1951 len = CertNameToStrA(context->dwCertEncodingType,
1952 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1953 info->lpszSubjectInfo = LocalAlloc(0, len);
1954 if(info->lpszSubjectInfo)
1955 CertNameToStrA(context->dwCertEncodingType,
1956 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1957 info->lpszSubjectInfo, len);
1958 len = CertNameToStrA(context->dwCertEncodingType,
1959 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1960 info->lpszIssuerInfo = LocalAlloc(0, len);
1961 if(info->lpszIssuerInfo)
1962 CertNameToStrA(context->dwCertEncodingType,
1963 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1964 info->lpszIssuerInfo, len);
1965 info->dwKeySize = NETCON_GetCipherStrength(&req->netConnection);
1966 CertFreeCertificateContext(context);
1967 return ERROR_SUCCESS;
1972 return INET_QueryOption(hdr, option, buffer, size, unicode);
1975 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1977 http_request_t *req = (http_request_t*)hdr;
1980 case INTERNET_OPTION_SECURITY_FLAGS:
1984 if (!buffer || size != sizeof(DWORD))
1985 return ERROR_INVALID_PARAMETER;
1986 flags = *(DWORD *)buffer;
1987 TRACE("%08x\n", flags);
1988 req->netConnection.security_flags = flags;
1989 return ERROR_SUCCESS;
1991 case INTERNET_OPTION_SEND_TIMEOUT:
1992 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1993 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1995 if (size != sizeof(DWORD))
1996 return ERROR_INVALID_PARAMETER;
1998 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
2001 case INTERNET_OPTION_USERNAME:
2002 HeapFree(GetProcessHeap(), 0, req->session->userName);
2003 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2004 return ERROR_SUCCESS;
2006 case INTERNET_OPTION_PASSWORD:
2007 HeapFree(GetProcessHeap(), 0, req->session->password);
2008 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2009 return ERROR_SUCCESS;
2010 case INTERNET_OPTION_HTTP_DECODING:
2011 if(size != sizeof(BOOL))
2012 return ERROR_INVALID_PARAMETER;
2013 req->decoding = *(BOOL*)buffer;
2014 return ERROR_SUCCESS;
2017 return ERROR_INTERNET_INVALID_OPTION;
2020 /* read some more data into the read buffer (the read section must be held) */
2021 static DWORD read_more_data( http_request_t *req, int maxlen )
2028 /* move existing data to the start of the buffer */
2030 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2034 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2036 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
2037 maxlen - req->read_size, 0, &len );
2038 if(res == ERROR_SUCCESS)
2039 req->read_size += len;
2044 /* remove some amount of data from the read buffer (the read section must be held) */
2045 static void remove_data( http_request_t *req, int count )
2047 if (!(req->read_size -= count)) req->read_pos = 0;
2048 else req->read_pos += count;
2051 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2053 int count, bytes_read, pos = 0;
2056 EnterCriticalSection( &req->read_section );
2059 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2063 count = eol - (req->read_buf + req->read_pos);
2064 bytes_read = count + 1;
2066 else count = bytes_read = req->read_size;
2068 count = min( count, *len - pos );
2069 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2071 remove_data( req, bytes_read );
2074 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2077 TRACE( "returning empty string\n" );
2078 LeaveCriticalSection( &req->read_section );
2079 INTERNET_SetLastError(res);
2083 LeaveCriticalSection( &req->read_section );
2087 if (pos && buffer[pos - 1] == '\r') pos--;
2090 buffer[*len - 1] = 0;
2091 TRACE( "returning %s\n", debugstr_a(buffer));
2095 /* check if we have reached the end of the data to read (the read section must be held) */
2096 static BOOL end_of_read_data( http_request_t *req )
2098 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2101 /* fetch some more data into the read buffer (the read section must be held) */
2102 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode)
2106 if(req->read_size == sizeof(req->read_buf))
2107 return ERROR_SUCCESS;
2111 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2115 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2116 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2117 req->read_size += read;
2119 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2123 /* return the size of data available to be read immediately (the read section must be held) */
2124 static DWORD get_avail_data( http_request_t *req )
2126 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2129 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2133 NETCON_query_data_available(&req->netConnection, &avail);
2137 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2139 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2140 return netconn_stream->content_read == netconn_stream->content_length || !NETCON_connected(&req->netConnection);
2143 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2144 DWORD *read, read_mode_t read_mode)
2146 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2150 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2152 if(read_mode == READMODE_NOBLOCK)
2153 size = min(size, netconn_get_avail_data(stream, req));
2156 if(NETCON_recv(&req->netConnection, buf + ret_read, size,
2157 read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2161 netconn_stream->content_read += *read = ret_read;
2162 TRACE("read %u bytes\n", ret_read);
2163 return ERROR_SUCCESS;
2166 static void netconn_destroy(data_stream_t *stream)
2170 static const data_stream_vtbl_t netconn_stream_vtbl = {
2171 netconn_get_avail_data,
2172 netconn_end_of_data,
2177 /* read some more data into the read buffer (the read section must be held) */
2178 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2183 if (stream->buf_pos)
2185 /* move existing data to the start of the buffer */
2186 if(stream->buf_size)
2187 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2188 stream->buf_pos = 0;
2191 if (maxlen == -1) maxlen = sizeof(stream->buf);
2193 res = NETCON_recv( &req->netConnection, stream->buf + stream->buf_size,
2194 maxlen - stream->buf_size, 0, &len );
2195 if(res == ERROR_SUCCESS)
2196 stream->buf_size += len;
2201 /* remove some amount of data from the read buffer (the read section must be held) */
2202 static void remove_chunked_data(chunked_stream_t *stream, int count)
2204 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2205 else stream->buf_pos += count;
2208 /* discard data contents until we reach end of line (the read section must be held) */
2209 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2215 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2218 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2221 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2222 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2223 } while (stream->buf_size);
2224 return ERROR_SUCCESS;
2227 /* read the size of the next chunk (the read section must be held) */
2228 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2231 DWORD chunk_size = 0, res;
2233 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2238 while (stream->buf_size)
2240 char ch = stream->buf[stream->buf_pos];
2241 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2242 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2243 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2244 else if (ch == ';' || ch == '\r' || ch == '\n')
2246 TRACE( "reading %u byte chunk\n", chunk_size );
2247 stream->chunk_size = chunk_size;
2248 req->contentLength += chunk_size;
2249 return discard_chunked_eol(stream, req);
2251 remove_chunked_data(stream, 1);
2253 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2254 if (!stream->buf_size)
2256 stream->chunk_size = 0;
2257 return ERROR_SUCCESS;
2262 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2264 /* Allow reading only from read buffer */
2268 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2270 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2271 return !chunked_stream->chunk_size;
2274 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2275 DWORD *read, read_mode_t read_mode)
2277 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2278 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2280 if(chunked_stream->chunk_size == ~0u) {
2281 res = start_next_chunk(chunked_stream, req);
2282 if(res != ERROR_SUCCESS)
2286 while(size && chunked_stream->chunk_size) {
2287 if(chunked_stream->buf_size) {
2288 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2290 /* this could block */
2291 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2294 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2295 remove_chunked_data(chunked_stream, read_bytes);
2297 read_bytes = min(size, chunked_stream->chunk_size);
2299 if(read_mode == READMODE_NOBLOCK) {
2302 if(!NETCON_query_data_available(&req->netConnection, &avail) || !avail)
2304 if(read_bytes > avail)
2307 /* this could block */
2308 if(read_bytes == chunked_stream->chunk_size)
2312 res = NETCON_recv(&req->netConnection, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2313 if(res != ERROR_SUCCESS)
2317 chunked_stream->chunk_size -= read_bytes;
2319 ret_read += read_bytes;
2320 if(!chunked_stream->chunk_size) {
2321 assert(read_mode != READMODE_NOBLOCK);
2322 res = start_next_chunk(chunked_stream, req);
2323 if(res != ERROR_SUCCESS)
2327 if(read_mode == READMODE_ASYNC)
2328 read_mode = READMODE_NOBLOCK;
2331 TRACE("read %u bytes\n", ret_read);
2336 static void chunked_destroy(data_stream_t *stream)
2338 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2339 heap_free(chunked_stream);
2342 static const data_stream_vtbl_t chunked_stream_vtbl = {
2343 chunked_get_avail_data,
2344 chunked_end_of_data,
2349 /* set the request content length based on the headers */
2350 static DWORD set_content_length(http_request_t *request)
2352 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2356 size = sizeof(request->contentLength);
2357 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2358 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2359 request->contentLength = ~0u;
2360 request->netconn_stream.content_length = request->contentLength;
2361 request->netconn_stream.content_read = request->read_size;
2363 size = sizeof(encoding);
2364 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2365 !strcmpiW(encoding, szChunked))
2367 chunked_stream_t *chunked_stream;
2369 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2371 return ERROR_OUTOFMEMORY;
2373 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2374 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2375 chunked_stream->chunk_size = ~0u;
2377 if(request->read_size) {
2378 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2379 chunked_stream->buf_size = request->read_size;
2380 request->read_size = request->read_pos = 0;
2383 request->data_stream = &chunked_stream->data_stream;
2384 request->contentLength = ~0u;
2385 request->read_chunked = TRUE;
2388 if(request->decoding) {
2391 static const WCHAR gzipW[] = {'g','z','i','p',0};
2393 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2394 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2395 return init_gzip_stream(request);
2398 return ERROR_SUCCESS;
2401 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2403 INTERNET_ASYNC_RESULT iar;
2408 EnterCriticalSection( &req->read_section );
2409 if ((res = refill_read_buffer(req, READMODE_ASYNC)) == ERROR_SUCCESS) {
2410 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2411 iar.dwError = first_notif ? 0 : get_avail_data(req);
2412 if(!first_notif && !iar.dwError)
2413 ERR("No data reported!\n");
2418 LeaveCriticalSection( &req->read_section );
2420 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2421 sizeof(INTERNET_ASYNC_RESULT));
2424 /* read data from the http connection (the read section must be held) */
2425 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2427 DWORD current_read = 0, ret_read = 0;
2428 read_mode_t read_mode;
2429 DWORD res = ERROR_SUCCESS;
2431 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2433 EnterCriticalSection( &req->read_section );
2435 if(req->read_size) {
2436 ret_read = min(size, req->read_size);
2437 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2438 req->read_size -= ret_read;
2439 req->read_pos += ret_read;
2440 if(read_mode == READMODE_ASYNC)
2441 read_mode = READMODE_NOBLOCK;
2444 if(ret_read < size) {
2445 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2446 ret_read += current_read;
2449 LeaveCriticalSection( &req->read_section );
2452 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2454 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2458 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2460 WARN("WriteFile failed: %u\n", GetLastError());
2463 if(end_of_read_data(req))
2464 HTTP_FinishedReading(req);
2470 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2472 http_request_t *req = (http_request_t*)hdr;
2475 EnterCriticalSection( &req->read_section );
2476 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2477 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2479 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2480 if(res == ERROR_SUCCESS)
2482 LeaveCriticalSection( &req->read_section );
2487 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2489 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2490 http_request_t *req = (http_request_t*)workRequest->hdr;
2491 INTERNET_ASYNC_RESULT iar;
2494 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2496 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2497 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2499 iar.dwResult = res == ERROR_SUCCESS;
2502 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2503 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2504 sizeof(INTERNET_ASYNC_RESULT));
2507 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2508 DWORD flags, DWORD_PTR context)
2510 http_request_t *req = (http_request_t*)hdr;
2511 DWORD res, size, read, error = ERROR_SUCCESS;
2513 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2514 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2516 if (buffers->dwStructSize != sizeof(*buffers))
2517 return ERROR_INVALID_PARAMETER;
2519 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2521 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2523 WORKREQUEST workRequest;
2525 if (TryEnterCriticalSection( &req->read_section ))
2527 if (get_avail_data(req))
2529 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2530 &buffers->dwBufferLength, FALSE);
2531 size = buffers->dwBufferLength;
2532 LeaveCriticalSection( &req->read_section );
2535 LeaveCriticalSection( &req->read_section );
2538 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2539 workRequest.hdr = WININET_AddRef(&req->hdr);
2540 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2542 INTERNET_AsyncCall(&workRequest);
2544 return ERROR_IO_PENDING;
2548 size = buffers->dwBufferLength;
2550 EnterCriticalSection( &req->read_section );
2551 if(hdr->dwError == ERROR_SUCCESS)
2552 hdr->dwError = INTERNET_HANDLE_IN_USE;
2553 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2554 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2557 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2558 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2559 if(res != ERROR_SUCCESS)
2562 read += buffers->dwBufferLength;
2563 if(read == size || end_of_read_data(req))
2566 LeaveCriticalSection( &req->read_section );
2568 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2569 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2570 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2571 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2573 EnterCriticalSection( &req->read_section );
2576 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2577 hdr->dwError = ERROR_SUCCESS;
2579 error = hdr->dwError;
2581 LeaveCriticalSection( &req->read_section );
2582 size = buffers->dwBufferLength;
2583 buffers->dwBufferLength = read;
2586 if (res == ERROR_SUCCESS) {
2587 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2588 &size, sizeof(size));
2591 return res==ERROR_SUCCESS ? error : res;
2594 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2596 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2597 http_request_t *req = (http_request_t*)workRequest->hdr;
2598 INTERNET_ASYNC_RESULT iar;
2601 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2603 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2604 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2606 iar.dwResult = res == ERROR_SUCCESS;
2609 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2610 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2611 sizeof(INTERNET_ASYNC_RESULT));
2614 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2615 DWORD flags, DWORD_PTR context)
2618 http_request_t *req = (http_request_t*)hdr;
2619 DWORD res, size, read, error = ERROR_SUCCESS;
2621 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2622 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2624 if (buffers->dwStructSize != sizeof(*buffers))
2625 return ERROR_INVALID_PARAMETER;
2627 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2629 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2631 WORKREQUEST workRequest;
2633 if (TryEnterCriticalSection( &req->read_section ))
2635 if (get_avail_data(req))
2637 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2638 &buffers->dwBufferLength, FALSE);
2639 size = buffers->dwBufferLength;
2640 LeaveCriticalSection( &req->read_section );
2643 LeaveCriticalSection( &req->read_section );
2646 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2647 workRequest.hdr = WININET_AddRef(&req->hdr);
2648 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2650 INTERNET_AsyncCall(&workRequest);
2652 return ERROR_IO_PENDING;
2656 size = buffers->dwBufferLength;
2658 EnterCriticalSection( &req->read_section );
2659 if(hdr->dwError == ERROR_SUCCESS)
2660 hdr->dwError = INTERNET_HANDLE_IN_USE;
2661 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2662 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2665 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2666 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2667 if(res != ERROR_SUCCESS)
2670 read += buffers->dwBufferLength;
2671 if(read == size || end_of_read_data(req))
2674 LeaveCriticalSection( &req->read_section );
2676 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2677 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2678 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2679 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2681 EnterCriticalSection( &req->read_section );
2684 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2685 hdr->dwError = ERROR_SUCCESS;
2687 error = hdr->dwError;
2689 LeaveCriticalSection( &req->read_section );
2690 size = buffers->dwBufferLength;
2691 buffers->dwBufferLength = read;
2694 if (res == ERROR_SUCCESS) {
2695 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2696 &size, sizeof(size));
2699 return res==ERROR_SUCCESS ? error : res;
2702 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2705 http_request_t *request = (http_request_t*)hdr;
2707 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2710 res = NETCON_send(&request->netConnection, buffer, size, 0, (LPINT)written);
2711 if (res == ERROR_SUCCESS)
2712 request->bytesWritten += *written;
2714 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2718 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2720 http_request_t *req = (http_request_t*)workRequest->hdr;
2722 HTTP_ReceiveRequestData(req, FALSE);
2725 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2727 http_request_t *req = (http_request_t*)hdr;
2729 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2731 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2733 WORKREQUEST workRequest;
2735 /* never wait, if we can't enter the section we queue an async request right away */
2736 if (TryEnterCriticalSection( &req->read_section ))
2738 refill_read_buffer(req, READMODE_NOBLOCK);
2739 if ((*available = get_avail_data( req ))) goto done;
2740 if (end_of_read_data( req )) goto done;
2741 LeaveCriticalSection( &req->read_section );
2744 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2745 workRequest.hdr = WININET_AddRef( &req->hdr );
2747 INTERNET_AsyncCall(&workRequest);
2749 return ERROR_IO_PENDING;
2752 EnterCriticalSection( &req->read_section );
2754 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2756 refill_read_buffer( req, READMODE_ASYNC );
2757 *available = get_avail_data( req );
2761 LeaveCriticalSection( &req->read_section );
2763 TRACE( "returning %u\n", *available );
2764 return ERROR_SUCCESS;
2767 static const object_vtbl_t HTTPREQVtbl = {
2769 HTTPREQ_CloseConnection,
2770 HTTPREQ_QueryOption,
2773 HTTPREQ_ReadFileExA,
2774 HTTPREQ_ReadFileExW,
2776 HTTPREQ_QueryDataAvailable,
2780 /***********************************************************************
2781 * HTTP_HttpOpenRequestW (internal)
2783 * Open a HTTP request handle
2786 * HINTERNET a HTTP request handle on success
2790 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
2791 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2792 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2793 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2795 appinfo_t *hIC = NULL;
2796 http_request_t *request;
2797 LPWSTR lpszHostName = NULL;
2798 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2803 assert( session->hdr.htype == WH_HHTTPSESSION );
2804 hIC = session->appInfo;
2806 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
2808 return ERROR_OUTOFMEMORY;
2810 request->hdr.htype = WH_HHTTPREQ;
2811 request->hdr.dwFlags = dwFlags;
2812 request->hdr.dwContext = dwContext;
2813 request->contentLength = ~0u;
2815 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
2816 request->data_stream = &request->netconn_stream.data_stream;
2818 InitializeCriticalSection( &request->read_section );
2820 WININET_AddRef( &session->hdr );
2821 request->session = session;
2822 list_add_head( &session->hdr.children, &request->hdr.entry );
2824 lpszHostName = heap_alloc(sizeof(WCHAR) * (strlenW(session->hostName) + 7 /* length of ":65535" + 1 */));
2825 if (NULL == lpszHostName)
2827 res = ERROR_OUTOFMEMORY;
2831 if ((res = NETCON_init(&request->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2833 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
2834 request->netConnection.security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
2835 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
2836 request->netConnection.security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
2838 if (lpszObjectName && *lpszObjectName) {
2842 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2843 if (rc != E_POINTER)
2844 len = strlenW(lpszObjectName)+1;
2845 request->path = heap_alloc(len*sizeof(WCHAR));
2846 rc = UrlEscapeW(lpszObjectName, request->path, &len,
2847 URL_ESCAPE_SPACES_ONLY);
2850 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2851 strcpyW(request->path,lpszObjectName);
2854 static const WCHAR slashW[] = {'/',0};
2856 request->path = heap_strdupW(slashW);
2859 if (lpszReferrer && *lpszReferrer)
2860 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2862 if (lpszAcceptTypes)
2865 for (i = 0; lpszAcceptTypes[i]; i++)
2867 if (!*lpszAcceptTypes[i]) continue;
2868 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
2869 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2870 HTTP_ADDHDR_FLAG_REQ |
2871 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2875 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2876 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2878 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
2879 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
2880 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
2882 sprintfW(lpszHostName, szHostForm, session->hostName, session->hostPort);
2883 HTTP_ProcessHeader(request, hostW, lpszHostName,
2884 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2887 HTTP_ProcessHeader(request, hostW, session->hostName,
2888 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2890 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
2891 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
2892 INTERNET_DEFAULT_HTTPS_PORT :
2893 INTERNET_DEFAULT_HTTP_PORT);
2895 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
2896 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2897 INTERNET_DEFAULT_HTTPS_PORT :
2898 INTERNET_DEFAULT_HTTP_PORT);
2900 if (NULL != hIC->proxy && hIC->proxy[0] != 0)
2901 HTTP_DealWithProxy( hIC, session, request );
2903 INTERNET_SendCallback(&session->hdr, dwContext,
2904 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
2908 TRACE("<-- %u (%p)\n", res, request);
2910 HeapFree(GetProcessHeap(), 0, lpszHostName);
2911 if(res != ERROR_SUCCESS) {
2912 WININET_Release( &request->hdr );
2917 *ret = request->hdr.hInternet;
2918 return ERROR_SUCCESS;
2921 /***********************************************************************
2922 * HttpOpenRequestW (WININET.@)
2924 * Open a HTTP request handle
2927 * HINTERNET a HTTP request handle on success
2931 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2932 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2933 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2934 DWORD dwFlags, DWORD_PTR dwContext)
2936 http_session_t *session;
2937 HINTERNET handle = NULL;
2940 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2941 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2942 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2943 dwFlags, dwContext);
2944 if(lpszAcceptTypes!=NULL)
2947 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2948 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2951 session = (http_session_t*) get_handle_object( hHttpSession );
2952 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
2954 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2959 * My tests seem to show that the windows version does not
2960 * become asynchronous until after this point. And anyhow
2961 * if this call was asynchronous then how would you get the
2962 * necessary HINTERNET pointer returned by this function.
2965 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
2966 lpszVersion, lpszReferrer, lpszAcceptTypes,
2967 dwFlags, dwContext, &handle);
2970 WININET_Release( &session->hdr );
2971 TRACE("returning %p\n", handle);
2972 if(res != ERROR_SUCCESS)
2977 /* read any content returned by the server so that the connection can be
2979 static void HTTP_DrainContent(http_request_t *req)
2983 if (!NETCON_connected(&req->netConnection)) return;
2985 if (req->contentLength == -1)
2987 NETCON_close(&req->netConnection);
2990 if (!strcmpW(req->verb, szHEAD)) return;
2995 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2997 } while (bytes_read);
3000 static const LPCWSTR header_lookup[] = {
3001 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3002 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3003 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3004 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3005 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3006 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3007 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3008 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3009 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3010 szDate, /* HTTP_QUERY_DATE = 9 */
3011 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3012 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3013 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3014 szURI, /* HTTP_QUERY_URI = 13 */
3015 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3016 NULL, /* HTTP_QUERY_COST = 15 */
3017 NULL, /* HTTP_QUERY_LINK = 16 */
3018 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3019 NULL, /* HTTP_QUERY_VERSION = 18 */
3020 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3021 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3022 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3023 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3024 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3025 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3026 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3027 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3028 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3029 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3030 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3031 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3032 NULL, /* HTTP_QUERY_FROM = 31 */
3033 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3034 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3035 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3036 szReferer, /* HTTP_QUERY_REFERER = 35 */
3037 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3038 szServer, /* HTTP_QUERY_SERVER = 37 */
3039 NULL, /* HTTP_TITLE = 38 */
3040 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3041 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3042 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3043 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3044 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3045 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3046 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3047 NULL, /* HTTP_QUERY_REFRESH = 46 */
3048 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3049 szAge, /* HTTP_QUERY_AGE = 48 */
3050 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3051 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3052 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3053 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3054 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3055 szETag, /* HTTP_QUERY_ETAG = 54 */
3056 hostW, /* HTTP_QUERY_HOST = 55 */
3057 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3058 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3059 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3060 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3061 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3062 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3063 szRange, /* HTTP_QUERY_RANGE = 62 */
3064 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3065 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3066 szVary, /* HTTP_QUERY_VARY = 65 */
3067 szVia, /* HTTP_QUERY_VIA = 66 */
3068 szWarning, /* HTTP_QUERY_WARNING = 67 */
3069 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3070 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3071 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3074 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3076 /***********************************************************************
3077 * HTTP_HttpQueryInfoW (internal)
3079 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3080 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3082 LPHTTPHEADERW lphttpHdr = NULL;
3083 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3084 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3085 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3088 /* Find requested header structure */
3091 case HTTP_QUERY_CUSTOM:
3092 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3093 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3095 case HTTP_QUERY_RAW_HEADERS_CRLF:
3099 DWORD res = ERROR_INVALID_PARAMETER;
3102 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3104 headers = request->rawHeaders;
3107 len = strlenW(headers) * sizeof(WCHAR);
3109 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3111 len += sizeof(WCHAR);
3112 res = ERROR_INSUFFICIENT_BUFFER;
3117 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3120 len = strlenW(szCrLf) * sizeof(WCHAR);
3121 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3123 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3124 res = ERROR_SUCCESS;
3126 *lpdwBufferLength = len;
3129 HeapFree(GetProcessHeap(), 0, headers);
3132 case HTTP_QUERY_RAW_HEADERS:
3134 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3136 LPWSTR pszString = lpBuffer;
3138 for (i = 0; ppszRawHeaderLines[i]; i++)
3139 size += strlenW(ppszRawHeaderLines[i]) + 1;
3141 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3143 HTTP_FreeTokens(ppszRawHeaderLines);
3144 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3145 return ERROR_INSUFFICIENT_BUFFER;
3149 for (i = 0; ppszRawHeaderLines[i]; i++)
3151 DWORD len = strlenW(ppszRawHeaderLines[i]);
3152 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3156 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3158 *lpdwBufferLength = size * sizeof(WCHAR);
3159 HTTP_FreeTokens(ppszRawHeaderLines);
3161 return ERROR_SUCCESS;
3163 case HTTP_QUERY_STATUS_TEXT:
3164 if (request->statusText)
3166 DWORD len = strlenW(request->statusText);
3167 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3169 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3170 return ERROR_INSUFFICIENT_BUFFER;
3174 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3175 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3177 *lpdwBufferLength = len * sizeof(WCHAR);
3178 return ERROR_SUCCESS;
3181 case HTTP_QUERY_VERSION:
3182 if (request->version)
3184 DWORD len = strlenW(request->version);
3185 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3187 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3188 return ERROR_INSUFFICIENT_BUFFER;
3192 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3193 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3195 *lpdwBufferLength = len * sizeof(WCHAR);
3196 return ERROR_SUCCESS;
3199 case HTTP_QUERY_CONTENT_ENCODING:
3200 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3201 requested_index,request_only);
3204 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3206 if (level < LAST_TABLE_HEADER && header_lookup[level])
3207 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3208 requested_index,request_only);
3212 lphttpHdr = &request->custHeaders[index];
3214 /* Ensure header satisfies requested attributes */
3216 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3217 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3219 return ERROR_HTTP_HEADER_NOT_FOUND;
3222 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3224 /* coalesce value to requested type */
3225 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3227 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3228 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3230 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3236 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3238 tmpTM = *gmtime(&tmpTime);
3239 STHook = (SYSTEMTIME *)lpBuffer;
3240 STHook->wDay = tmpTM.tm_mday;
3241 STHook->wHour = tmpTM.tm_hour;
3242 STHook->wMilliseconds = 0;
3243 STHook->wMinute = tmpTM.tm_min;
3244 STHook->wDayOfWeek = tmpTM.tm_wday;
3245 STHook->wMonth = tmpTM.tm_mon + 1;
3246 STHook->wSecond = tmpTM.tm_sec;
3247 STHook->wYear = tmpTM.tm_year;
3249 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3250 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3251 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3253 else if (lphttpHdr->lpszValue)
3255 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3257 if (len > *lpdwBufferLength)
3259 *lpdwBufferLength = len;
3260 return ERROR_INSUFFICIENT_BUFFER;
3264 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3265 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3267 *lpdwBufferLength = len - sizeof(WCHAR);
3269 return ERROR_SUCCESS;
3272 /***********************************************************************
3273 * HttpQueryInfoW (WININET.@)
3275 * Queries for information about an HTTP request
3282 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3283 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3285 http_request_t *request;
3288 if (TRACE_ON(wininet)) {
3289 #define FE(x) { x, #x }
3290 static const wininet_flag_info query_flags[] = {
3291 FE(HTTP_QUERY_MIME_VERSION),
3292 FE(HTTP_QUERY_CONTENT_TYPE),
3293 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3294 FE(HTTP_QUERY_CONTENT_ID),
3295 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3296 FE(HTTP_QUERY_CONTENT_LENGTH),
3297 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3298 FE(HTTP_QUERY_ALLOW),
3299 FE(HTTP_QUERY_PUBLIC),
3300 FE(HTTP_QUERY_DATE),
3301 FE(HTTP_QUERY_EXPIRES),
3302 FE(HTTP_QUERY_LAST_MODIFIED),
3303 FE(HTTP_QUERY_MESSAGE_ID),
3305 FE(HTTP_QUERY_DERIVED_FROM),
3306 FE(HTTP_QUERY_COST),
3307 FE(HTTP_QUERY_LINK),
3308 FE(HTTP_QUERY_PRAGMA),
3309 FE(HTTP_QUERY_VERSION),
3310 FE(HTTP_QUERY_STATUS_CODE),
3311 FE(HTTP_QUERY_STATUS_TEXT),
3312 FE(HTTP_QUERY_RAW_HEADERS),
3313 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3314 FE(HTTP_QUERY_CONNECTION),
3315 FE(HTTP_QUERY_ACCEPT),
3316 FE(HTTP_QUERY_ACCEPT_CHARSET),
3317 FE(HTTP_QUERY_ACCEPT_ENCODING),
3318 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3319 FE(HTTP_QUERY_AUTHORIZATION),
3320 FE(HTTP_QUERY_CONTENT_ENCODING),
3321 FE(HTTP_QUERY_FORWARDED),
3322 FE(HTTP_QUERY_FROM),
3323 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3324 FE(HTTP_QUERY_LOCATION),
3325 FE(HTTP_QUERY_ORIG_URI),
3326 FE(HTTP_QUERY_REFERER),
3327 FE(HTTP_QUERY_RETRY_AFTER),
3328 FE(HTTP_QUERY_SERVER),
3329 FE(HTTP_QUERY_TITLE),
3330 FE(HTTP_QUERY_USER_AGENT),
3331 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3332 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3333 FE(HTTP_QUERY_ACCEPT_RANGES),
3334 FE(HTTP_QUERY_SET_COOKIE),
3335 FE(HTTP_QUERY_COOKIE),
3336 FE(HTTP_QUERY_REQUEST_METHOD),
3337 FE(HTTP_QUERY_REFRESH),
3338 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3340 FE(HTTP_QUERY_CACHE_CONTROL),
3341 FE(HTTP_QUERY_CONTENT_BASE),
3342 FE(HTTP_QUERY_CONTENT_LOCATION),
3343 FE(HTTP_QUERY_CONTENT_MD5),
3344 FE(HTTP_QUERY_CONTENT_RANGE),
3345 FE(HTTP_QUERY_ETAG),
3346 FE(HTTP_QUERY_HOST),
3347 FE(HTTP_QUERY_IF_MATCH),
3348 FE(HTTP_QUERY_IF_NONE_MATCH),
3349 FE(HTTP_QUERY_IF_RANGE),
3350 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3351 FE(HTTP_QUERY_MAX_FORWARDS),
3352 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3353 FE(HTTP_QUERY_RANGE),
3354 FE(HTTP_QUERY_TRANSFER_ENCODING),
3355 FE(HTTP_QUERY_UPGRADE),
3356 FE(HTTP_QUERY_VARY),
3358 FE(HTTP_QUERY_WARNING),
3359 FE(HTTP_QUERY_CUSTOM)
3361 static const wininet_flag_info modifier_flags[] = {
3362 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3363 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3364 FE(HTTP_QUERY_FLAG_NUMBER),
3365 FE(HTTP_QUERY_FLAG_COALESCE)
3368 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3369 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3372 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3373 TRACE(" Attribute:");
3374 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3375 if (query_flags[i].val == info) {
3376 TRACE(" %s", query_flags[i].name);
3380 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3381 TRACE(" Unknown (%08x)", info);
3384 TRACE(" Modifier:");
3385 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3386 if (modifier_flags[i].val & info_mod) {
3387 TRACE(" %s", modifier_flags[i].name);
3388 info_mod &= ~ modifier_flags[i].val;
3393 TRACE(" Unknown (%08x)", info_mod);
3398 request = (http_request_t*) get_handle_object( hHttpRequest );
3399 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3401 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3405 if (lpBuffer == NULL)
3406 *lpdwBufferLength = 0;
3407 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3408 lpBuffer, lpdwBufferLength, lpdwIndex);
3412 WININET_Release( &request->hdr );
3414 TRACE("%u <--\n", res);
3415 if(res != ERROR_SUCCESS)
3417 return res == ERROR_SUCCESS;
3420 /***********************************************************************
3421 * HttpQueryInfoA (WININET.@)
3423 * Queries for information about an HTTP request
3430 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3431 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3437 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3438 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3440 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3441 lpdwBufferLength, lpdwIndex );
3447 len = (*lpdwBufferLength)*sizeof(WCHAR);
3448 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3450 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3456 bufferW = heap_alloc(alloclen);
3457 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3458 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3459 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3466 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3470 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3471 lpBuffer, *lpdwBufferLength, NULL, NULL );
3472 *lpdwBufferLength = len - 1;
3474 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3477 /* since the strings being returned from HttpQueryInfoW should be
3478 * only ASCII characters, it is reasonable to assume that all of
3479 * the Unicode characters can be reduced to a single byte */
3480 *lpdwBufferLength = len / sizeof(WCHAR);
3482 HeapFree(GetProcessHeap(), 0, bufferW );
3487 /***********************************************************************
3488 * HTTP_GetRedirectURL (internal)
3490 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3492 static WCHAR szHttp[] = {'h','t','t','p',0};
3493 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3494 http_session_t *session = request->session;
3495 URL_COMPONENTSW urlComponents;
3496 DWORD url_length = 0;
3498 LPWSTR combined_url;
3500 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3501 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3502 urlComponents.dwSchemeLength = 0;
3503 urlComponents.lpszHostName = session->hostName;
3504 urlComponents.dwHostNameLength = 0;
3505 urlComponents.nPort = session->hostPort;
3506 urlComponents.lpszUserName = session->userName;
3507 urlComponents.dwUserNameLength = 0;
3508 urlComponents.lpszPassword = NULL;
3509 urlComponents.dwPasswordLength = 0;
3510 urlComponents.lpszUrlPath = request->path;
3511 urlComponents.dwUrlPathLength = 0;
3512 urlComponents.lpszExtraInfo = NULL;
3513 urlComponents.dwExtraInfoLength = 0;
3515 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3516 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3519 orig_url = heap_alloc(url_length);
3521 /* convert from bytes to characters */
3522 url_length = url_length / sizeof(WCHAR) - 1;
3523 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3525 HeapFree(GetProcessHeap(), 0, orig_url);
3530 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3531 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3533 HeapFree(GetProcessHeap(), 0, orig_url);
3536 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3538 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3540 HeapFree(GetProcessHeap(), 0, orig_url);
3541 HeapFree(GetProcessHeap(), 0, combined_url);
3544 HeapFree(GetProcessHeap(), 0, orig_url);
3545 return combined_url;
3549 /***********************************************************************
3550 * HTTP_HandleRedirect (internal)
3552 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3554 http_session_t *session = request->session;
3555 appinfo_t *hIC = session->appInfo;
3556 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3557 WCHAR path[INTERNET_MAX_URL_LENGTH];
3562 /* if it's an absolute path, keep the same session info */
3563 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3567 URL_COMPONENTSW urlComponents;
3568 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3569 static WCHAR szHttp[] = {'h','t','t','p',0};
3570 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3576 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3577 urlComponents.lpszScheme = protocol;
3578 urlComponents.dwSchemeLength = 32;
3579 urlComponents.lpszHostName = hostName;
3580 urlComponents.dwHostNameLength = MAXHOSTNAME;
3581 urlComponents.lpszUserName = userName;
3582 urlComponents.dwUserNameLength = 1024;
3583 urlComponents.lpszPassword = NULL;
3584 urlComponents.dwPasswordLength = 0;
3585 urlComponents.lpszUrlPath = path;
3586 urlComponents.dwUrlPathLength = 2048;
3587 urlComponents.lpszExtraInfo = NULL;
3588 urlComponents.dwExtraInfoLength = 0;
3589 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3590 return INTERNET_GetLastError();
3592 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3593 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3595 TRACE("redirect from secure page to non-secure page\n");
3596 /* FIXME: warn about from secure redirect to non-secure page */
3597 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3599 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3600 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3602 TRACE("redirect from non-secure page to secure page\n");
3603 /* FIXME: notify about redirect to secure page */
3604 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3607 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3609 if (lstrlenW(protocol)>4) /*https*/
3610 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3612 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3617 * This upsets redirects to binary files on sourceforge.net
3618 * and gives an html page instead of the target file
3619 * Examination of the HTTP request sent by native wininet.dll
3620 * reveals that it doesn't send a referrer in that case.
3621 * Maybe there's a flag that enables this, or maybe a referrer
3622 * shouldn't be added in case of a redirect.
3625 /* consider the current host as the referrer */
3626 if (session->lpszServerName && *session->lpszServerName)
3627 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3628 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3629 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3632 HeapFree(GetProcessHeap(), 0, session->hostName);
3633 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3634 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3637 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3638 len = lstrlenW(hostName);
3639 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3640 session->hostName = heap_alloc(len*sizeof(WCHAR));
3641 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3644 session->hostName = heap_strdupW(hostName);
3646 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3648 HeapFree(GetProcessHeap(), 0, session->userName);
3649 session->userName = NULL;
3651 session->userName = heap_strdupW(userName);
3655 if (strcmpiW(session->serverName, hostName) || session->serverPort != urlComponents.nPort)
3659 HeapFree(GetProcessHeap(), 0, session->serverName);
3660 session->serverName = heap_strdupW(hostName);
3661 session->serverPort = urlComponents.nPort;
3663 NETCON_close(&request->netConnection);
3664 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS)
3667 res = NETCON_init(&request->netConnection, request->hdr.dwFlags & INTERNET_FLAG_SECURE);
3668 if (res != ERROR_SUCCESS)
3671 reset_data_stream(request);
3675 TRACE("Redirect through proxy\n");
3678 HeapFree(GetProcessHeap(), 0, request->path);
3685 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3686 if (rc != E_POINTER)
3687 needed = strlenW(path)+1;
3688 request->path = heap_alloc(needed*sizeof(WCHAR));
3689 rc = UrlEscapeW(path, request->path, &needed,
3690 URL_ESCAPE_SPACES_ONLY);
3693 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3694 strcpyW(request->path,path);
3698 /* Remove custom content-type/length headers on redirects. */
3699 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3701 HTTP_DeleteCustomHeader(request, index);
3702 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3704 HTTP_DeleteCustomHeader(request, index);
3706 return ERROR_SUCCESS;
3709 /***********************************************************************
3710 * HTTP_build_req (internal)
3712 * concatenate all the strings in the request together
3714 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3719 for( t = list; *t ; t++ )
3720 len += strlenW( *t );
3723 str = heap_alloc(len*sizeof(WCHAR));
3726 for( t = list; *t ; t++ )
3732 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3735 LPWSTR requestString;
3741 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3742 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3743 http_session_t *session = request->session;
3747 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3748 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3749 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3750 HeapFree( GetProcessHeap(), 0, lpszPath );
3752 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3753 NULL, 0, NULL, NULL );
3754 len--; /* the nul terminator isn't needed */
3755 ascii_req = heap_alloc(len);
3756 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3757 ascii_req, len, NULL, NULL );
3758 HeapFree( GetProcessHeap(), 0, requestString );
3760 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3762 res = NETCON_send( &request->netConnection, ascii_req, len, 0, &cnt );
3763 HeapFree( GetProcessHeap(), 0, ascii_req );
3764 if (res != ERROR_SUCCESS)
3767 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3769 return ERROR_HTTP_INVALID_HEADER;
3771 return ERROR_SUCCESS;
3774 static void HTTP_InsertCookies(http_request_t *request)
3776 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3777 LPWSTR lpszCookies, lpszUrl = NULL;
3778 DWORD nCookieSize, size;
3779 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
3781 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(request->path)) * sizeof(WCHAR);
3782 if (!(lpszUrl = heap_alloc(size))) return;
3783 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, request->path);
3785 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3788 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3790 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3791 if ((lpszCookies = heap_alloc(size)))
3793 cnt += sprintfW(lpszCookies, szCookie);
3794 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3795 strcatW(lpszCookies, szCrLf);
3797 HTTP_HttpAddRequestHeadersW(request, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3798 HeapFree(GetProcessHeap(), 0, lpszCookies);
3801 HeapFree(GetProcessHeap(), 0, lpszUrl);
3804 static WORD HTTP_ParseDay(LPCWSTR day)
3806 static const WCHAR days[7][4] = {{ 's','u','n',0 },
3814 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
3815 if (!strcmpiW(day, days[i]))
3822 static WORD HTTP_ParseMonth(LPCWSTR month)
3824 static const WCHAR jan[] = { 'j','a','n',0 };
3825 static const WCHAR feb[] = { 'f','e','b',0 };
3826 static const WCHAR mar[] = { 'm','a','r',0 };
3827 static const WCHAR apr[] = { 'a','p','r',0 };
3828 static const WCHAR may[] = { 'm','a','y',0 };
3829 static const WCHAR jun[] = { 'j','u','n',0 };
3830 static const WCHAR jul[] = { 'j','u','l',0 };
3831 static const WCHAR aug[] = { 'a','u','g',0 };
3832 static const WCHAR sep[] = { 's','e','p',0 };
3833 static const WCHAR oct[] = { 'o','c','t',0 };
3834 static const WCHAR nov[] = { 'n','o','v',0 };
3835 static const WCHAR dec[] = { 'd','e','c',0 };
3837 if (!strcmpiW(month, jan)) return 1;
3838 if (!strcmpiW(month, feb)) return 2;
3839 if (!strcmpiW(month, mar)) return 3;
3840 if (!strcmpiW(month, apr)) return 4;
3841 if (!strcmpiW(month, may)) return 5;
3842 if (!strcmpiW(month, jun)) return 6;
3843 if (!strcmpiW(month, jul)) return 7;
3844 if (!strcmpiW(month, aug)) return 8;
3845 if (!strcmpiW(month, sep)) return 9;
3846 if (!strcmpiW(month, oct)) return 10;
3847 if (!strcmpiW(month, nov)) return 11;
3848 if (!strcmpiW(month, dec)) return 12;
3853 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
3854 * optionally preceded by whitespace.
3855 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
3856 * st, and sets *str to the first character after the time format.
3858 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
3864 while (isspaceW(*ptr))
3867 num = strtoulW(ptr, &nextPtr, 10);
3868 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3870 ERR("unexpected time format %s\n", debugstr_w(ptr));
3875 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
3879 st->wHour = (WORD)num;
3880 num = strtoulW(ptr, &nextPtr, 10);
3881 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3883 ERR("unexpected time format %s\n", debugstr_w(ptr));
3888 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
3892 st->wMinute = (WORD)num;
3893 num = strtoulW(ptr, &nextPtr, 10);
3894 if (!nextPtr || nextPtr <= ptr)
3896 ERR("unexpected time format %s\n", debugstr_w(ptr));
3901 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
3906 st->wSecond = (WORD)num;
3910 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
3912 static const WCHAR gmt[]= { 'G','M','T',0 };
3913 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
3915 SYSTEMTIME st = { 0 };
3918 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
3919 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
3922 st.wDayOfWeek = HTTP_ParseDay(day);
3923 if (st.wDayOfWeek >= 7)
3925 ERR("unexpected weekday %s\n", debugstr_w(day));
3929 while (isspaceW(*ptr))
3932 for (monthPtr = month; !isspace(*ptr) &&
3933 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
3937 st.wMonth = HTTP_ParseMonth(month);
3938 if (!st.wMonth || st.wMonth > 12)
3940 ERR("unexpected month %s\n", debugstr_w(month));
3944 while (isspaceW(*ptr))
3947 num = strtoulW(ptr, &nextPtr, 10);
3948 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
3950 ERR("unexpected day %s\n", debugstr_w(ptr));
3954 st.wDay = (WORD)num;
3956 while (isspaceW(*ptr))
3959 if (!HTTP_ParseTime(&st, &ptr))
3962 while (isspaceW(*ptr))
3965 num = strtoulW(ptr, &nextPtr, 10);
3966 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
3968 ERR("unexpected year %s\n", debugstr_w(ptr));
3972 st.wYear = (WORD)num;
3974 while (isspaceW(*ptr))
3977 /* asctime() doesn't report a timezone, but some web servers do, so accept
3978 * with or without GMT.
3980 if (*ptr && strcmpW(ptr, gmt))
3982 ERR("unexpected timezone %s\n", debugstr_w(ptr));
3985 return SystemTimeToFileTime(&st, ft);
3988 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
3990 static const WCHAR gmt[]= { 'G','M','T',0 };
3991 WCHAR *nextPtr, day[4], month[4], *monthPtr;
3994 SYSTEMTIME st = { 0 };
3996 ptr = strchrW(value, ',');
3999 if (ptr - value != 3)
4001 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4004 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4006 st.wDayOfWeek = HTTP_ParseDay(day);
4007 if (st.wDayOfWeek > 6)
4009 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4014 while (isspaceW(*ptr))
4017 num = strtoulW(ptr, &nextPtr, 10);
4018 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4020 ERR("unexpected day %s\n", debugstr_w(value));
4024 st.wDay = (WORD)num;
4026 while (isspaceW(*ptr))
4029 for (monthPtr = month; !isspace(*ptr) &&
4030 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4034 st.wMonth = HTTP_ParseMonth(month);
4035 if (!st.wMonth || st.wMonth > 12)
4037 ERR("unexpected month %s\n", debugstr_w(month));
4041 while (isspaceW(*ptr))
4044 num = strtoulW(ptr, &nextPtr, 10);
4045 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4047 ERR("unexpected year %s\n", debugstr_w(value));
4051 st.wYear = (WORD)num;
4053 if (!HTTP_ParseTime(&st, &ptr))
4056 while (isspaceW(*ptr))
4059 if (strcmpW(ptr, gmt))
4061 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4064 return SystemTimeToFileTime(&st, ft);
4067 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4068 * which may not be the only formats actually seen in the wild.
4069 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4070 * should be accepted as well.
4072 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4076 if (strchrW(value, ','))
4077 ret = HTTP_ParseRfc1123Date(value, ft);
4080 ret = HTTP_ParseDateAsAsctime(value, ft);
4082 ERR("unexpected date format %s\n", debugstr_w(value));
4087 static void HTTP_ProcessExpires(http_request_t *request)
4089 BOOL expirationFound = FALSE;
4092 /* Look for a Cache-Control header with a max-age directive, as it takes
4093 * precedence over the Expires header.
4095 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4096 if (headerIndex != -1)
4098 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4101 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4103 LPWSTR comma = strchrW(ptr, ','), end, equal;
4108 end = ptr + strlenW(ptr);
4109 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4113 static const WCHAR max_age[] = {
4114 'm','a','x','-','a','g','e',0 };
4116 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4121 age = strtoulW(equal + 1, &nextPtr, 10);
4122 if (nextPtr > equal + 1)
4126 NtQuerySystemTime( &ft );
4127 /* Age is in seconds, FILETIME resolution is in
4128 * 100 nanosecond intervals.
4130 ft.QuadPart += age * (ULONGLONG)1000000;
4131 request->expires.dwLowDateTime = ft.u.LowPart;
4132 request->expires.dwHighDateTime = ft.u.HighPart;
4133 expirationFound = TRUE;
4140 while (isspaceW(*ptr))
4147 if (!expirationFound)
4149 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4150 if (headerIndex != -1)
4152 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4155 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4157 expirationFound = TRUE;
4158 request->expires = ft;
4162 if (!expirationFound)
4166 /* With no known age, default to 10 minutes until expiration. */
4167 NtQuerySystemTime( &t );
4168 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4169 request->expires.dwLowDateTime = t.u.LowPart;
4170 request->expires.dwHighDateTime = t.u.HighPart;
4174 static void HTTP_ProcessLastModified(http_request_t *request)
4178 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4179 if (headerIndex != -1)
4181 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4184 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4185 request->last_modified = ft;
4189 static void HTTP_CacheRequest(http_request_t *request)
4191 WCHAR url[INTERNET_MAX_URL_LENGTH];
4192 WCHAR cacheFileName[MAX_PATH+1];
4195 b = HTTP_GetRequestURL(request, url);
4197 WARN("Could not get URL\n");
4201 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4203 HeapFree(GetProcessHeap(), 0, request->cacheFile);
4204 CloseHandle(request->hCacheFile);
4206 request->cacheFile = heap_strdupW(cacheFileName);
4207 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4208 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4209 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4210 WARN("Could not create file: %u\n", GetLastError());
4211 request->hCacheFile = NULL;
4214 WARN("Could not create cache entry: %08x\n", GetLastError());
4218 /***********************************************************************
4219 * HTTP_HttpSendRequestW (internal)
4221 * Sends the specified request to the HTTP server
4224 * ERROR_SUCCESS on success
4225 * win32 error code on failure
4228 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4229 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4230 DWORD dwContentLength, BOOL bEndRequest)
4233 BOOL redirected = FALSE;
4234 LPWSTR requestString = NULL;
4237 INTERNET_ASYNC_RESULT iar;
4238 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4239 static const WCHAR szContentLength[] =
4240 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4241 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4244 TRACE("--> %p\n", request);
4246 assert(request->hdr.htype == WH_HHTTPREQ);
4248 /* if the verb is NULL default to GET */
4250 request->verb = heap_strdupW(szGET);
4252 if (dwContentLength || strcmpW(request->verb, szGET))
4254 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4255 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4256 request->bytesToWrite = dwContentLength;
4258 if (request->session->appInfo->agent)
4260 WCHAR *agent_header;
4261 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4264 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4265 agent_header = heap_alloc(len * sizeof(WCHAR));
4266 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4268 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4269 HeapFree(GetProcessHeap(), 0, agent_header);
4271 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4273 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4274 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4276 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4278 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4279 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4280 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4286 BOOL reusing_connection;
4291 /* like native, just in case the caller forgot to call InternetReadFile
4292 * for all the data */
4293 HTTP_DrainContent(request);
4295 request->contentLength = ~0u;
4296 request->bytesToWrite = 0;
4299 if (TRACE_ON(wininet))
4301 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4302 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4305 HTTP_FixURL(request);
4306 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4308 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4310 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4311 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4313 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4314 HTTP_InsertCookies(request);
4316 /* add the headers the caller supplied */
4317 if( lpszHeaders && dwHeaderLength )
4319 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4320 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4323 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4325 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4326 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4327 HeapFree(GetProcessHeap(), 0, url);
4330 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4333 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4335 /* Send the request and store the results */
4336 if(NETCON_connected(&request->netConnection))
4337 reusing_connection = TRUE;
4339 reusing_connection = FALSE;
4341 if ((res = HTTP_OpenConnection(request)) != ERROR_SUCCESS)
4344 /* send the request as ASCII, tack on the optional data */
4345 if (!lpOptional || redirected)
4346 dwOptionalLength = 0;
4347 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4348 NULL, 0, NULL, NULL );
4349 ascii_req = heap_alloc(len + dwOptionalLength);
4350 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4351 ascii_req, len, NULL, NULL );
4353 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4354 len = (len + dwOptionalLength - 1);
4356 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4358 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4359 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4361 res = NETCON_send(&request->netConnection, ascii_req, len, 0, &cnt);
4362 HeapFree( GetProcessHeap(), 0, ascii_req );
4364 request->bytesWritten = dwOptionalLength;
4366 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4367 INTERNET_STATUS_REQUEST_SENT,
4368 &len, sizeof(DWORD));
4375 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4376 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4378 if (res != ERROR_SUCCESS)
4381 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4382 /* FIXME: We should know that connection is closed before sending
4383 * headers. Otherwise wrong callbacks are executed */
4384 if(!responseLen && reusing_connection) {
4385 TRACE("Connection closed by server, reconnecting\n");
4390 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4391 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4394 HTTP_ProcessCookies(request);
4395 HTTP_ProcessExpires(request);
4396 HTTP_ProcessLastModified(request);
4398 res = set_content_length(request);
4399 if(res != ERROR_SUCCESS)
4401 if(!request->contentLength)
4402 HTTP_FinishedReading(request);
4404 dwBufferSize = sizeof(dwStatusCode);
4405 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4406 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4409 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4411 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4412 dwBufferSize=sizeof(szNewLocation);
4413 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4414 dwStatusCode == HTTP_STATUS_MOVED ||
4415 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4416 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4418 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4420 HeapFree(GetProcessHeap(), 0, request->verb);
4421 request->verb = heap_strdupW(szGET);
4423 HTTP_DrainContent(request);
4424 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4426 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4427 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4428 res = HTTP_HandleRedirect(request, new_url);
4429 if (res == ERROR_SUCCESS)
4431 HeapFree(GetProcessHeap(), 0, requestString);
4434 HeapFree( GetProcessHeap(), 0, new_url );
4439 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4441 WCHAR szAuthValue[2048];
4443 if (dwStatusCode == HTTP_STATUS_DENIED)
4445 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4447 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4449 if (HTTP_DoAuthorization(request, szAuthValue,
4451 request->session->userName,
4452 request->session->password,
4455 HeapFree(GetProcessHeap(), 0, requestString);
4462 TRACE("Cleaning wrong authorization data\n");
4463 destroy_authinfo(request->authInfo);
4464 request->authInfo = NULL;
4467 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4470 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4472 if (HTTP_DoAuthorization(request, szAuthValue,
4473 &request->proxyAuthInfo,
4474 request->session->appInfo->proxyUsername,
4475 request->session->appInfo->proxyPassword,
4484 TRACE("Cleaning wrong proxy authorization data\n");
4485 destroy_authinfo(request->proxyAuthInfo);
4486 request->proxyAuthInfo = NULL;
4492 res = ERROR_SUCCESS;
4496 if(res == ERROR_SUCCESS)
4497 HTTP_CacheRequest(request);
4501 HeapFree(GetProcessHeap(), 0, requestString);
4503 /* TODO: send notification for P3P header */
4505 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4507 if (res == ERROR_SUCCESS && request->bytesWritten == request->bytesToWrite)
4508 HTTP_ReceiveRequestData(request, TRUE);
4511 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4514 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4515 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4516 sizeof(INTERNET_ASYNC_RESULT));
4524 /***********************************************************************
4526 * Helper functions for the HttpSendRequest(Ex) functions
4529 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4531 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4532 http_request_t *request = (http_request_t*) workRequest->hdr;
4534 TRACE("%p\n", request);
4536 HTTP_HttpSendRequestW(request, req->lpszHeader,
4537 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4538 req->dwContentLength, req->bEndRequest);
4540 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
4544 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4548 DWORD res = ERROR_SUCCESS;
4550 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4551 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4553 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4555 res = ERROR_HTTP_HEADER_NOT_FOUND;
4557 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4558 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4560 /* process cookies here. Is this right? */
4561 HTTP_ProcessCookies(request);
4562 HTTP_ProcessExpires(request);
4563 HTTP_ProcessLastModified(request);
4565 if ((res = set_content_length( request )) == ERROR_SUCCESS) {
4566 if(!request->contentLength)
4567 HTTP_FinishedReading(request);
4570 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4572 DWORD dwCode,dwCodeLength = sizeof(DWORD);
4573 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
4574 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
4576 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4577 dwBufferSize=sizeof(szNewLocation);
4578 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4580 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4582 HeapFree(GetProcessHeap(), 0, request->verb);
4583 request->verb = heap_strdupW(szGET);
4585 HTTP_DrainContent(request);
4586 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4588 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4589 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4590 res = HTTP_HandleRedirect(request, new_url);
4591 if (res == ERROR_SUCCESS)
4592 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4593 HeapFree( GetProcessHeap(), 0, new_url );
4599 if (res == ERROR_SUCCESS) {
4600 HTTP_ReceiveRequestData(request, TRUE);
4602 INTERNET_ASYNC_RESULT iar = {0, res};
4604 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4605 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4606 sizeof(INTERNET_ASYNC_RESULT));
4612 /***********************************************************************
4613 * HttpEndRequestA (WININET.@)
4615 * Ends an HTTP request that was started by HttpSendRequestEx
4618 * TRUE if successful
4622 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4623 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4625 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4629 SetLastError(ERROR_INVALID_PARAMETER);
4633 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4636 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4638 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4639 http_request_t *request = (http_request_t*)work->hdr;
4641 TRACE("%p\n", request);
4643 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4646 /***********************************************************************
4647 * HttpEndRequestW (WININET.@)
4649 * Ends an HTTP request that was started by HttpSendRequestEx
4652 * TRUE if successful
4656 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4657 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4659 http_request_t *request;
4666 SetLastError(ERROR_INVALID_PARAMETER);
4670 request = (http_request_t*) get_handle_object( hRequest );
4672 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4674 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4676 WININET_Release( &request->hdr );
4679 request->hdr.dwFlags |= dwFlags;
4681 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4684 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
4686 work.asyncproc = AsyncHttpEndRequestProc;
4687 work.hdr = WININET_AddRef( &request->hdr );
4689 work_endrequest = &work.u.HttpEndRequestW;
4690 work_endrequest->dwFlags = dwFlags;
4691 work_endrequest->dwContext = dwContext;
4693 INTERNET_AsyncCall(&work);
4694 res = ERROR_IO_PENDING;
4697 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
4699 WININET_Release( &request->hdr );
4700 TRACE("%u <--\n", res);
4701 if(res != ERROR_SUCCESS)
4703 return res == ERROR_SUCCESS;
4706 /***********************************************************************
4707 * HttpSendRequestExA (WININET.@)
4709 * Sends the specified request to the HTTP server and allows chunked
4714 * Failure: FALSE, call GetLastError() for more information.
4716 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4717 LPINTERNET_BUFFERSA lpBuffersIn,
4718 LPINTERNET_BUFFERSA lpBuffersOut,
4719 DWORD dwFlags, DWORD_PTR dwContext)
4721 INTERNET_BUFFERSW BuffersInW;
4724 LPWSTR header = NULL;
4726 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4727 lpBuffersOut, dwFlags, dwContext);
4731 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4732 if (lpBuffersIn->lpcszHeader)
4734 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4735 lpBuffersIn->dwHeadersLength,0,0);
4736 header = heap_alloc(headerlen*sizeof(WCHAR));
4737 if (!(BuffersInW.lpcszHeader = header))
4739 SetLastError(ERROR_OUTOFMEMORY);
4742 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4743 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4747 BuffersInW.lpcszHeader = NULL;
4748 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4749 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4750 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4751 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4752 BuffersInW.Next = NULL;
4755 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4757 HeapFree(GetProcessHeap(),0,header);
4762 /***********************************************************************
4763 * HttpSendRequestExW (WININET.@)
4765 * Sends the specified request to the HTTP server and allows chunked
4770 * Failure: FALSE, call GetLastError() for more information.
4772 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4773 LPINTERNET_BUFFERSW lpBuffersIn,
4774 LPINTERNET_BUFFERSW lpBuffersOut,
4775 DWORD dwFlags, DWORD_PTR dwContext)
4777 http_request_t *request;
4778 http_session_t *session;
4782 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4783 lpBuffersOut, dwFlags, dwContext);
4785 request = (http_request_t*) get_handle_object( hRequest );
4787 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4789 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4793 session = request->session;
4794 assert(session->hdr.htype == WH_HHTTPSESSION);
4795 hIC = session->appInfo;
4796 assert(hIC->hdr.htype == WH_HINIT);
4798 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4800 WORKREQUEST workRequest;
4801 struct WORKREQ_HTTPSENDREQUESTW *req;
4803 workRequest.asyncproc = AsyncHttpSendRequestProc;
4804 workRequest.hdr = WININET_AddRef( &request->hdr );
4805 req = &workRequest.u.HttpSendRequestW;
4810 if (lpBuffersIn->lpcszHeader)
4812 if (lpBuffersIn->dwHeadersLength == ~0u)
4813 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4815 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4817 req->lpszHeader = heap_alloc(size);
4818 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4820 else req->lpszHeader = NULL;
4822 req->dwHeaderLength = size / sizeof(WCHAR);
4823 req->lpOptional = lpBuffersIn->lpvBuffer;
4824 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4825 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4829 req->lpszHeader = NULL;
4830 req->dwHeaderLength = 0;
4831 req->lpOptional = NULL;
4832 req->dwOptionalLength = 0;
4833 req->dwContentLength = 0;
4836 req->bEndRequest = FALSE;
4838 INTERNET_AsyncCall(&workRequest);
4840 * This is from windows.
4842 res = ERROR_IO_PENDING;
4847 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4848 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4849 lpBuffersIn->dwBufferTotal, FALSE);
4851 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
4856 WININET_Release( &request->hdr );
4860 return res == ERROR_SUCCESS;
4863 /***********************************************************************
4864 * HttpSendRequestW (WININET.@)
4866 * Sends the specified request to the HTTP server
4873 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4874 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4876 http_request_t *request;
4877 http_session_t *session = NULL;
4878 appinfo_t *hIC = NULL;
4879 DWORD res = ERROR_SUCCESS;
4881 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4882 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4884 request = (http_request_t*) get_handle_object( hHttpRequest );
4885 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4887 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4891 session = request->session;
4892 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
4894 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4898 hIC = session->appInfo;
4899 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4901 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4905 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4907 WORKREQUEST workRequest;
4908 struct WORKREQ_HTTPSENDREQUESTW *req;
4910 workRequest.asyncproc = AsyncHttpSendRequestProc;
4911 workRequest.hdr = WININET_AddRef( &request->hdr );
4912 req = &workRequest.u.HttpSendRequestW;
4917 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4918 else size = dwHeaderLength * sizeof(WCHAR);
4920 req->lpszHeader = heap_alloc(size);
4921 memcpy(req->lpszHeader, lpszHeaders, size);
4924 req->lpszHeader = 0;
4925 req->dwHeaderLength = dwHeaderLength;
4926 req->lpOptional = lpOptional;
4927 req->dwOptionalLength = dwOptionalLength;
4928 req->dwContentLength = dwOptionalLength;
4929 req->bEndRequest = TRUE;
4931 INTERNET_AsyncCall(&workRequest);
4933 * This is from windows.
4935 res = ERROR_IO_PENDING;
4939 res = HTTP_HttpSendRequestW(request, lpszHeaders,
4940 dwHeaderLength, lpOptional, dwOptionalLength,
4941 dwOptionalLength, TRUE);
4945 WININET_Release( &request->hdr );
4948 return res == ERROR_SUCCESS;
4951 /***********************************************************************
4952 * HttpSendRequestA (WININET.@)
4954 * Sends the specified request to the HTTP server
4961 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4962 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4965 LPWSTR szHeaders=NULL;
4966 DWORD nLen=dwHeaderLength;
4967 if(lpszHeaders!=NULL)
4969 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4970 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
4971 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4973 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4974 HeapFree(GetProcessHeap(),0,szHeaders);
4978 /***********************************************************************
4979 * HTTPSESSION_Destroy (internal)
4981 * Deallocate session handle
4984 static void HTTPSESSION_Destroy(object_header_t *hdr)
4986 http_session_t *session = (http_session_t*) hdr;
4988 TRACE("%p\n", session);
4990 WININET_Release(&session->appInfo->hdr);
4992 HeapFree(GetProcessHeap(), 0, session->hostName);
4993 HeapFree(GetProcessHeap(), 0, session->serverName);
4994 HeapFree(GetProcessHeap(), 0, session->password);
4995 HeapFree(GetProcessHeap(), 0, session->userName);
4998 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5001 case INTERNET_OPTION_HANDLE_TYPE:
5002 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5004 if (*size < sizeof(ULONG))
5005 return ERROR_INSUFFICIENT_BUFFER;
5007 *size = sizeof(DWORD);
5008 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5009 return ERROR_SUCCESS;
5012 return INET_QueryOption(hdr, option, buffer, size, unicode);
5015 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5017 http_session_t *ses = (http_session_t*)hdr;
5020 case INTERNET_OPTION_USERNAME:
5022 HeapFree(GetProcessHeap(), 0, ses->userName);
5023 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5024 return ERROR_SUCCESS;
5026 case INTERNET_OPTION_PASSWORD:
5028 HeapFree(GetProcessHeap(), 0, ses->password);
5029 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5030 return ERROR_SUCCESS;
5035 return ERROR_INTERNET_INVALID_OPTION;
5038 static const object_vtbl_t HTTPSESSIONVtbl = {
5039 HTTPSESSION_Destroy,
5041 HTTPSESSION_QueryOption,
5042 HTTPSESSION_SetOption,
5051 /***********************************************************************
5052 * HTTP_Connect (internal)
5054 * Create http session handle
5057 * HINTERNET a session handle on success
5061 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5062 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5063 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5064 DWORD dwInternalFlags, HINTERNET *ret)
5066 http_session_t *session = NULL;
5070 if (!lpszServerName || !lpszServerName[0])
5071 return ERROR_INVALID_PARAMETER;
5073 assert( hIC->hdr.htype == WH_HINIT );
5075 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5077 return ERROR_OUTOFMEMORY;
5080 * According to my tests. The name is not resolved until a request is sent
5083 session->hdr.htype = WH_HHTTPSESSION;
5084 session->hdr.dwFlags = dwFlags;
5085 session->hdr.dwContext = dwContext;
5086 session->hdr.dwInternalFlags |= dwInternalFlags;
5088 WININET_AddRef( &hIC->hdr );
5089 session->appInfo = hIC;
5090 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5092 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5093 if(hIC->proxyBypass)
5094 FIXME("Proxy bypass is ignored.\n");
5096 session->serverName = heap_strdupW(lpszServerName);
5097 session->hostName = heap_strdupW(lpszServerName);
5098 if (lpszUserName && lpszUserName[0])
5099 session->userName = heap_strdupW(lpszUserName);
5100 if (lpszPassword && lpszPassword[0])
5101 session->password = heap_strdupW(lpszPassword);
5102 session->serverPort = serverPort;
5103 session->hostPort = serverPort;
5105 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5106 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5108 INTERNET_SendCallback(&hIC->hdr, dwContext,
5109 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5114 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5118 TRACE("%p --> %p\n", hIC, session);
5120 *ret = session->hdr.hInternet;
5121 return ERROR_SUCCESS;
5125 /***********************************************************************
5126 * HTTP_OpenConnection (internal)
5128 * Connect to a web server
5135 static DWORD HTTP_OpenConnection(http_request_t *request)
5137 http_session_t *session;
5138 appinfo_t *hIC = NULL;
5139 char szaddr[INET6_ADDRSTRLEN];
5141 DWORD res = ERROR_SUCCESS;
5146 if (request->hdr.htype != WH_HHTTPREQ)
5148 res = ERROR_INVALID_PARAMETER;
5152 if (NETCON_connected(&request->netConnection))
5154 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS) goto lend;
5156 session = request->session;
5158 hIC = session->appInfo;
5159 switch (session->socketAddress.ss_family)
5162 addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
5165 addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
5168 WARN("unsupported family %d\n", session->socketAddress.ss_family);
5169 return ERROR_INTERNET_NAME_NOT_RESOLVED;
5171 inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
5172 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5173 INTERNET_STATUS_CONNECTING_TO_SERVER,
5177 res = NETCON_create(&request->netConnection, session->socketAddress.ss_family, SOCK_STREAM, 0);
5178 if (res != ERROR_SUCCESS)
5180 WARN("Socket creation failed: %u\n", res);
5184 res = NETCON_connect(&request->netConnection, (struct sockaddr *)&session->socketAddress,
5186 if(res != ERROR_SUCCESS)
5189 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5190 INTERNET_STATUS_CONNECTED_TO_SERVER,
5191 szaddr, strlen(szaddr)+1);
5193 if (request->hdr.dwFlags & INTERNET_FLAG_SECURE)
5195 /* Note: we differ from Microsoft's WinINet here. they seem to have
5196 * a bug that causes no status callbacks to be sent when starting
5197 * a tunnel to a proxy server using the CONNECT verb. i believe our
5198 * behaviour to be more correct and to not cause any incompatibilities
5199 * because using a secure connection through a proxy server is a rare
5200 * case that would be hard for anyone to depend on */
5201 if (hIC->proxy && (res = HTTP_SecureProxyConnect(request)) != ERROR_SUCCESS) {
5202 HTTPREQ_CloseConnection(&request->hdr);
5206 res = NETCON_secure_connect(&request->netConnection, session->hostName);
5207 if(res != ERROR_SUCCESS)
5209 WARN("Couldn't connect securely to host\n");
5211 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
5212 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
5213 || res == ERROR_INTERNET_INVALID_CA
5214 || res == ERROR_INTERNET_SEC_CERT_NO_REV
5215 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
5216 || res == ERROR_INTERNET_SEC_CERT_REVOKED
5217 || res == ERROR_INTERNET_SEC_INVALID_CERT
5218 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
5219 res = ERROR_INTERNET_SEC_CERT_ERRORS;
5221 HTTPREQ_CloseConnection(&request->hdr);
5228 reset_data_stream(request);
5230 TRACE("%d <--\n", res);
5235 /***********************************************************************
5236 * HTTP_clear_response_headers (internal)
5238 * clear out any old response headers
5240 static void HTTP_clear_response_headers( http_request_t *request )
5244 for( i=0; i<request->nCustHeaders; i++)
5246 if( !request->custHeaders[i].lpszField )
5248 if( !request->custHeaders[i].lpszValue )
5250 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5252 HTTP_DeleteCustomHeader( request, i );
5257 /***********************************************************************
5258 * HTTP_GetResponseHeaders (internal)
5260 * Read server response
5267 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5270 WCHAR buffer[MAX_REPLY_LEN];
5271 DWORD buflen = MAX_REPLY_LEN;
5272 BOOL bSuccess = FALSE;
5274 char bufferA[MAX_REPLY_LEN];
5275 LPWSTR status_code = NULL, status_text = NULL;
5276 DWORD cchMaxRawHeaders = 1024;
5277 LPWSTR lpszRawHeaders = NULL;
5279 DWORD cchRawHeaders = 0;
5280 BOOL codeHundred = FALSE;
5284 if (!NETCON_connected(&request->netConnection))
5288 static const WCHAR szHundred[] = {'1','0','0',0};
5290 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5292 buflen = MAX_REPLY_LEN;
5293 if (!read_line(request, bufferA, &buflen))
5296 /* clear old response headers (eg. from a redirect response) */
5298 HTTP_clear_response_headers( request );
5303 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5304 /* check is this a status code line? */
5305 if (!strncmpW(buffer, g_szHttp1_0, 4))
5307 /* split the version from the status code */
5308 status_code = strchrW( buffer, ' ' );
5313 /* split the status code from the status text */
5314 status_text = strchrW( status_code, ' ' );
5319 TRACE("version [%s] status code [%s] status text [%s]\n",
5320 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5322 codeHundred = (!strcmpW(status_code, szHundred));
5324 else if (!codeHundred)
5326 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5328 HeapFree(GetProcessHeap(), 0, request->version);
5329 HeapFree(GetProcessHeap(), 0, request->statusText);
5331 request->version = heap_strdupW(g_szHttp1_0);
5332 request->statusText = heap_strdupW(szOK);
5334 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5335 request->rawHeaders = heap_strdupW(szDefaultHeader);
5340 } while (codeHundred);
5342 /* Add status code */
5343 HTTP_ProcessHeader(request, szStatus, status_code,
5344 HTTP_ADDHDR_FLAG_REPLACE);
5346 HeapFree(GetProcessHeap(),0,request->version);
5347 HeapFree(GetProcessHeap(),0,request->statusText);
5349 request->version = heap_strdupW(buffer);
5350 request->statusText = heap_strdupW(status_text);
5352 /* Restore the spaces */
5353 *(status_code-1) = ' ';
5354 *(status_text-1) = ' ';
5356 /* regenerate raw headers */
5357 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5358 if (!lpszRawHeaders) goto lend;
5360 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5361 cchMaxRawHeaders *= 2;
5362 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5363 if (temp == NULL) goto lend;
5364 lpszRawHeaders = temp;
5365 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5366 cchRawHeaders += (buflen-1);
5367 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5368 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5369 lpszRawHeaders[cchRawHeaders] = '\0';
5371 /* Parse each response line */
5374 buflen = MAX_REPLY_LEN;
5375 if (read_line(request, bufferA, &buflen))
5377 LPWSTR * pFieldAndValue;
5379 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5381 if (!bufferA[0]) break;
5382 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5384 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5387 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5388 cchMaxRawHeaders *= 2;
5389 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5390 if (temp == NULL) goto lend;
5391 lpszRawHeaders = temp;
5392 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5393 cchRawHeaders += (buflen-1);
5394 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5395 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5396 lpszRawHeaders[cchRawHeaders] = '\0';
5398 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5399 HTTP_ADDREQ_FLAG_ADD );
5401 HTTP_FreeTokens(pFieldAndValue);
5412 /* make sure the response header is terminated with an empty line. Some apps really
5413 truly care about that empty line being there for some reason. Just add it to the
5415 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5417 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5418 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5419 if (temp == NULL) goto lend;
5420 lpszRawHeaders = temp;
5423 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5425 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5426 request->rawHeaders = lpszRawHeaders;
5427 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5437 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
5442 /***********************************************************************
5443 * HTTP_InterpretHttpHeader (internal)
5445 * Parse server response
5449 * Pointer to array of field, value, NULL on success.
5452 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5454 LPWSTR * pTokenPair;
5458 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5460 pszColon = strchrW(buffer, ':');
5461 /* must have two tokens */
5464 HTTP_FreeTokens(pTokenPair);
5466 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5470 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5473 HTTP_FreeTokens(pTokenPair);
5476 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5477 pTokenPair[0][pszColon - buffer] = '\0';
5481 len = strlenW(pszColon);
5482 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5485 HTTP_FreeTokens(pTokenPair);
5488 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5490 strip_spaces(pTokenPair[0]);
5491 strip_spaces(pTokenPair[1]);
5493 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5497 /***********************************************************************
5498 * HTTP_ProcessHeader (internal)
5500 * Stuff header into header tables according to <dwModifier>
5504 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5506 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5508 LPHTTPHEADERW lphttpHdr = NULL;
5510 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5511 DWORD res = ERROR_HTTP_INVALID_HEADER;
5513 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5515 /* REPLACE wins out over ADD */
5516 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5517 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5519 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5522 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5526 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5527 return ERROR_HTTP_INVALID_HEADER;
5528 lphttpHdr = &request->custHeaders[index];
5534 hdr.lpszField = (LPWSTR)field;
5535 hdr.lpszValue = (LPWSTR)value;
5536 hdr.wFlags = hdr.wCount = 0;
5538 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5539 hdr.wFlags |= HDR_ISREQUEST;
5541 return HTTP_InsertCustomHeader(request, &hdr);
5543 /* no value to delete */
5544 else return ERROR_SUCCESS;
5546 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5547 lphttpHdr->wFlags |= HDR_ISREQUEST;
5549 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5551 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5553 HTTP_DeleteCustomHeader( request, index );
5559 hdr.lpszField = (LPWSTR)field;
5560 hdr.lpszValue = (LPWSTR)value;
5561 hdr.wFlags = hdr.wCount = 0;
5563 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5564 hdr.wFlags |= HDR_ISREQUEST;
5566 return HTTP_InsertCustomHeader(request, &hdr);
5569 return ERROR_SUCCESS;
5571 else if (dwModifier & COALESCEFLAGS)
5576 INT origlen = strlenW(lphttpHdr->lpszValue);
5577 INT valuelen = strlenW(value);
5579 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5582 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5584 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5587 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5590 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5592 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5595 lphttpHdr->lpszValue = lpsztmp;
5596 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5599 lphttpHdr->lpszValue[origlen] = ch;
5601 lphttpHdr->lpszValue[origlen] = ' ';
5605 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5606 lphttpHdr->lpszValue[len] = '\0';
5607 res = ERROR_SUCCESS;
5611 WARN("heap_realloc (%d bytes) failed\n",len+1);
5612 res = ERROR_OUTOFMEMORY;
5615 TRACE("<-- %d\n", res);
5620 /***********************************************************************
5621 * HTTP_FinishedReading (internal)
5623 * Called when all content from server has been read by client.
5626 static BOOL HTTP_FinishedReading(http_request_t *request)
5628 BOOL keepalive = HTTP_KeepAlive(request);
5635 HTTPREQ_CloseConnection(&request->hdr);
5638 /* FIXME: store data in the URL cache here */
5644 /***********************************************************************
5645 * HTTP_GetCustomHeaderIndex (internal)
5647 * Return index of custom header from header array
5650 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5651 int requested_index, BOOL request_only)
5655 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5657 for (index = 0; index < request->nCustHeaders; index++)
5659 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5662 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5665 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5668 if (requested_index == 0)
5673 if (index >= request->nCustHeaders)
5676 TRACE("Return: %d\n", index);
5681 /***********************************************************************
5682 * HTTP_InsertCustomHeader (internal)
5684 * Insert header into array
5687 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5690 LPHTTPHEADERW lph = NULL;
5692 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5693 count = request->nCustHeaders + 1;
5695 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5697 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5700 return ERROR_OUTOFMEMORY;
5702 request->custHeaders = lph;
5703 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5704 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5705 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5706 request->custHeaders[count-1].wCount= lpHdr->wCount;
5707 request->nCustHeaders++;
5709 return ERROR_SUCCESS;
5713 /***********************************************************************
5714 * HTTP_DeleteCustomHeader (internal)
5716 * Delete header from array
5717 * If this function is called, the indexs may change.
5719 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5721 if( request->nCustHeaders <= 0 )
5723 if( index >= request->nCustHeaders )
5725 request->nCustHeaders--;
5727 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszField);
5728 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszValue);
5730 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5731 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5732 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5738 /***********************************************************************
5739 * HTTP_VerifyValidHeader (internal)
5741 * Verify the given header is not invalid for the given http request
5744 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5746 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5747 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5748 return ERROR_HTTP_INVALID_HEADER;
5750 return ERROR_SUCCESS;
5753 /***********************************************************************
5754 * IsHostInProxyBypassList (@)
5759 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5761 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5765 /***********************************************************************
5766 * InternetShowSecurityInfoByURLA (@)
5768 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5770 FIXME("stub: %s %p\n", url, window);
5774 /***********************************************************************
5775 * InternetShowSecurityInfoByURLW (@)
5777 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5779 FIXME("stub: %s %p\n", debugstr_w(url), window);