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 if(NETCON_connected(&req->netConnection)) {
1999 FIXME("unsupported without active connection\n");
2000 return ERROR_SUCCESS;
2003 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
2006 case INTERNET_OPTION_USERNAME:
2007 HeapFree(GetProcessHeap(), 0, req->session->userName);
2008 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2009 return ERROR_SUCCESS;
2011 case INTERNET_OPTION_PASSWORD:
2012 HeapFree(GetProcessHeap(), 0, req->session->password);
2013 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2014 return ERROR_SUCCESS;
2015 case INTERNET_OPTION_HTTP_DECODING:
2016 if(size != sizeof(BOOL))
2017 return ERROR_INVALID_PARAMETER;
2018 req->decoding = *(BOOL*)buffer;
2019 return ERROR_SUCCESS;
2022 return ERROR_INTERNET_INVALID_OPTION;
2025 /* read some more data into the read buffer (the read section must be held) */
2026 static DWORD read_more_data( http_request_t *req, int maxlen )
2033 /* move existing data to the start of the buffer */
2035 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2039 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2041 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
2042 maxlen - req->read_size, 0, &len );
2043 if(res == ERROR_SUCCESS)
2044 req->read_size += len;
2049 /* remove some amount of data from the read buffer (the read section must be held) */
2050 static void remove_data( http_request_t *req, int count )
2052 if (!(req->read_size -= count)) req->read_pos = 0;
2053 else req->read_pos += count;
2056 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2058 int count, bytes_read, pos = 0;
2061 EnterCriticalSection( &req->read_section );
2064 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2068 count = eol - (req->read_buf + req->read_pos);
2069 bytes_read = count + 1;
2071 else count = bytes_read = req->read_size;
2073 count = min( count, *len - pos );
2074 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2076 remove_data( req, bytes_read );
2079 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2082 TRACE( "returning empty string\n" );
2083 LeaveCriticalSection( &req->read_section );
2084 INTERNET_SetLastError(res);
2088 LeaveCriticalSection( &req->read_section );
2092 if (pos && buffer[pos - 1] == '\r') pos--;
2095 buffer[*len - 1] = 0;
2096 TRACE( "returning %s\n", debugstr_a(buffer));
2100 /* check if we have reached the end of the data to read (the read section must be held) */
2101 static BOOL end_of_read_data( http_request_t *req )
2103 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2106 /* fetch some more data into the read buffer (the read section must be held) */
2107 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode)
2111 if(req->read_size == sizeof(req->read_buf))
2112 return ERROR_SUCCESS;
2116 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2120 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2121 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2122 req->read_size += read;
2124 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2128 /* return the size of data available to be read immediately (the read section must be held) */
2129 static DWORD get_avail_data( http_request_t *req )
2131 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2134 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2138 NETCON_query_data_available(&req->netConnection, &avail);
2142 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2144 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2145 return netconn_stream->content_read == netconn_stream->content_length || !NETCON_connected(&req->netConnection);
2148 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2149 DWORD *read, read_mode_t read_mode)
2151 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2154 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2156 if(read_mode == READMODE_NOBLOCK)
2157 size = min(size, netconn_get_avail_data(stream, req));
2160 if(NETCON_recv(&req->netConnection, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2164 netconn_stream->content_read += *read = len;
2165 TRACE("read %u bytes\n", len);
2166 return ERROR_SUCCESS;
2169 static void netconn_destroy(data_stream_t *stream)
2173 static const data_stream_vtbl_t netconn_stream_vtbl = {
2174 netconn_get_avail_data,
2175 netconn_end_of_data,
2180 /* read some more data into the read buffer (the read section must be held) */
2181 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2186 if (stream->buf_pos)
2188 /* move existing data to the start of the buffer */
2189 if(stream->buf_size)
2190 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2191 stream->buf_pos = 0;
2194 if (maxlen == -1) maxlen = sizeof(stream->buf);
2196 res = NETCON_recv( &req->netConnection, stream->buf + stream->buf_size,
2197 maxlen - stream->buf_size, 0, &len );
2198 if(res == ERROR_SUCCESS)
2199 stream->buf_size += len;
2204 /* remove some amount of data from the read buffer (the read section must be held) */
2205 static void remove_chunked_data(chunked_stream_t *stream, int count)
2207 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2208 else stream->buf_pos += count;
2211 /* discard data contents until we reach end of line (the read section must be held) */
2212 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2218 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2221 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2224 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2225 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2226 } while (stream->buf_size);
2227 return ERROR_SUCCESS;
2230 /* read the size of the next chunk (the read section must be held) */
2231 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2234 DWORD chunk_size = 0, res;
2236 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2241 while (stream->buf_size)
2243 char ch = stream->buf[stream->buf_pos];
2244 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2245 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2246 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2247 else if (ch == ';' || ch == '\r' || ch == '\n')
2249 TRACE( "reading %u byte chunk\n", chunk_size );
2250 stream->chunk_size = chunk_size;
2251 req->contentLength += chunk_size;
2252 return discard_chunked_eol(stream, req);
2254 remove_chunked_data(stream, 1);
2256 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2257 if (!stream->buf_size)
2259 stream->chunk_size = 0;
2260 return ERROR_SUCCESS;
2265 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2267 /* Allow reading only from read buffer */
2271 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2273 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2274 return !chunked_stream->chunk_size;
2277 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2278 DWORD *read, read_mode_t read_mode)
2280 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2281 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2283 if(chunked_stream->chunk_size == ~0u) {
2284 res = start_next_chunk(chunked_stream, req);
2285 if(res != ERROR_SUCCESS)
2289 while(size && chunked_stream->chunk_size) {
2290 if(chunked_stream->buf_size) {
2291 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2293 /* this could block */
2294 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2297 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2298 remove_chunked_data(chunked_stream, read_bytes);
2300 read_bytes = min(size, chunked_stream->chunk_size);
2302 if(read_mode == READMODE_NOBLOCK) {
2305 if(!NETCON_query_data_available(&req->netConnection, &avail) || !avail)
2307 if(read_bytes > avail)
2310 /* this could block */
2311 if(read_bytes == chunked_stream->chunk_size)
2315 res = NETCON_recv(&req->netConnection, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2316 if(res != ERROR_SUCCESS)
2320 chunked_stream->chunk_size -= read_bytes;
2322 ret_read += read_bytes;
2323 if(!chunked_stream->chunk_size) {
2324 assert(read_mode != READMODE_NOBLOCK);
2325 res = start_next_chunk(chunked_stream, req);
2326 if(res != ERROR_SUCCESS)
2330 if(read_mode == READMODE_ASYNC)
2331 read_mode = READMODE_NOBLOCK;
2334 TRACE("read %u bytes\n", ret_read);
2339 static void chunked_destroy(data_stream_t *stream)
2341 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2342 heap_free(chunked_stream);
2345 static const data_stream_vtbl_t chunked_stream_vtbl = {
2346 chunked_get_avail_data,
2347 chunked_end_of_data,
2352 /* set the request content length based on the headers */
2353 static DWORD set_content_length(http_request_t *request)
2355 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2359 size = sizeof(request->contentLength);
2360 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2361 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2362 request->contentLength = ~0u;
2363 request->netconn_stream.content_length = request->contentLength;
2364 request->netconn_stream.content_read = request->read_size;
2366 size = sizeof(encoding);
2367 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2368 !strcmpiW(encoding, szChunked))
2370 chunked_stream_t *chunked_stream;
2372 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2374 return ERROR_OUTOFMEMORY;
2376 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2377 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2378 chunked_stream->chunk_size = ~0u;
2380 if(request->read_size) {
2381 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2382 chunked_stream->buf_size = request->read_size;
2383 request->read_size = request->read_pos = 0;
2386 request->data_stream = &chunked_stream->data_stream;
2387 request->contentLength = ~0u;
2388 request->read_chunked = TRUE;
2391 if(request->decoding) {
2394 static const WCHAR gzipW[] = {'g','z','i','p',0};
2396 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2397 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2398 return init_gzip_stream(request);
2401 return ERROR_SUCCESS;
2404 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2406 INTERNET_ASYNC_RESULT iar;
2411 EnterCriticalSection( &req->read_section );
2412 if ((res = refill_read_buffer(req, READMODE_ASYNC)) == ERROR_SUCCESS) {
2413 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2414 iar.dwError = first_notif ? 0 : get_avail_data(req);
2415 if(!first_notif && !iar.dwError)
2416 ERR("No data reported!\n");
2421 LeaveCriticalSection( &req->read_section );
2423 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2424 sizeof(INTERNET_ASYNC_RESULT));
2427 /* read data from the http connection (the read section must be held) */
2428 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2430 DWORD current_read = 0, ret_read = 0;
2431 read_mode_t read_mode;
2432 DWORD res = ERROR_SUCCESS;
2434 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2436 EnterCriticalSection( &req->read_section );
2438 if(req->read_size) {
2439 ret_read = min(size, req->read_size);
2440 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2441 req->read_size -= ret_read;
2442 req->read_pos += ret_read;
2443 if(read_mode == READMODE_ASYNC)
2444 read_mode = READMODE_NOBLOCK;
2447 if(ret_read < size) {
2448 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2449 ret_read += current_read;
2452 LeaveCriticalSection( &req->read_section );
2455 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2457 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2461 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2463 WARN("WriteFile failed: %u\n", GetLastError());
2466 if(end_of_read_data(req))
2467 HTTP_FinishedReading(req);
2473 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2475 http_request_t *req = (http_request_t*)hdr;
2478 EnterCriticalSection( &req->read_section );
2479 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2480 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2482 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2483 if(res == ERROR_SUCCESS)
2485 LeaveCriticalSection( &req->read_section );
2490 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2492 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2493 http_request_t *req = (http_request_t*)workRequest->hdr;
2494 INTERNET_ASYNC_RESULT iar;
2497 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2499 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2500 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2502 iar.dwResult = res == ERROR_SUCCESS;
2505 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2506 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2507 sizeof(INTERNET_ASYNC_RESULT));
2510 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2511 DWORD flags, DWORD_PTR context)
2513 http_request_t *req = (http_request_t*)hdr;
2514 DWORD res, size, read, error = ERROR_SUCCESS;
2516 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2517 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2519 if (buffers->dwStructSize != sizeof(*buffers))
2520 return ERROR_INVALID_PARAMETER;
2522 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2524 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2526 WORKREQUEST workRequest;
2528 if (TryEnterCriticalSection( &req->read_section ))
2530 if (get_avail_data(req))
2532 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2533 &buffers->dwBufferLength, FALSE);
2534 size = buffers->dwBufferLength;
2535 LeaveCriticalSection( &req->read_section );
2538 LeaveCriticalSection( &req->read_section );
2541 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2542 workRequest.hdr = WININET_AddRef(&req->hdr);
2543 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2545 INTERNET_AsyncCall(&workRequest);
2547 return ERROR_IO_PENDING;
2551 size = buffers->dwBufferLength;
2553 EnterCriticalSection( &req->read_section );
2554 if(hdr->dwError == ERROR_SUCCESS)
2555 hdr->dwError = INTERNET_HANDLE_IN_USE;
2556 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2557 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2560 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2561 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2562 if(res != ERROR_SUCCESS)
2565 read += buffers->dwBufferLength;
2566 if(read == size || end_of_read_data(req))
2569 LeaveCriticalSection( &req->read_section );
2571 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2572 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2573 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2574 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2576 EnterCriticalSection( &req->read_section );
2579 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2580 hdr->dwError = ERROR_SUCCESS;
2582 error = hdr->dwError;
2584 LeaveCriticalSection( &req->read_section );
2585 size = buffers->dwBufferLength;
2586 buffers->dwBufferLength = read;
2589 if (res == ERROR_SUCCESS) {
2590 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2591 &size, sizeof(size));
2594 return res==ERROR_SUCCESS ? error : res;
2597 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2599 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2600 http_request_t *req = (http_request_t*)workRequest->hdr;
2601 INTERNET_ASYNC_RESULT iar;
2604 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2606 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2607 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2609 iar.dwResult = res == ERROR_SUCCESS;
2612 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2613 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2614 sizeof(INTERNET_ASYNC_RESULT));
2617 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2618 DWORD flags, DWORD_PTR context)
2621 http_request_t *req = (http_request_t*)hdr;
2622 DWORD res, size, read, error = ERROR_SUCCESS;
2624 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2625 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2627 if (buffers->dwStructSize != sizeof(*buffers))
2628 return ERROR_INVALID_PARAMETER;
2630 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2632 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2634 WORKREQUEST workRequest;
2636 if (TryEnterCriticalSection( &req->read_section ))
2638 if (get_avail_data(req))
2640 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2641 &buffers->dwBufferLength, FALSE);
2642 size = buffers->dwBufferLength;
2643 LeaveCriticalSection( &req->read_section );
2646 LeaveCriticalSection( &req->read_section );
2649 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2650 workRequest.hdr = WININET_AddRef(&req->hdr);
2651 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2653 INTERNET_AsyncCall(&workRequest);
2655 return ERROR_IO_PENDING;
2659 size = buffers->dwBufferLength;
2661 EnterCriticalSection( &req->read_section );
2662 if(hdr->dwError == ERROR_SUCCESS)
2663 hdr->dwError = INTERNET_HANDLE_IN_USE;
2664 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2665 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2668 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2669 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2670 if(res != ERROR_SUCCESS)
2673 read += buffers->dwBufferLength;
2674 if(read == size || end_of_read_data(req))
2677 LeaveCriticalSection( &req->read_section );
2679 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2680 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2681 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2682 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2684 EnterCriticalSection( &req->read_section );
2687 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2688 hdr->dwError = ERROR_SUCCESS;
2690 error = hdr->dwError;
2692 LeaveCriticalSection( &req->read_section );
2693 size = buffers->dwBufferLength;
2694 buffers->dwBufferLength = read;
2697 if (res == ERROR_SUCCESS) {
2698 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2699 &size, sizeof(size));
2702 return res==ERROR_SUCCESS ? error : res;
2705 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2708 http_request_t *request = (http_request_t*)hdr;
2710 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2713 res = NETCON_send(&request->netConnection, buffer, size, 0, (LPINT)written);
2714 if (res == ERROR_SUCCESS)
2715 request->bytesWritten += *written;
2717 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2721 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2723 http_request_t *req = (http_request_t*)workRequest->hdr;
2725 HTTP_ReceiveRequestData(req, FALSE);
2728 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2730 http_request_t *req = (http_request_t*)hdr;
2732 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2734 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2736 WORKREQUEST workRequest;
2738 /* never wait, if we can't enter the section we queue an async request right away */
2739 if (TryEnterCriticalSection( &req->read_section ))
2741 refill_read_buffer(req, READMODE_NOBLOCK);
2742 if ((*available = get_avail_data( req ))) goto done;
2743 if (end_of_read_data( req )) goto done;
2744 LeaveCriticalSection( &req->read_section );
2747 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2748 workRequest.hdr = WININET_AddRef( &req->hdr );
2750 INTERNET_AsyncCall(&workRequest);
2752 return ERROR_IO_PENDING;
2755 EnterCriticalSection( &req->read_section );
2757 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2759 refill_read_buffer( req, READMODE_ASYNC );
2760 *available = get_avail_data( req );
2764 LeaveCriticalSection( &req->read_section );
2766 TRACE( "returning %u\n", *available );
2767 return ERROR_SUCCESS;
2770 static const object_vtbl_t HTTPREQVtbl = {
2772 HTTPREQ_CloseConnection,
2773 HTTPREQ_QueryOption,
2776 HTTPREQ_ReadFileExA,
2777 HTTPREQ_ReadFileExW,
2779 HTTPREQ_QueryDataAvailable,
2783 /***********************************************************************
2784 * HTTP_HttpOpenRequestW (internal)
2786 * Open a HTTP request handle
2789 * HINTERNET a HTTP request handle on success
2793 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
2794 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2795 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2796 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2798 appinfo_t *hIC = NULL;
2799 http_request_t *request;
2804 assert( session->hdr.htype == WH_HHTTPSESSION );
2805 hIC = session->appInfo;
2807 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
2809 return ERROR_OUTOFMEMORY;
2811 request->hdr.htype = WH_HHTTPREQ;
2812 request->hdr.dwFlags = dwFlags;
2813 request->hdr.dwContext = dwContext;
2814 request->contentLength = ~0u;
2816 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
2817 request->data_stream = &request->netconn_stream.data_stream;
2819 InitializeCriticalSection( &request->read_section );
2821 WININET_AddRef( &session->hdr );
2822 request->session = session;
2823 list_add_head( &session->hdr.children, &request->hdr.entry );
2825 if ((res = NETCON_init(&request->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2827 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
2828 request->netConnection.security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
2829 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
2830 request->netConnection.security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
2832 if (lpszObjectName && *lpszObjectName) {
2836 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2837 if (rc != E_POINTER)
2838 len = strlenW(lpszObjectName)+1;
2839 request->path = heap_alloc(len*sizeof(WCHAR));
2840 rc = UrlEscapeW(lpszObjectName, request->path, &len,
2841 URL_ESCAPE_SPACES_ONLY);
2844 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2845 strcpyW(request->path,lpszObjectName);
2848 static const WCHAR slashW[] = {'/',0};
2850 request->path = heap_strdupW(slashW);
2853 if (lpszReferrer && *lpszReferrer)
2854 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2856 if (lpszAcceptTypes)
2859 for (i = 0; lpszAcceptTypes[i]; i++)
2861 if (!*lpszAcceptTypes[i]) continue;
2862 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
2863 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2864 HTTP_ADDHDR_FLAG_REQ |
2865 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2869 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2870 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2872 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
2873 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
2874 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
2878 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
2880 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
2882 res = ERROR_OUTOFMEMORY;
2886 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
2887 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2888 heap_free(host_name);
2891 HTTP_ProcessHeader(request, hostW, session->hostName,
2892 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2894 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
2895 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
2896 INTERNET_DEFAULT_HTTPS_PORT :
2897 INTERNET_DEFAULT_HTTP_PORT);
2899 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
2900 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2901 INTERNET_DEFAULT_HTTPS_PORT :
2902 INTERNET_DEFAULT_HTTP_PORT);
2904 if (NULL != hIC->proxy && hIC->proxy[0] != 0)
2905 HTTP_DealWithProxy( hIC, session, request );
2907 INTERNET_SendCallback(&session->hdr, dwContext,
2908 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
2912 TRACE("<-- %u (%p)\n", res, request);
2914 if(res != ERROR_SUCCESS) {
2915 WININET_Release( &request->hdr );
2920 *ret = request->hdr.hInternet;
2921 return ERROR_SUCCESS;
2924 /***********************************************************************
2925 * HttpOpenRequestW (WININET.@)
2927 * Open a HTTP request handle
2930 * HINTERNET a HTTP request handle on success
2934 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2935 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2936 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2937 DWORD dwFlags, DWORD_PTR dwContext)
2939 http_session_t *session;
2940 HINTERNET handle = NULL;
2943 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2944 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2945 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2946 dwFlags, dwContext);
2947 if(lpszAcceptTypes!=NULL)
2950 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2951 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2954 session = (http_session_t*) get_handle_object( hHttpSession );
2955 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
2957 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2962 * My tests seem to show that the windows version does not
2963 * become asynchronous until after this point. And anyhow
2964 * if this call was asynchronous then how would you get the
2965 * necessary HINTERNET pointer returned by this function.
2968 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
2969 lpszVersion, lpszReferrer, lpszAcceptTypes,
2970 dwFlags, dwContext, &handle);
2973 WININET_Release( &session->hdr );
2974 TRACE("returning %p\n", handle);
2975 if(res != ERROR_SUCCESS)
2980 /* read any content returned by the server so that the connection can be
2982 static void HTTP_DrainContent(http_request_t *req)
2986 if (!NETCON_connected(&req->netConnection)) return;
2988 if (req->contentLength == -1)
2990 NETCON_close(&req->netConnection);
2993 if (!strcmpW(req->verb, szHEAD)) return;
2998 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
3000 } while (bytes_read);
3003 static const LPCWSTR header_lookup[] = {
3004 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3005 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3006 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3007 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3008 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3009 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3010 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3011 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3012 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3013 szDate, /* HTTP_QUERY_DATE = 9 */
3014 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3015 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3016 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3017 szURI, /* HTTP_QUERY_URI = 13 */
3018 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3019 NULL, /* HTTP_QUERY_COST = 15 */
3020 NULL, /* HTTP_QUERY_LINK = 16 */
3021 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3022 NULL, /* HTTP_QUERY_VERSION = 18 */
3023 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3024 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3025 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3026 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3027 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3028 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3029 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3030 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3031 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3032 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3033 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3034 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3035 NULL, /* HTTP_QUERY_FROM = 31 */
3036 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3037 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3038 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3039 szReferer, /* HTTP_QUERY_REFERER = 35 */
3040 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3041 szServer, /* HTTP_QUERY_SERVER = 37 */
3042 NULL, /* HTTP_TITLE = 38 */
3043 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3044 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3045 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3046 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3047 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3048 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3049 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3050 NULL, /* HTTP_QUERY_REFRESH = 46 */
3051 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3052 szAge, /* HTTP_QUERY_AGE = 48 */
3053 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3054 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3055 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3056 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3057 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3058 szETag, /* HTTP_QUERY_ETAG = 54 */
3059 hostW, /* HTTP_QUERY_HOST = 55 */
3060 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3061 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3062 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3063 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3064 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3065 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3066 szRange, /* HTTP_QUERY_RANGE = 62 */
3067 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3068 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3069 szVary, /* HTTP_QUERY_VARY = 65 */
3070 szVia, /* HTTP_QUERY_VIA = 66 */
3071 szWarning, /* HTTP_QUERY_WARNING = 67 */
3072 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3073 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3074 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3077 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3079 /***********************************************************************
3080 * HTTP_HttpQueryInfoW (internal)
3082 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3083 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3085 LPHTTPHEADERW lphttpHdr = NULL;
3086 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3087 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3088 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3091 /* Find requested header structure */
3094 case HTTP_QUERY_CUSTOM:
3095 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3096 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3098 case HTTP_QUERY_RAW_HEADERS_CRLF:
3102 DWORD res = ERROR_INVALID_PARAMETER;
3105 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3107 headers = request->rawHeaders;
3110 len = strlenW(headers) * sizeof(WCHAR);
3112 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3114 len += sizeof(WCHAR);
3115 res = ERROR_INSUFFICIENT_BUFFER;
3120 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3123 len = strlenW(szCrLf) * sizeof(WCHAR);
3124 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3126 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3127 res = ERROR_SUCCESS;
3129 *lpdwBufferLength = len;
3132 HeapFree(GetProcessHeap(), 0, headers);
3135 case HTTP_QUERY_RAW_HEADERS:
3137 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3139 LPWSTR pszString = lpBuffer;
3141 for (i = 0; ppszRawHeaderLines[i]; i++)
3142 size += strlenW(ppszRawHeaderLines[i]) + 1;
3144 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3146 HTTP_FreeTokens(ppszRawHeaderLines);
3147 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3148 return ERROR_INSUFFICIENT_BUFFER;
3152 for (i = 0; ppszRawHeaderLines[i]; i++)
3154 DWORD len = strlenW(ppszRawHeaderLines[i]);
3155 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3159 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3161 *lpdwBufferLength = size * sizeof(WCHAR);
3162 HTTP_FreeTokens(ppszRawHeaderLines);
3164 return ERROR_SUCCESS;
3166 case HTTP_QUERY_STATUS_TEXT:
3167 if (request->statusText)
3169 DWORD len = strlenW(request->statusText);
3170 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3172 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3173 return ERROR_INSUFFICIENT_BUFFER;
3177 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3178 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3180 *lpdwBufferLength = len * sizeof(WCHAR);
3181 return ERROR_SUCCESS;
3184 case HTTP_QUERY_VERSION:
3185 if (request->version)
3187 DWORD len = strlenW(request->version);
3188 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3190 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3191 return ERROR_INSUFFICIENT_BUFFER;
3195 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3196 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3198 *lpdwBufferLength = len * sizeof(WCHAR);
3199 return ERROR_SUCCESS;
3202 case HTTP_QUERY_CONTENT_ENCODING:
3203 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3204 requested_index,request_only);
3207 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3209 if (level < LAST_TABLE_HEADER && header_lookup[level])
3210 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3211 requested_index,request_only);
3215 lphttpHdr = &request->custHeaders[index];
3217 /* Ensure header satisfies requested attributes */
3219 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3220 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3222 return ERROR_HTTP_HEADER_NOT_FOUND;
3225 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3227 /* coalesce value to requested type */
3228 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3230 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3231 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3233 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3239 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3241 tmpTM = *gmtime(&tmpTime);
3242 STHook = (SYSTEMTIME *)lpBuffer;
3243 STHook->wDay = tmpTM.tm_mday;
3244 STHook->wHour = tmpTM.tm_hour;
3245 STHook->wMilliseconds = 0;
3246 STHook->wMinute = tmpTM.tm_min;
3247 STHook->wDayOfWeek = tmpTM.tm_wday;
3248 STHook->wMonth = tmpTM.tm_mon + 1;
3249 STHook->wSecond = tmpTM.tm_sec;
3250 STHook->wYear = tmpTM.tm_year;
3252 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3253 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3254 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3256 else if (lphttpHdr->lpszValue)
3258 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3260 if (len > *lpdwBufferLength)
3262 *lpdwBufferLength = len;
3263 return ERROR_INSUFFICIENT_BUFFER;
3267 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3268 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3270 *lpdwBufferLength = len - sizeof(WCHAR);
3272 return ERROR_SUCCESS;
3275 /***********************************************************************
3276 * HttpQueryInfoW (WININET.@)
3278 * Queries for information about an HTTP request
3285 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3286 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3288 http_request_t *request;
3291 if (TRACE_ON(wininet)) {
3292 #define FE(x) { x, #x }
3293 static const wininet_flag_info query_flags[] = {
3294 FE(HTTP_QUERY_MIME_VERSION),
3295 FE(HTTP_QUERY_CONTENT_TYPE),
3296 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3297 FE(HTTP_QUERY_CONTENT_ID),
3298 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3299 FE(HTTP_QUERY_CONTENT_LENGTH),
3300 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3301 FE(HTTP_QUERY_ALLOW),
3302 FE(HTTP_QUERY_PUBLIC),
3303 FE(HTTP_QUERY_DATE),
3304 FE(HTTP_QUERY_EXPIRES),
3305 FE(HTTP_QUERY_LAST_MODIFIED),
3306 FE(HTTP_QUERY_MESSAGE_ID),
3308 FE(HTTP_QUERY_DERIVED_FROM),
3309 FE(HTTP_QUERY_COST),
3310 FE(HTTP_QUERY_LINK),
3311 FE(HTTP_QUERY_PRAGMA),
3312 FE(HTTP_QUERY_VERSION),
3313 FE(HTTP_QUERY_STATUS_CODE),
3314 FE(HTTP_QUERY_STATUS_TEXT),
3315 FE(HTTP_QUERY_RAW_HEADERS),
3316 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3317 FE(HTTP_QUERY_CONNECTION),
3318 FE(HTTP_QUERY_ACCEPT),
3319 FE(HTTP_QUERY_ACCEPT_CHARSET),
3320 FE(HTTP_QUERY_ACCEPT_ENCODING),
3321 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3322 FE(HTTP_QUERY_AUTHORIZATION),
3323 FE(HTTP_QUERY_CONTENT_ENCODING),
3324 FE(HTTP_QUERY_FORWARDED),
3325 FE(HTTP_QUERY_FROM),
3326 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3327 FE(HTTP_QUERY_LOCATION),
3328 FE(HTTP_QUERY_ORIG_URI),
3329 FE(HTTP_QUERY_REFERER),
3330 FE(HTTP_QUERY_RETRY_AFTER),
3331 FE(HTTP_QUERY_SERVER),
3332 FE(HTTP_QUERY_TITLE),
3333 FE(HTTP_QUERY_USER_AGENT),
3334 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3335 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3336 FE(HTTP_QUERY_ACCEPT_RANGES),
3337 FE(HTTP_QUERY_SET_COOKIE),
3338 FE(HTTP_QUERY_COOKIE),
3339 FE(HTTP_QUERY_REQUEST_METHOD),
3340 FE(HTTP_QUERY_REFRESH),
3341 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3343 FE(HTTP_QUERY_CACHE_CONTROL),
3344 FE(HTTP_QUERY_CONTENT_BASE),
3345 FE(HTTP_QUERY_CONTENT_LOCATION),
3346 FE(HTTP_QUERY_CONTENT_MD5),
3347 FE(HTTP_QUERY_CONTENT_RANGE),
3348 FE(HTTP_QUERY_ETAG),
3349 FE(HTTP_QUERY_HOST),
3350 FE(HTTP_QUERY_IF_MATCH),
3351 FE(HTTP_QUERY_IF_NONE_MATCH),
3352 FE(HTTP_QUERY_IF_RANGE),
3353 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3354 FE(HTTP_QUERY_MAX_FORWARDS),
3355 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3356 FE(HTTP_QUERY_RANGE),
3357 FE(HTTP_QUERY_TRANSFER_ENCODING),
3358 FE(HTTP_QUERY_UPGRADE),
3359 FE(HTTP_QUERY_VARY),
3361 FE(HTTP_QUERY_WARNING),
3362 FE(HTTP_QUERY_CUSTOM)
3364 static const wininet_flag_info modifier_flags[] = {
3365 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3366 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3367 FE(HTTP_QUERY_FLAG_NUMBER),
3368 FE(HTTP_QUERY_FLAG_COALESCE)
3371 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3372 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3375 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3376 TRACE(" Attribute:");
3377 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3378 if (query_flags[i].val == info) {
3379 TRACE(" %s", query_flags[i].name);
3383 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3384 TRACE(" Unknown (%08x)", info);
3387 TRACE(" Modifier:");
3388 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3389 if (modifier_flags[i].val & info_mod) {
3390 TRACE(" %s", modifier_flags[i].name);
3391 info_mod &= ~ modifier_flags[i].val;
3396 TRACE(" Unknown (%08x)", info_mod);
3401 request = (http_request_t*) get_handle_object( hHttpRequest );
3402 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3404 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3408 if (lpBuffer == NULL)
3409 *lpdwBufferLength = 0;
3410 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3411 lpBuffer, lpdwBufferLength, lpdwIndex);
3415 WININET_Release( &request->hdr );
3417 TRACE("%u <--\n", res);
3418 if(res != ERROR_SUCCESS)
3420 return res == ERROR_SUCCESS;
3423 /***********************************************************************
3424 * HttpQueryInfoA (WININET.@)
3426 * Queries for information about an HTTP request
3433 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3434 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3440 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3441 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3443 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3444 lpdwBufferLength, lpdwIndex );
3450 len = (*lpdwBufferLength)*sizeof(WCHAR);
3451 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3453 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3459 bufferW = heap_alloc(alloclen);
3460 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3461 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3462 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3469 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3473 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3474 lpBuffer, *lpdwBufferLength, NULL, NULL );
3475 *lpdwBufferLength = len - 1;
3477 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3480 /* since the strings being returned from HttpQueryInfoW should be
3481 * only ASCII characters, it is reasonable to assume that all of
3482 * the Unicode characters can be reduced to a single byte */
3483 *lpdwBufferLength = len / sizeof(WCHAR);
3485 HeapFree(GetProcessHeap(), 0, bufferW );
3490 /***********************************************************************
3491 * HTTP_GetRedirectURL (internal)
3493 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3495 static WCHAR szHttp[] = {'h','t','t','p',0};
3496 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3497 http_session_t *session = request->session;
3498 URL_COMPONENTSW urlComponents;
3499 DWORD url_length = 0;
3501 LPWSTR combined_url;
3503 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3504 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3505 urlComponents.dwSchemeLength = 0;
3506 urlComponents.lpszHostName = session->hostName;
3507 urlComponents.dwHostNameLength = 0;
3508 urlComponents.nPort = session->hostPort;
3509 urlComponents.lpszUserName = session->userName;
3510 urlComponents.dwUserNameLength = 0;
3511 urlComponents.lpszPassword = NULL;
3512 urlComponents.dwPasswordLength = 0;
3513 urlComponents.lpszUrlPath = request->path;
3514 urlComponents.dwUrlPathLength = 0;
3515 urlComponents.lpszExtraInfo = NULL;
3516 urlComponents.dwExtraInfoLength = 0;
3518 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3519 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3522 orig_url = heap_alloc(url_length);
3524 /* convert from bytes to characters */
3525 url_length = url_length / sizeof(WCHAR) - 1;
3526 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3528 HeapFree(GetProcessHeap(), 0, orig_url);
3533 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3534 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3536 HeapFree(GetProcessHeap(), 0, orig_url);
3539 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3541 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3543 HeapFree(GetProcessHeap(), 0, orig_url);
3544 HeapFree(GetProcessHeap(), 0, combined_url);
3547 HeapFree(GetProcessHeap(), 0, orig_url);
3548 return combined_url;
3552 /***********************************************************************
3553 * HTTP_HandleRedirect (internal)
3555 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3557 http_session_t *session = request->session;
3558 appinfo_t *hIC = session->appInfo;
3559 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3560 WCHAR path[INTERNET_MAX_URL_LENGTH];
3565 /* if it's an absolute path, keep the same session info */
3566 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3570 URL_COMPONENTSW urlComponents;
3571 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3572 static WCHAR szHttp[] = {'h','t','t','p',0};
3573 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3579 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3580 urlComponents.lpszScheme = protocol;
3581 urlComponents.dwSchemeLength = 32;
3582 urlComponents.lpszHostName = hostName;
3583 urlComponents.dwHostNameLength = MAXHOSTNAME;
3584 urlComponents.lpszUserName = userName;
3585 urlComponents.dwUserNameLength = 1024;
3586 urlComponents.lpszPassword = NULL;
3587 urlComponents.dwPasswordLength = 0;
3588 urlComponents.lpszUrlPath = path;
3589 urlComponents.dwUrlPathLength = 2048;
3590 urlComponents.lpszExtraInfo = NULL;
3591 urlComponents.dwExtraInfoLength = 0;
3592 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3593 return INTERNET_GetLastError();
3595 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3596 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3598 TRACE("redirect from secure page to non-secure page\n");
3599 /* FIXME: warn about from secure redirect to non-secure page */
3600 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3602 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3603 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3605 TRACE("redirect from non-secure page to secure page\n");
3606 /* FIXME: notify about redirect to secure page */
3607 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3610 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3612 if (lstrlenW(protocol)>4) /*https*/
3613 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3615 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3620 * This upsets redirects to binary files on sourceforge.net
3621 * and gives an html page instead of the target file
3622 * Examination of the HTTP request sent by native wininet.dll
3623 * reveals that it doesn't send a referrer in that case.
3624 * Maybe there's a flag that enables this, or maybe a referrer
3625 * shouldn't be added in case of a redirect.
3628 /* consider the current host as the referrer */
3629 if (session->lpszServerName && *session->lpszServerName)
3630 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3631 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3632 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3635 HeapFree(GetProcessHeap(), 0, session->hostName);
3636 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3637 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3640 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3641 len = lstrlenW(hostName);
3642 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3643 session->hostName = heap_alloc(len*sizeof(WCHAR));
3644 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3647 session->hostName = heap_strdupW(hostName);
3649 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3651 HeapFree(GetProcessHeap(), 0, session->userName);
3652 session->userName = NULL;
3654 session->userName = heap_strdupW(userName);
3658 if (strcmpiW(session->serverName, hostName) || session->serverPort != urlComponents.nPort)
3662 HeapFree(GetProcessHeap(), 0, session->serverName);
3663 session->serverName = heap_strdupW(hostName);
3664 session->serverPort = urlComponents.nPort;
3666 NETCON_close(&request->netConnection);
3667 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS)
3670 res = NETCON_init(&request->netConnection, request->hdr.dwFlags & INTERNET_FLAG_SECURE);
3671 if (res != ERROR_SUCCESS)
3674 reset_data_stream(request);
3678 TRACE("Redirect through proxy\n");
3681 HeapFree(GetProcessHeap(), 0, request->path);
3688 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3689 if (rc != E_POINTER)
3690 needed = strlenW(path)+1;
3691 request->path = heap_alloc(needed*sizeof(WCHAR));
3692 rc = UrlEscapeW(path, request->path, &needed,
3693 URL_ESCAPE_SPACES_ONLY);
3696 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3697 strcpyW(request->path,path);
3701 /* Remove custom content-type/length headers on redirects. */
3702 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3704 HTTP_DeleteCustomHeader(request, index);
3705 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3707 HTTP_DeleteCustomHeader(request, index);
3709 return ERROR_SUCCESS;
3712 /***********************************************************************
3713 * HTTP_build_req (internal)
3715 * concatenate all the strings in the request together
3717 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3722 for( t = list; *t ; t++ )
3723 len += strlenW( *t );
3726 str = heap_alloc(len*sizeof(WCHAR));
3729 for( t = list; *t ; t++ )
3735 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3738 LPWSTR requestString;
3744 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3745 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3746 http_session_t *session = request->session;
3750 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3751 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3752 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3753 HeapFree( GetProcessHeap(), 0, lpszPath );
3755 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3756 NULL, 0, NULL, NULL );
3757 len--; /* the nul terminator isn't needed */
3758 ascii_req = heap_alloc(len);
3759 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3760 ascii_req, len, NULL, NULL );
3761 HeapFree( GetProcessHeap(), 0, requestString );
3763 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3765 res = NETCON_send( &request->netConnection, ascii_req, len, 0, &cnt );
3766 HeapFree( GetProcessHeap(), 0, ascii_req );
3767 if (res != ERROR_SUCCESS)
3770 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3772 return ERROR_HTTP_INVALID_HEADER;
3774 return ERROR_SUCCESS;
3777 static void HTTP_InsertCookies(http_request_t *request)
3779 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3780 LPWSTR lpszCookies, lpszUrl = NULL;
3781 DWORD nCookieSize, size;
3782 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
3784 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(request->path)) * sizeof(WCHAR);
3785 if (!(lpszUrl = heap_alloc(size))) return;
3786 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, request->path);
3788 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3791 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3793 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3794 if ((lpszCookies = heap_alloc(size)))
3796 cnt += sprintfW(lpszCookies, szCookie);
3797 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3798 strcatW(lpszCookies, szCrLf);
3800 HTTP_HttpAddRequestHeadersW(request, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3801 HeapFree(GetProcessHeap(), 0, lpszCookies);
3804 HeapFree(GetProcessHeap(), 0, lpszUrl);
3807 static WORD HTTP_ParseDay(LPCWSTR day)
3809 static const WCHAR days[7][4] = {{ 's','u','n',0 },
3817 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
3818 if (!strcmpiW(day, days[i]))
3825 static WORD HTTP_ParseMonth(LPCWSTR month)
3827 static const WCHAR jan[] = { 'j','a','n',0 };
3828 static const WCHAR feb[] = { 'f','e','b',0 };
3829 static const WCHAR mar[] = { 'm','a','r',0 };
3830 static const WCHAR apr[] = { 'a','p','r',0 };
3831 static const WCHAR may[] = { 'm','a','y',0 };
3832 static const WCHAR jun[] = { 'j','u','n',0 };
3833 static const WCHAR jul[] = { 'j','u','l',0 };
3834 static const WCHAR aug[] = { 'a','u','g',0 };
3835 static const WCHAR sep[] = { 's','e','p',0 };
3836 static const WCHAR oct[] = { 'o','c','t',0 };
3837 static const WCHAR nov[] = { 'n','o','v',0 };
3838 static const WCHAR dec[] = { 'd','e','c',0 };
3840 if (!strcmpiW(month, jan)) return 1;
3841 if (!strcmpiW(month, feb)) return 2;
3842 if (!strcmpiW(month, mar)) return 3;
3843 if (!strcmpiW(month, apr)) return 4;
3844 if (!strcmpiW(month, may)) return 5;
3845 if (!strcmpiW(month, jun)) return 6;
3846 if (!strcmpiW(month, jul)) return 7;
3847 if (!strcmpiW(month, aug)) return 8;
3848 if (!strcmpiW(month, sep)) return 9;
3849 if (!strcmpiW(month, oct)) return 10;
3850 if (!strcmpiW(month, nov)) return 11;
3851 if (!strcmpiW(month, dec)) return 12;
3856 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
3857 * optionally preceded by whitespace.
3858 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
3859 * st, and sets *str to the first character after the time format.
3861 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
3867 while (isspaceW(*ptr))
3870 num = strtoulW(ptr, &nextPtr, 10);
3871 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3873 ERR("unexpected time format %s\n", debugstr_w(ptr));
3878 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
3882 st->wHour = (WORD)num;
3883 num = strtoulW(ptr, &nextPtr, 10);
3884 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3886 ERR("unexpected time format %s\n", debugstr_w(ptr));
3891 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
3895 st->wMinute = (WORD)num;
3896 num = strtoulW(ptr, &nextPtr, 10);
3897 if (!nextPtr || nextPtr <= ptr)
3899 ERR("unexpected time format %s\n", debugstr_w(ptr));
3904 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
3909 st->wSecond = (WORD)num;
3913 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
3915 static const WCHAR gmt[]= { 'G','M','T',0 };
3916 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
3918 SYSTEMTIME st = { 0 };
3921 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
3922 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
3925 st.wDayOfWeek = HTTP_ParseDay(day);
3926 if (st.wDayOfWeek >= 7)
3928 ERR("unexpected weekday %s\n", debugstr_w(day));
3932 while (isspaceW(*ptr))
3935 for (monthPtr = month; !isspace(*ptr) &&
3936 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
3940 st.wMonth = HTTP_ParseMonth(month);
3941 if (!st.wMonth || st.wMonth > 12)
3943 ERR("unexpected month %s\n", debugstr_w(month));
3947 while (isspaceW(*ptr))
3950 num = strtoulW(ptr, &nextPtr, 10);
3951 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
3953 ERR("unexpected day %s\n", debugstr_w(ptr));
3957 st.wDay = (WORD)num;
3959 while (isspaceW(*ptr))
3962 if (!HTTP_ParseTime(&st, &ptr))
3965 while (isspaceW(*ptr))
3968 num = strtoulW(ptr, &nextPtr, 10);
3969 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
3971 ERR("unexpected year %s\n", debugstr_w(ptr));
3975 st.wYear = (WORD)num;
3977 while (isspaceW(*ptr))
3980 /* asctime() doesn't report a timezone, but some web servers do, so accept
3981 * with or without GMT.
3983 if (*ptr && strcmpW(ptr, gmt))
3985 ERR("unexpected timezone %s\n", debugstr_w(ptr));
3988 return SystemTimeToFileTime(&st, ft);
3991 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
3993 static const WCHAR gmt[]= { 'G','M','T',0 };
3994 WCHAR *nextPtr, day[4], month[4], *monthPtr;
3997 SYSTEMTIME st = { 0 };
3999 ptr = strchrW(value, ',');
4002 if (ptr - value != 3)
4004 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4007 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4009 st.wDayOfWeek = HTTP_ParseDay(day);
4010 if (st.wDayOfWeek > 6)
4012 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4017 while (isspaceW(*ptr))
4020 num = strtoulW(ptr, &nextPtr, 10);
4021 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4023 ERR("unexpected day %s\n", debugstr_w(value));
4027 st.wDay = (WORD)num;
4029 while (isspaceW(*ptr))
4032 for (monthPtr = month; !isspace(*ptr) &&
4033 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4037 st.wMonth = HTTP_ParseMonth(month);
4038 if (!st.wMonth || st.wMonth > 12)
4040 ERR("unexpected month %s\n", debugstr_w(month));
4044 while (isspaceW(*ptr))
4047 num = strtoulW(ptr, &nextPtr, 10);
4048 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4050 ERR("unexpected year %s\n", debugstr_w(value));
4054 st.wYear = (WORD)num;
4056 if (!HTTP_ParseTime(&st, &ptr))
4059 while (isspaceW(*ptr))
4062 if (strcmpW(ptr, gmt))
4064 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4067 return SystemTimeToFileTime(&st, ft);
4070 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4071 * which may not be the only formats actually seen in the wild.
4072 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4073 * should be accepted as well.
4075 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4079 if (strchrW(value, ','))
4080 ret = HTTP_ParseRfc1123Date(value, ft);
4083 ret = HTTP_ParseDateAsAsctime(value, ft);
4085 ERR("unexpected date format %s\n", debugstr_w(value));
4090 static void HTTP_ProcessExpires(http_request_t *request)
4092 BOOL expirationFound = FALSE;
4095 /* Look for a Cache-Control header with a max-age directive, as it takes
4096 * precedence over the Expires header.
4098 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4099 if (headerIndex != -1)
4101 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4104 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4106 LPWSTR comma = strchrW(ptr, ','), end, equal;
4111 end = ptr + strlenW(ptr);
4112 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4116 static const WCHAR max_age[] = {
4117 'm','a','x','-','a','g','e',0 };
4119 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4124 age = strtoulW(equal + 1, &nextPtr, 10);
4125 if (nextPtr > equal + 1)
4129 NtQuerySystemTime( &ft );
4130 /* Age is in seconds, FILETIME resolution is in
4131 * 100 nanosecond intervals.
4133 ft.QuadPart += age * (ULONGLONG)1000000;
4134 request->expires.dwLowDateTime = ft.u.LowPart;
4135 request->expires.dwHighDateTime = ft.u.HighPart;
4136 expirationFound = TRUE;
4143 while (isspaceW(*ptr))
4150 if (!expirationFound)
4152 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4153 if (headerIndex != -1)
4155 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4158 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4160 expirationFound = TRUE;
4161 request->expires = ft;
4165 if (!expirationFound)
4169 /* With no known age, default to 10 minutes until expiration. */
4170 NtQuerySystemTime( &t );
4171 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4172 request->expires.dwLowDateTime = t.u.LowPart;
4173 request->expires.dwHighDateTime = t.u.HighPart;
4177 static void HTTP_ProcessLastModified(http_request_t *request)
4181 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4182 if (headerIndex != -1)
4184 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4187 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4188 request->last_modified = ft;
4192 static void HTTP_CacheRequest(http_request_t *request)
4194 WCHAR url[INTERNET_MAX_URL_LENGTH];
4195 WCHAR cacheFileName[MAX_PATH+1];
4198 b = HTTP_GetRequestURL(request, url);
4200 WARN("Could not get URL\n");
4204 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4206 HeapFree(GetProcessHeap(), 0, request->cacheFile);
4207 CloseHandle(request->hCacheFile);
4209 request->cacheFile = heap_strdupW(cacheFileName);
4210 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4211 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4212 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4213 WARN("Could not create file: %u\n", GetLastError());
4214 request->hCacheFile = NULL;
4217 WARN("Could not create cache entry: %08x\n", GetLastError());
4221 /***********************************************************************
4222 * HTTP_HttpSendRequestW (internal)
4224 * Sends the specified request to the HTTP server
4227 * ERROR_SUCCESS on success
4228 * win32 error code on failure
4231 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4232 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4233 DWORD dwContentLength, BOOL bEndRequest)
4236 BOOL redirected = FALSE;
4237 LPWSTR requestString = NULL;
4240 INTERNET_ASYNC_RESULT iar;
4241 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4242 static const WCHAR szContentLength[] =
4243 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4244 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4247 TRACE("--> %p\n", request);
4249 assert(request->hdr.htype == WH_HHTTPREQ);
4251 /* if the verb is NULL default to GET */
4253 request->verb = heap_strdupW(szGET);
4255 if (dwContentLength || strcmpW(request->verb, szGET))
4257 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4258 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4259 request->bytesToWrite = dwContentLength;
4261 if (request->session->appInfo->agent)
4263 WCHAR *agent_header;
4264 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4267 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4268 agent_header = heap_alloc(len * sizeof(WCHAR));
4269 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4271 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4272 HeapFree(GetProcessHeap(), 0, agent_header);
4274 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4276 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4277 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4279 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4281 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4282 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4283 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4289 BOOL reusing_connection;
4294 /* like native, just in case the caller forgot to call InternetReadFile
4295 * for all the data */
4296 HTTP_DrainContent(request);
4298 request->contentLength = ~0u;
4299 request->bytesToWrite = 0;
4302 if (TRACE_ON(wininet))
4304 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4305 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4308 HTTP_FixURL(request);
4309 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4311 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4313 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4314 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4316 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4317 HTTP_InsertCookies(request);
4319 /* add the headers the caller supplied */
4320 if( lpszHeaders && dwHeaderLength )
4322 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4323 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4326 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4328 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4329 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4330 HeapFree(GetProcessHeap(), 0, url);
4333 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4336 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4338 /* Send the request and store the results */
4339 if(NETCON_connected(&request->netConnection))
4340 reusing_connection = TRUE;
4342 reusing_connection = FALSE;
4344 if ((res = HTTP_OpenConnection(request)) != ERROR_SUCCESS)
4347 /* send the request as ASCII, tack on the optional data */
4348 if (!lpOptional || redirected)
4349 dwOptionalLength = 0;
4350 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4351 NULL, 0, NULL, NULL );
4352 ascii_req = heap_alloc(len + dwOptionalLength);
4353 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4354 ascii_req, len, NULL, NULL );
4356 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4357 len = (len + dwOptionalLength - 1);
4359 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4361 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4362 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4364 res = NETCON_send(&request->netConnection, ascii_req, len, 0, &cnt);
4365 HeapFree( GetProcessHeap(), 0, ascii_req );
4367 request->bytesWritten = dwOptionalLength;
4369 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4370 INTERNET_STATUS_REQUEST_SENT,
4371 &len, sizeof(DWORD));
4378 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4379 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4381 if (res != ERROR_SUCCESS)
4384 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4385 /* FIXME: We should know that connection is closed before sending
4386 * headers. Otherwise wrong callbacks are executed */
4387 if(!responseLen && reusing_connection) {
4388 TRACE("Connection closed by server, reconnecting\n");
4393 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4394 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4397 HTTP_ProcessCookies(request);
4398 HTTP_ProcessExpires(request);
4399 HTTP_ProcessLastModified(request);
4401 res = set_content_length(request);
4402 if(res != ERROR_SUCCESS)
4404 if(!request->contentLength)
4405 HTTP_FinishedReading(request);
4407 dwBufferSize = sizeof(dwStatusCode);
4408 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4409 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4412 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4414 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4415 dwBufferSize=sizeof(szNewLocation);
4416 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4417 dwStatusCode == HTTP_STATUS_MOVED ||
4418 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4419 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4420 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4422 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4424 HeapFree(GetProcessHeap(), 0, request->verb);
4425 request->verb = heap_strdupW(szGET);
4427 HTTP_DrainContent(request);
4428 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4430 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4431 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4432 res = HTTP_HandleRedirect(request, new_url);
4433 if (res == ERROR_SUCCESS)
4435 HeapFree(GetProcessHeap(), 0, requestString);
4438 HeapFree( GetProcessHeap(), 0, new_url );
4443 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4445 WCHAR szAuthValue[2048];
4447 if (dwStatusCode == HTTP_STATUS_DENIED)
4449 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4451 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4453 if (HTTP_DoAuthorization(request, szAuthValue,
4455 request->session->userName,
4456 request->session->password,
4459 HeapFree(GetProcessHeap(), 0, requestString);
4466 TRACE("Cleaning wrong authorization data\n");
4467 destroy_authinfo(request->authInfo);
4468 request->authInfo = NULL;
4471 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4474 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4476 if (HTTP_DoAuthorization(request, szAuthValue,
4477 &request->proxyAuthInfo,
4478 request->session->appInfo->proxyUsername,
4479 request->session->appInfo->proxyPassword,
4488 TRACE("Cleaning wrong proxy authorization data\n");
4489 destroy_authinfo(request->proxyAuthInfo);
4490 request->proxyAuthInfo = NULL;
4496 res = ERROR_SUCCESS;
4500 if(res == ERROR_SUCCESS)
4501 HTTP_CacheRequest(request);
4505 HeapFree(GetProcessHeap(), 0, requestString);
4507 /* TODO: send notification for P3P header */
4509 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4511 if (res == ERROR_SUCCESS && request->bytesWritten == request->bytesToWrite)
4512 HTTP_ReceiveRequestData(request, TRUE);
4515 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4518 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4519 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4520 sizeof(INTERNET_ASYNC_RESULT));
4528 /***********************************************************************
4530 * Helper functions for the HttpSendRequest(Ex) functions
4533 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4535 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4536 http_request_t *request = (http_request_t*) workRequest->hdr;
4538 TRACE("%p\n", request);
4540 HTTP_HttpSendRequestW(request, req->lpszHeader,
4541 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4542 req->dwContentLength, req->bEndRequest);
4544 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
4548 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4552 DWORD res = ERROR_SUCCESS;
4554 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4555 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4557 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4559 res = ERROR_HTTP_HEADER_NOT_FOUND;
4561 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4562 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4564 /* process cookies here. Is this right? */
4565 HTTP_ProcessCookies(request);
4566 HTTP_ProcessExpires(request);
4567 HTTP_ProcessLastModified(request);
4569 if ((res = set_content_length( request )) == ERROR_SUCCESS) {
4570 if(!request->contentLength)
4571 HTTP_FinishedReading(request);
4574 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4576 DWORD dwCode,dwCodeLength = sizeof(DWORD);
4577 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
4578 && (dwCode == HTTP_STATUS_REDIRECT ||
4579 dwCode == HTTP_STATUS_MOVED ||
4580 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
4581 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB))
4583 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4584 dwBufferSize=sizeof(szNewLocation);
4585 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4587 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4589 HeapFree(GetProcessHeap(), 0, request->verb);
4590 request->verb = heap_strdupW(szGET);
4592 HTTP_DrainContent(request);
4593 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4595 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4596 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4597 res = HTTP_HandleRedirect(request, new_url);
4598 if (res == ERROR_SUCCESS)
4599 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4600 HeapFree( GetProcessHeap(), 0, new_url );
4606 if (res == ERROR_SUCCESS) {
4607 HTTP_ReceiveRequestData(request, TRUE);
4609 INTERNET_ASYNC_RESULT iar = {0, res};
4611 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4612 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4613 sizeof(INTERNET_ASYNC_RESULT));
4619 /***********************************************************************
4620 * HttpEndRequestA (WININET.@)
4622 * Ends an HTTP request that was started by HttpSendRequestEx
4625 * TRUE if successful
4629 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4630 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4632 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4636 SetLastError(ERROR_INVALID_PARAMETER);
4640 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4643 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4645 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4646 http_request_t *request = (http_request_t*)work->hdr;
4648 TRACE("%p\n", request);
4650 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4653 /***********************************************************************
4654 * HttpEndRequestW (WININET.@)
4656 * Ends an HTTP request that was started by HttpSendRequestEx
4659 * TRUE if successful
4663 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4664 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4666 http_request_t *request;
4673 SetLastError(ERROR_INVALID_PARAMETER);
4677 request = (http_request_t*) get_handle_object( hRequest );
4679 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4681 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4683 WININET_Release( &request->hdr );
4686 request->hdr.dwFlags |= dwFlags;
4688 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4691 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
4693 work.asyncproc = AsyncHttpEndRequestProc;
4694 work.hdr = WININET_AddRef( &request->hdr );
4696 work_endrequest = &work.u.HttpEndRequestW;
4697 work_endrequest->dwFlags = dwFlags;
4698 work_endrequest->dwContext = dwContext;
4700 INTERNET_AsyncCall(&work);
4701 res = ERROR_IO_PENDING;
4704 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
4706 WININET_Release( &request->hdr );
4707 TRACE("%u <--\n", res);
4708 if(res != ERROR_SUCCESS)
4710 return res == ERROR_SUCCESS;
4713 /***********************************************************************
4714 * HttpSendRequestExA (WININET.@)
4716 * Sends the specified request to the HTTP server and allows chunked
4721 * Failure: FALSE, call GetLastError() for more information.
4723 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4724 LPINTERNET_BUFFERSA lpBuffersIn,
4725 LPINTERNET_BUFFERSA lpBuffersOut,
4726 DWORD dwFlags, DWORD_PTR dwContext)
4728 INTERNET_BUFFERSW BuffersInW;
4731 LPWSTR header = NULL;
4733 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4734 lpBuffersOut, dwFlags, dwContext);
4738 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4739 if (lpBuffersIn->lpcszHeader)
4741 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4742 lpBuffersIn->dwHeadersLength,0,0);
4743 header = heap_alloc(headerlen*sizeof(WCHAR));
4744 if (!(BuffersInW.lpcszHeader = header))
4746 SetLastError(ERROR_OUTOFMEMORY);
4749 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4750 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4754 BuffersInW.lpcszHeader = NULL;
4755 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4756 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4757 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4758 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4759 BuffersInW.Next = NULL;
4762 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4764 HeapFree(GetProcessHeap(),0,header);
4769 /***********************************************************************
4770 * HttpSendRequestExW (WININET.@)
4772 * Sends the specified request to the HTTP server and allows chunked
4777 * Failure: FALSE, call GetLastError() for more information.
4779 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4780 LPINTERNET_BUFFERSW lpBuffersIn,
4781 LPINTERNET_BUFFERSW lpBuffersOut,
4782 DWORD dwFlags, DWORD_PTR dwContext)
4784 http_request_t *request;
4785 http_session_t *session;
4789 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4790 lpBuffersOut, dwFlags, dwContext);
4792 request = (http_request_t*) get_handle_object( hRequest );
4794 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4796 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4800 session = request->session;
4801 assert(session->hdr.htype == WH_HHTTPSESSION);
4802 hIC = session->appInfo;
4803 assert(hIC->hdr.htype == WH_HINIT);
4805 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4807 WORKREQUEST workRequest;
4808 struct WORKREQ_HTTPSENDREQUESTW *req;
4810 workRequest.asyncproc = AsyncHttpSendRequestProc;
4811 workRequest.hdr = WININET_AddRef( &request->hdr );
4812 req = &workRequest.u.HttpSendRequestW;
4817 if (lpBuffersIn->lpcszHeader)
4819 if (lpBuffersIn->dwHeadersLength == ~0u)
4820 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4822 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4824 req->lpszHeader = heap_alloc(size);
4825 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4827 else req->lpszHeader = NULL;
4829 req->dwHeaderLength = size / sizeof(WCHAR);
4830 req->lpOptional = lpBuffersIn->lpvBuffer;
4831 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4832 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4836 req->lpszHeader = NULL;
4837 req->dwHeaderLength = 0;
4838 req->lpOptional = NULL;
4839 req->dwOptionalLength = 0;
4840 req->dwContentLength = 0;
4843 req->bEndRequest = FALSE;
4845 INTERNET_AsyncCall(&workRequest);
4847 * This is from windows.
4849 res = ERROR_IO_PENDING;
4854 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4855 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4856 lpBuffersIn->dwBufferTotal, FALSE);
4858 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
4863 WININET_Release( &request->hdr );
4867 return res == ERROR_SUCCESS;
4870 /***********************************************************************
4871 * HttpSendRequestW (WININET.@)
4873 * Sends the specified request to the HTTP server
4880 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4881 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4883 http_request_t *request;
4884 http_session_t *session = NULL;
4885 appinfo_t *hIC = NULL;
4886 DWORD res = ERROR_SUCCESS;
4888 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4889 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4891 request = (http_request_t*) get_handle_object( hHttpRequest );
4892 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4894 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4898 session = request->session;
4899 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
4901 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4905 hIC = session->appInfo;
4906 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4908 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4912 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4914 WORKREQUEST workRequest;
4915 struct WORKREQ_HTTPSENDREQUESTW *req;
4917 workRequest.asyncproc = AsyncHttpSendRequestProc;
4918 workRequest.hdr = WININET_AddRef( &request->hdr );
4919 req = &workRequest.u.HttpSendRequestW;
4924 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4925 else size = dwHeaderLength * sizeof(WCHAR);
4927 req->lpszHeader = heap_alloc(size);
4928 memcpy(req->lpszHeader, lpszHeaders, size);
4931 req->lpszHeader = 0;
4932 req->dwHeaderLength = dwHeaderLength;
4933 req->lpOptional = lpOptional;
4934 req->dwOptionalLength = dwOptionalLength;
4935 req->dwContentLength = dwOptionalLength;
4936 req->bEndRequest = TRUE;
4938 INTERNET_AsyncCall(&workRequest);
4940 * This is from windows.
4942 res = ERROR_IO_PENDING;
4946 res = HTTP_HttpSendRequestW(request, lpszHeaders,
4947 dwHeaderLength, lpOptional, dwOptionalLength,
4948 dwOptionalLength, TRUE);
4952 WININET_Release( &request->hdr );
4955 return res == ERROR_SUCCESS;
4958 /***********************************************************************
4959 * HttpSendRequestA (WININET.@)
4961 * Sends the specified request to the HTTP server
4968 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4969 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4972 LPWSTR szHeaders=NULL;
4973 DWORD nLen=dwHeaderLength;
4974 if(lpszHeaders!=NULL)
4976 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4977 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
4978 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4980 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4981 HeapFree(GetProcessHeap(),0,szHeaders);
4985 /***********************************************************************
4986 * HTTPSESSION_Destroy (internal)
4988 * Deallocate session handle
4991 static void HTTPSESSION_Destroy(object_header_t *hdr)
4993 http_session_t *session = (http_session_t*) hdr;
4995 TRACE("%p\n", session);
4997 WININET_Release(&session->appInfo->hdr);
4999 HeapFree(GetProcessHeap(), 0, session->hostName);
5000 HeapFree(GetProcessHeap(), 0, session->serverName);
5001 HeapFree(GetProcessHeap(), 0, session->password);
5002 HeapFree(GetProcessHeap(), 0, session->userName);
5005 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5008 case INTERNET_OPTION_HANDLE_TYPE:
5009 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5011 if (*size < sizeof(ULONG))
5012 return ERROR_INSUFFICIENT_BUFFER;
5014 *size = sizeof(DWORD);
5015 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5016 return ERROR_SUCCESS;
5019 return INET_QueryOption(hdr, option, buffer, size, unicode);
5022 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5024 http_session_t *ses = (http_session_t*)hdr;
5027 case INTERNET_OPTION_USERNAME:
5029 HeapFree(GetProcessHeap(), 0, ses->userName);
5030 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5031 return ERROR_SUCCESS;
5033 case INTERNET_OPTION_PASSWORD:
5035 HeapFree(GetProcessHeap(), 0, ses->password);
5036 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5037 return ERROR_SUCCESS;
5042 return ERROR_INTERNET_INVALID_OPTION;
5045 static const object_vtbl_t HTTPSESSIONVtbl = {
5046 HTTPSESSION_Destroy,
5048 HTTPSESSION_QueryOption,
5049 HTTPSESSION_SetOption,
5058 /***********************************************************************
5059 * HTTP_Connect (internal)
5061 * Create http session handle
5064 * HINTERNET a session handle on success
5068 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5069 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5070 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5071 DWORD dwInternalFlags, HINTERNET *ret)
5073 http_session_t *session = NULL;
5077 if (!lpszServerName || !lpszServerName[0])
5078 return ERROR_INVALID_PARAMETER;
5080 assert( hIC->hdr.htype == WH_HINIT );
5082 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5084 return ERROR_OUTOFMEMORY;
5087 * According to my tests. The name is not resolved until a request is sent
5090 session->hdr.htype = WH_HHTTPSESSION;
5091 session->hdr.dwFlags = dwFlags;
5092 session->hdr.dwContext = dwContext;
5093 session->hdr.dwInternalFlags |= dwInternalFlags;
5095 WININET_AddRef( &hIC->hdr );
5096 session->appInfo = hIC;
5097 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5099 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5100 if(hIC->proxyBypass)
5101 FIXME("Proxy bypass is ignored.\n");
5103 session->serverName = heap_strdupW(lpszServerName);
5104 session->hostName = heap_strdupW(lpszServerName);
5105 if (lpszUserName && lpszUserName[0])
5106 session->userName = heap_strdupW(lpszUserName);
5107 if (lpszPassword && lpszPassword[0])
5108 session->password = heap_strdupW(lpszPassword);
5109 session->serverPort = serverPort;
5110 session->hostPort = serverPort;
5112 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5113 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5115 INTERNET_SendCallback(&hIC->hdr, dwContext,
5116 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5121 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5125 TRACE("%p --> %p\n", hIC, session);
5127 *ret = session->hdr.hInternet;
5128 return ERROR_SUCCESS;
5132 /***********************************************************************
5133 * HTTP_OpenConnection (internal)
5135 * Connect to a web server
5142 static DWORD HTTP_OpenConnection(http_request_t *request)
5144 http_session_t *session;
5145 appinfo_t *hIC = NULL;
5146 char szaddr[INET6_ADDRSTRLEN];
5148 DWORD res = ERROR_SUCCESS;
5153 if (request->hdr.htype != WH_HHTTPREQ)
5155 res = ERROR_INVALID_PARAMETER;
5159 if (NETCON_connected(&request->netConnection))
5161 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS) goto lend;
5163 session = request->session;
5165 hIC = session->appInfo;
5166 switch (session->socketAddress.ss_family)
5169 addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
5172 addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
5175 WARN("unsupported family %d\n", session->socketAddress.ss_family);
5176 return ERROR_INTERNET_NAME_NOT_RESOLVED;
5178 inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
5179 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5180 INTERNET_STATUS_CONNECTING_TO_SERVER,
5184 res = NETCON_create(&request->netConnection, session->socketAddress.ss_family, SOCK_STREAM, 0);
5185 if (res != ERROR_SUCCESS)
5187 WARN("Socket creation failed: %u\n", res);
5191 res = NETCON_connect(&request->netConnection, (struct sockaddr *)&session->socketAddress,
5193 if(res != ERROR_SUCCESS)
5196 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5197 INTERNET_STATUS_CONNECTED_TO_SERVER,
5198 szaddr, strlen(szaddr)+1);
5200 if (request->hdr.dwFlags & INTERNET_FLAG_SECURE)
5202 /* Note: we differ from Microsoft's WinINet here. they seem to have
5203 * a bug that causes no status callbacks to be sent when starting
5204 * a tunnel to a proxy server using the CONNECT verb. i believe our
5205 * behaviour to be more correct and to not cause any incompatibilities
5206 * because using a secure connection through a proxy server is a rare
5207 * case that would be hard for anyone to depend on */
5208 if (hIC->proxy && (res = HTTP_SecureProxyConnect(request)) != ERROR_SUCCESS) {
5209 HTTPREQ_CloseConnection(&request->hdr);
5213 res = NETCON_secure_connect(&request->netConnection, session->hostName);
5214 if(res != ERROR_SUCCESS)
5216 WARN("Couldn't connect securely to host\n");
5218 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
5219 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
5220 || res == ERROR_INTERNET_INVALID_CA
5221 || res == ERROR_INTERNET_SEC_CERT_NO_REV
5222 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
5223 || res == ERROR_INTERNET_SEC_CERT_REVOKED
5224 || res == ERROR_INTERNET_SEC_INVALID_CERT
5225 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
5226 res = ERROR_INTERNET_SEC_CERT_ERRORS;
5228 HTTPREQ_CloseConnection(&request->hdr);
5235 reset_data_stream(request);
5237 TRACE("%d <--\n", res);
5242 /***********************************************************************
5243 * HTTP_clear_response_headers (internal)
5245 * clear out any old response headers
5247 static void HTTP_clear_response_headers( http_request_t *request )
5251 for( i=0; i<request->nCustHeaders; i++)
5253 if( !request->custHeaders[i].lpszField )
5255 if( !request->custHeaders[i].lpszValue )
5257 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5259 HTTP_DeleteCustomHeader( request, i );
5264 /***********************************************************************
5265 * HTTP_GetResponseHeaders (internal)
5267 * Read server response
5274 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5277 WCHAR buffer[MAX_REPLY_LEN];
5278 DWORD buflen = MAX_REPLY_LEN;
5279 BOOL bSuccess = FALSE;
5281 char bufferA[MAX_REPLY_LEN];
5282 LPWSTR status_code = NULL, status_text = NULL;
5283 DWORD cchMaxRawHeaders = 1024;
5284 LPWSTR lpszRawHeaders = NULL;
5286 DWORD cchRawHeaders = 0;
5287 BOOL codeHundred = FALSE;
5291 if (!NETCON_connected(&request->netConnection))
5295 static const WCHAR szHundred[] = {'1','0','0',0};
5297 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5299 buflen = MAX_REPLY_LEN;
5300 if (!read_line(request, bufferA, &buflen))
5303 /* clear old response headers (eg. from a redirect response) */
5305 HTTP_clear_response_headers( request );
5310 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5311 /* check is this a status code line? */
5312 if (!strncmpW(buffer, g_szHttp1_0, 4))
5314 /* split the version from the status code */
5315 status_code = strchrW( buffer, ' ' );
5320 /* split the status code from the status text */
5321 status_text = strchrW( status_code, ' ' );
5326 TRACE("version [%s] status code [%s] status text [%s]\n",
5327 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5329 codeHundred = (!strcmpW(status_code, szHundred));
5331 else if (!codeHundred)
5333 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5335 HeapFree(GetProcessHeap(), 0, request->version);
5336 HeapFree(GetProcessHeap(), 0, request->statusText);
5338 request->version = heap_strdupW(g_szHttp1_0);
5339 request->statusText = heap_strdupW(szOK);
5341 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5342 request->rawHeaders = heap_strdupW(szDefaultHeader);
5347 } while (codeHundred);
5349 /* Add status code */
5350 HTTP_ProcessHeader(request, szStatus, status_code,
5351 HTTP_ADDHDR_FLAG_REPLACE);
5353 HeapFree(GetProcessHeap(),0,request->version);
5354 HeapFree(GetProcessHeap(),0,request->statusText);
5356 request->version = heap_strdupW(buffer);
5357 request->statusText = heap_strdupW(status_text);
5359 /* Restore the spaces */
5360 *(status_code-1) = ' ';
5361 *(status_text-1) = ' ';
5363 /* regenerate raw headers */
5364 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5365 if (!lpszRawHeaders) goto lend;
5367 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5368 cchMaxRawHeaders *= 2;
5369 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5370 if (temp == NULL) goto lend;
5371 lpszRawHeaders = temp;
5372 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5373 cchRawHeaders += (buflen-1);
5374 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5375 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5376 lpszRawHeaders[cchRawHeaders] = '\0';
5378 /* Parse each response line */
5381 buflen = MAX_REPLY_LEN;
5382 if (read_line(request, bufferA, &buflen))
5384 LPWSTR * pFieldAndValue;
5386 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5388 if (!bufferA[0]) break;
5389 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5391 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5394 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5395 cchMaxRawHeaders *= 2;
5396 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5397 if (temp == NULL) goto lend;
5398 lpszRawHeaders = temp;
5399 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5400 cchRawHeaders += (buflen-1);
5401 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5402 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5403 lpszRawHeaders[cchRawHeaders] = '\0';
5405 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5406 HTTP_ADDREQ_FLAG_ADD );
5408 HTTP_FreeTokens(pFieldAndValue);
5419 /* make sure the response header is terminated with an empty line. Some apps really
5420 truly care about that empty line being there for some reason. Just add it to the
5422 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5424 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5425 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5426 if (temp == NULL) goto lend;
5427 lpszRawHeaders = temp;
5430 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5432 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5433 request->rawHeaders = lpszRawHeaders;
5434 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5444 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
5449 /***********************************************************************
5450 * HTTP_InterpretHttpHeader (internal)
5452 * Parse server response
5456 * Pointer to array of field, value, NULL on success.
5459 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5461 LPWSTR * pTokenPair;
5465 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5467 pszColon = strchrW(buffer, ':');
5468 /* must have two tokens */
5471 HTTP_FreeTokens(pTokenPair);
5473 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5477 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5480 HTTP_FreeTokens(pTokenPair);
5483 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5484 pTokenPair[0][pszColon - buffer] = '\0';
5488 len = strlenW(pszColon);
5489 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5492 HTTP_FreeTokens(pTokenPair);
5495 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5497 strip_spaces(pTokenPair[0]);
5498 strip_spaces(pTokenPair[1]);
5500 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5504 /***********************************************************************
5505 * HTTP_ProcessHeader (internal)
5507 * Stuff header into header tables according to <dwModifier>
5511 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5513 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5515 LPHTTPHEADERW lphttpHdr = NULL;
5517 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5518 DWORD res = ERROR_HTTP_INVALID_HEADER;
5520 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5522 /* REPLACE wins out over ADD */
5523 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5524 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5526 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5529 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5533 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5534 return ERROR_HTTP_INVALID_HEADER;
5535 lphttpHdr = &request->custHeaders[index];
5541 hdr.lpszField = (LPWSTR)field;
5542 hdr.lpszValue = (LPWSTR)value;
5543 hdr.wFlags = hdr.wCount = 0;
5545 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5546 hdr.wFlags |= HDR_ISREQUEST;
5548 return HTTP_InsertCustomHeader(request, &hdr);
5550 /* no value to delete */
5551 else return ERROR_SUCCESS;
5553 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5554 lphttpHdr->wFlags |= HDR_ISREQUEST;
5556 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5558 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5560 HTTP_DeleteCustomHeader( request, index );
5566 hdr.lpszField = (LPWSTR)field;
5567 hdr.lpszValue = (LPWSTR)value;
5568 hdr.wFlags = hdr.wCount = 0;
5570 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5571 hdr.wFlags |= HDR_ISREQUEST;
5573 return HTTP_InsertCustomHeader(request, &hdr);
5576 return ERROR_SUCCESS;
5578 else if (dwModifier & COALESCEFLAGS)
5583 INT origlen = strlenW(lphttpHdr->lpszValue);
5584 INT valuelen = strlenW(value);
5586 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5589 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5591 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5594 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5597 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5599 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5602 lphttpHdr->lpszValue = lpsztmp;
5603 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5606 lphttpHdr->lpszValue[origlen] = ch;
5608 lphttpHdr->lpszValue[origlen] = ' ';
5612 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5613 lphttpHdr->lpszValue[len] = '\0';
5614 res = ERROR_SUCCESS;
5618 WARN("heap_realloc (%d bytes) failed\n",len+1);
5619 res = ERROR_OUTOFMEMORY;
5622 TRACE("<-- %d\n", res);
5627 /***********************************************************************
5628 * HTTP_FinishedReading (internal)
5630 * Called when all content from server has been read by client.
5633 static BOOL HTTP_FinishedReading(http_request_t *request)
5635 BOOL keepalive = HTTP_KeepAlive(request);
5642 HTTPREQ_CloseConnection(&request->hdr);
5645 /* FIXME: store data in the URL cache here */
5651 /***********************************************************************
5652 * HTTP_GetCustomHeaderIndex (internal)
5654 * Return index of custom header from header array
5657 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5658 int requested_index, BOOL request_only)
5662 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5664 for (index = 0; index < request->nCustHeaders; index++)
5666 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5669 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5672 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5675 if (requested_index == 0)
5680 if (index >= request->nCustHeaders)
5683 TRACE("Return: %d\n", index);
5688 /***********************************************************************
5689 * HTTP_InsertCustomHeader (internal)
5691 * Insert header into array
5694 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5697 LPHTTPHEADERW lph = NULL;
5699 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5700 count = request->nCustHeaders + 1;
5702 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5704 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5707 return ERROR_OUTOFMEMORY;
5709 request->custHeaders = lph;
5710 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5711 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5712 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5713 request->custHeaders[count-1].wCount= lpHdr->wCount;
5714 request->nCustHeaders++;
5716 return ERROR_SUCCESS;
5720 /***********************************************************************
5721 * HTTP_DeleteCustomHeader (internal)
5723 * Delete header from array
5724 * If this function is called, the indexs may change.
5726 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5728 if( request->nCustHeaders <= 0 )
5730 if( index >= request->nCustHeaders )
5732 request->nCustHeaders--;
5734 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszField);
5735 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszValue);
5737 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5738 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5739 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5745 /***********************************************************************
5746 * HTTP_VerifyValidHeader (internal)
5748 * Verify the given header is not invalid for the given http request
5751 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5753 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5754 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5755 return ERROR_HTTP_INVALID_HEADER;
5757 return ERROR_SUCCESS;
5760 /***********************************************************************
5761 * IsHostInProxyBypassList (@)
5766 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5768 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5772 /***********************************************************************
5773 * InternetShowSecurityInfoByURLA (@)
5775 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5777 FIXME("stub: %s %p\n", url, window);
5781 /***********************************************************************
5782 * InternetShowSecurityInfoByURLW (@)
5784 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5786 FIXME("stub: %s %p\n", debugstr_w(url), window);