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 COLLECT_TIME 60000
160 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
171 unsigned int auth_data_len;
172 BOOL finished; /* finished authenticating */
176 typedef struct _basicAuthorizationData
183 UINT authorizationLen;
184 } basicAuthorizationData;
186 typedef struct _authorizationData
200 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
201 static struct list authorizationCache = LIST_INIT(authorizationCache);
203 static CRITICAL_SECTION authcache_cs;
204 static CRITICAL_SECTION_DEBUG critsect_debug =
207 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
208 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
210 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
212 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
213 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
214 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
215 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
216 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
217 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
218 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
219 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
220 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
221 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
222 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
224 static CRITICAL_SECTION connection_pool_cs;
225 static CRITICAL_SECTION_DEBUG connection_pool_debug =
227 0, 0, &connection_pool_cs,
228 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
229 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
231 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
233 static struct list connection_pool = LIST_INIT(connection_pool);
234 static BOOL collector_running;
236 void server_addref(server_t *server)
238 InterlockedIncrement(&server->ref);
241 void server_release(server_t *server)
243 if(InterlockedDecrement(&server->ref))
247 server->keep_until = GetTickCount64() + COLLECT_TIME;
250 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
252 server_t *iter, *server = NULL;
254 EnterCriticalSection(&connection_pool_cs);
256 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
257 if(iter->port == port && !strcmpW(iter->name, name)) {
259 server_addref(server);
265 server = heap_alloc(sizeof(*server));
267 server->addr_len = 0;
270 list_init(&server->conn_pool);
271 server->name = heap_strdupW(name);
273 list_add_head(&connection_pool, &server->entry);
281 LeaveCriticalSection(&connection_pool_cs);
286 BOOL collect_connections(BOOL collect_all)
288 netconn_t *netconn, *netconn_safe;
289 server_t *server, *server_safe;
290 BOOL remaining = FALSE;
293 now = GetTickCount64();
295 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
296 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
297 if(collect_all || netconn->keep_until < now) {
298 TRACE("freeing %p\n", netconn);
299 list_remove(&netconn->pool_entry);
300 free_netconn(netconn);
307 if(collect_all || server->keep_until < now) {
308 list_remove(&server->entry);
310 heap_free(server->name);
321 static DWORD WINAPI collect_connections_proc(void *arg)
323 BOOL remaining_conns;
326 /* FIXME: Use more sophisticated method */
329 EnterCriticalSection(&connection_pool_cs);
331 remaining_conns = collect_connections(FALSE);
333 collector_running = FALSE;
335 LeaveCriticalSection(&connection_pool_cs);
336 }while(remaining_conns);
338 FreeLibraryAndExitThread(WININET_hModule, 0);
341 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
344 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
345 if (HeaderIndex == -1)
348 return &req->custHeaders[HeaderIndex];
357 struct data_stream_vtbl_t {
358 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
359 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
360 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
361 BOOL (*drain_content)(data_stream_t*,http_request_t*);
362 void (*destroy)(data_stream_t*);
366 data_stream_t data_stream;
368 BYTE buf[READ_BUFFER_SIZE];
374 static inline void destroy_data_stream(data_stream_t *stream)
376 stream->vtbl->destroy(stream);
379 static void reset_data_stream(http_request_t *req)
381 destroy_data_stream(req->data_stream);
382 req->data_stream = &req->netconn_stream.data_stream;
383 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
384 req->read_chunked = req->read_gzip = FALSE;
390 data_stream_t stream;
391 data_stream_t *parent_stream;
393 BYTE buf[READ_BUFFER_SIZE];
399 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
401 /* Allow reading only from read buffer */
405 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
407 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
408 return gzip_stream->end_of_data;
411 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
412 DWORD *read, read_mode_t read_mode)
414 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
415 z_stream *zstream = &gzip_stream->zstream;
416 DWORD current_read, ret_read = 0;
419 DWORD res = ERROR_SUCCESS;
421 while(size && !gzip_stream->end_of_data) {
422 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
424 if(gzip_stream->buf_size <= 64 && !end) {
425 if(gzip_stream->buf_pos) {
426 if(gzip_stream->buf_size)
427 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
428 gzip_stream->buf_pos = 0;
430 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
431 sizeof(gzip_stream->buf)-gzip_stream->buf_size, ¤t_read, read_mode);
432 gzip_stream->buf_size += current_read;
433 if(res != ERROR_SUCCESS)
435 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
436 if(!current_read && !end) {
437 if(read_mode != READMODE_NOBLOCK) {
438 WARN("unexpected end of data\n");
439 gzip_stream->end_of_data = TRUE;
443 if(gzip_stream->buf_size <= 64 && !end)
447 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
448 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
449 zstream->next_out = buf+ret_read;
450 zstream->avail_out = size;
451 zres = inflate(&gzip_stream->zstream, 0);
452 current_read = size - zstream->avail_out;
453 size -= current_read;
454 ret_read += current_read;
455 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
456 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
457 if(zres == Z_STREAM_END) {
458 TRACE("end of data\n");
459 gzip_stream->end_of_data = TRUE;
461 }else if(zres != Z_OK) {
462 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
464 res = ERROR_INTERNET_DECODING_FAILED;
468 if(ret_read && read_mode == READMODE_ASYNC)
469 read_mode = READMODE_NOBLOCK;
472 TRACE("read %u bytes\n", ret_read);
477 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
479 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
480 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
483 static void gzip_destroy(data_stream_t *stream)
485 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
487 destroy_data_stream(gzip_stream->parent_stream);
489 if(!gzip_stream->end_of_data)
490 inflateEnd(&gzip_stream->zstream);
491 heap_free(gzip_stream);
494 static const data_stream_vtbl_t gzip_stream_vtbl = {
502 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
504 return heap_alloc(items*size);
507 static void wininet_zfree(voidpf opaque, voidpf address)
512 static DWORD init_gzip_stream(http_request_t *req)
514 gzip_stream_t *gzip_stream;
517 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
519 return ERROR_OUTOFMEMORY;
521 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
522 gzip_stream->zstream.zalloc = wininet_zalloc;
523 gzip_stream->zstream.zfree = wininet_zfree;
525 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
527 ERR("inflateInit failed: %d\n", zres);
528 heap_free(gzip_stream);
529 return ERROR_OUTOFMEMORY;
532 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
534 HTTP_DeleteCustomHeader(req, index);
537 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
538 gzip_stream->buf_size = req->read_size;
539 req->read_pos = req->read_size = 0;
542 req->read_gzip = TRUE;
543 gzip_stream->parent_stream = req->data_stream;
544 req->data_stream = &gzip_stream->stream;
545 return ERROR_SUCCESS;
550 static DWORD init_gzip_stream(http_request_t *req)
552 ERR("gzip stream not supported, missing zlib.\n");
553 return ERROR_SUCCESS;
558 /***********************************************************************
559 * HTTP_Tokenize (internal)
561 * Tokenize a string, allocating memory for the tokens.
563 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
565 LPWSTR * token_array;
572 /* empty string has no tokens */
576 for (i = 0; string[i]; i++)
578 if (!strncmpW(string+i, token_string, strlenW(token_string)))
582 /* we want to skip over separators, but not the null terminator */
583 for (j = 0; j < strlenW(token_string) - 1; j++)
591 /* add 1 for terminating NULL */
592 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
593 token_array[tokens] = NULL;
596 for (i = 0; i < tokens; i++)
599 next_token = strstrW(string, token_string);
600 if (!next_token) next_token = string+strlenW(string);
601 len = next_token - string;
602 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
603 memcpy(token_array[i], string, len*sizeof(WCHAR));
604 token_array[i][len] = '\0';
605 string = next_token+strlenW(token_string);
610 /***********************************************************************
611 * HTTP_FreeTokens (internal)
613 * Frees memory returned from HTTP_Tokenize.
615 static void HTTP_FreeTokens(LPWSTR * token_array)
618 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
619 heap_free(token_array);
622 static void HTTP_FixURL(http_request_t *request)
624 static const WCHAR szSlash[] = { '/',0 };
625 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
627 /* If we don't have a path we set it to root */
628 if (NULL == request->path)
629 request->path = heap_strdupW(szSlash);
630 else /* remove \r and \n*/
632 int nLen = strlenW(request->path);
633 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
636 request->path[nLen]='\0';
638 /* Replace '\' with '/' */
641 if (request->path[nLen] == '\\') request->path[nLen]='/';
645 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
646 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
647 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
649 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
651 strcpyW(fixurl + 1, request->path);
652 heap_free( request->path );
653 request->path = fixurl;
657 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
659 LPWSTR requestString;
665 static const WCHAR szSpace[] = { ' ',0 };
666 static const WCHAR szColon[] = { ':',' ',0 };
667 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
669 /* allocate space for an array of all the string pointers to be added */
670 len = (request->nCustHeaders)*4 + 10;
671 req = heap_alloc(len*sizeof(LPCWSTR));
673 /* add the verb, path and HTTP version string */
681 /* Append custom request headers */
682 for (i = 0; i < request->nCustHeaders; i++)
684 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
687 req[n++] = request->custHeaders[i].lpszField;
689 req[n++] = request->custHeaders[i].lpszValue;
691 TRACE("Adding custom header %s (%s)\n",
692 debugstr_w(request->custHeaders[i].lpszField),
693 debugstr_w(request->custHeaders[i].lpszValue));
698 ERR("oops. buffer overrun\n");
701 requestString = HTTP_build_req( req, 4 );
705 * Set (header) termination string for request
706 * Make sure there's exactly two new lines at the end of the request
708 p = &requestString[strlenW(requestString)-1];
709 while ( (*p == '\n') || (*p == '\r') )
711 strcpyW( p+1, sztwocrlf );
713 return requestString;
716 static void HTTP_ProcessCookies( http_request_t *request )
720 LPHTTPHEADERW setCookieHeader;
722 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
725 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
731 setCookieHeader = &request->custHeaders[HeaderIndex];
733 if (!setCookieHeader->lpszValue)
736 host = HTTP_GetHeader(request, hostW);
740 data = strchrW(setCookieHeader->lpszValue, '=');
744 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
749 set_cookie(host->lpszValue, request->path, name, data);
754 static void strip_spaces(LPWSTR start)
759 while (*str == ' ' && *str != '\0')
763 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
765 end = start + strlenW(start) - 1;
766 while (end >= start && *end == ' ')
773 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
775 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
776 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
778 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
779 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
780 if (is_basic && pszRealm)
783 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
787 token = strchrW(ptr,'=');
791 while (*realm == ' ' && *realm != '\0')
793 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
794 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
797 while (*token == ' ' && *token != '\0')
801 *pszRealm = heap_strdupW(token);
802 strip_spaces(*pszRealm);
809 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
811 if (!authinfo) return;
813 if (SecIsValidHandle(&authinfo->ctx))
814 DeleteSecurityContext(&authinfo->ctx);
815 if (SecIsValidHandle(&authinfo->cred))
816 FreeCredentialsHandle(&authinfo->cred);
818 heap_free(authinfo->auth_data);
819 heap_free(authinfo->scheme);
823 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
825 basicAuthorizationData *ad;
828 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
830 EnterCriticalSection(&authcache_cs);
831 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
833 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
835 TRACE("Authorization found in cache\n");
836 *auth_data = heap_alloc(ad->authorizationLen);
837 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
838 rc = ad->authorizationLen;
842 LeaveCriticalSection(&authcache_cs);
846 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
849 basicAuthorizationData* ad = NULL;
851 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
853 EnterCriticalSection(&authcache_cs);
854 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
856 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
857 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
866 TRACE("Found match in cache, replacing\n");
867 heap_free(ad->authorization);
868 ad->authorization = heap_alloc(auth_data_len);
869 memcpy(ad->authorization, auth_data, auth_data_len);
870 ad->authorizationLen = auth_data_len;
874 ad = heap_alloc(sizeof(basicAuthorizationData));
875 ad->host = heap_strdupW(host);
876 ad->host = heap_strdupW(realm);
877 ad->authorization = heap_alloc(auth_data_len);
878 memcpy(ad->authorization, auth_data, auth_data_len);
879 ad->authorizationLen = auth_data_len;
880 list_add_head(&basicAuthorizationCache,&ad->entry);
881 TRACE("authorization cached\n");
883 LeaveCriticalSection(&authcache_cs);
886 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
887 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
889 authorizationData *ad;
891 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
893 EnterCriticalSection(&authcache_cs);
894 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
895 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
896 TRACE("Authorization found in cache\n");
898 nt_auth_identity->User = heap_strdupW(ad->user);
899 nt_auth_identity->Password = heap_strdupW(ad->password);
900 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
901 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
902 (!nt_auth_identity->Domain && ad->domain_len)) {
903 heap_free(nt_auth_identity->User);
904 heap_free(nt_auth_identity->Password);
905 heap_free(nt_auth_identity->Domain);
909 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
910 nt_auth_identity->UserLength = ad->user_len;
911 nt_auth_identity->PasswordLength = ad->password_len;
912 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
913 nt_auth_identity->DomainLength = ad->domain_len;
914 LeaveCriticalSection(&authcache_cs);
918 LeaveCriticalSection(&authcache_cs);
923 static void cache_authorization(LPWSTR host, LPWSTR scheme,
924 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
926 authorizationData *ad;
929 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
931 EnterCriticalSection(&authcache_cs);
932 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
933 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
940 heap_free(ad->password);
941 heap_free(ad->domain);
943 ad = heap_alloc(sizeof(authorizationData));
945 LeaveCriticalSection(&authcache_cs);
949 ad->host = heap_strdupW(host);
950 ad->scheme = heap_strdupW(scheme);
951 list_add_head(&authorizationCache, &ad->entry);
954 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
955 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
956 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
957 ad->user_len = nt_auth_identity->UserLength;
958 ad->password_len = nt_auth_identity->PasswordLength;
959 ad->domain_len = nt_auth_identity->DomainLength;
961 if(!ad->host || !ad->scheme || !ad->user || !ad->password
962 || (nt_auth_identity->Domain && !ad->domain)) {
964 heap_free(ad->scheme);
966 heap_free(ad->password);
967 heap_free(ad->domain);
968 list_remove(&ad->entry);
972 LeaveCriticalSection(&authcache_cs);
975 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
976 struct HttpAuthInfo **ppAuthInfo,
977 LPWSTR domain_and_username, LPWSTR password,
980 SECURITY_STATUS sec_status;
981 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
983 LPWSTR szRealm = NULL;
985 TRACE("%s\n", debugstr_w(pszAuthValue));
992 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
996 SecInvalidateHandle(&pAuthInfo->cred);
997 SecInvalidateHandle(&pAuthInfo->ctx);
998 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
1000 pAuthInfo->auth_data = NULL;
1001 pAuthInfo->auth_data_len = 0;
1002 pAuthInfo->finished = FALSE;
1004 if (is_basic_auth_value(pszAuthValue,NULL))
1006 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1007 pAuthInfo->scheme = heap_strdupW(szBasic);
1008 if (!pAuthInfo->scheme)
1010 heap_free(pAuthInfo);
1017 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1019 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1020 if (!pAuthInfo->scheme)
1022 heap_free(pAuthInfo);
1026 if (domain_and_username)
1028 WCHAR *user = strchrW(domain_and_username, '\\');
1029 WCHAR *domain = domain_and_username;
1031 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1033 pAuthData = &nt_auth_identity;
1038 user = domain_and_username;
1042 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1043 nt_auth_identity.User = user;
1044 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1045 nt_auth_identity.Domain = domain;
1046 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1047 nt_auth_identity.Password = password;
1048 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1050 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1052 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1053 pAuthData = &nt_auth_identity;
1055 /* use default credentials */
1058 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1059 SECPKG_CRED_OUTBOUND, NULL,
1061 NULL, &pAuthInfo->cred,
1064 if(pAuthData && !domain_and_username) {
1065 heap_free(nt_auth_identity.User);
1066 heap_free(nt_auth_identity.Domain);
1067 heap_free(nt_auth_identity.Password);
1070 if (sec_status == SEC_E_OK)
1072 PSecPkgInfoW sec_pkg_info;
1073 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1074 if (sec_status == SEC_E_OK)
1076 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1077 FreeContextBuffer(sec_pkg_info);
1080 if (sec_status != SEC_E_OK)
1082 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1083 debugstr_w(pAuthInfo->scheme), sec_status);
1084 heap_free(pAuthInfo->scheme);
1085 heap_free(pAuthInfo);
1089 *ppAuthInfo = pAuthInfo;
1091 else if (pAuthInfo->finished)
1094 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1095 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1097 ERR("authentication scheme changed from %s to %s\n",
1098 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1102 if (is_basic_auth_value(pszAuthValue,&szRealm))
1106 char *auth_data = NULL;
1107 UINT auth_data_len = 0;
1109 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1111 if (!domain_and_username)
1113 if (host && szRealm)
1114 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1115 if (auth_data_len == 0)
1123 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1124 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1126 /* length includes a nul terminator, which will be re-used for the ':' */
1127 auth_data = heap_alloc(userlen + 1 + passlen);
1134 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1135 auth_data[userlen] = ':';
1136 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1137 auth_data_len = userlen + 1 + passlen;
1138 if (host && szRealm)
1139 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1142 pAuthInfo->auth_data = auth_data;
1143 pAuthInfo->auth_data_len = auth_data_len;
1144 pAuthInfo->finished = TRUE;
1150 LPCWSTR pszAuthData;
1151 SecBufferDesc out_desc, in_desc;
1153 unsigned char *buffer;
1154 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1155 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1157 in.BufferType = SECBUFFER_TOKEN;
1161 in_desc.ulVersion = 0;
1162 in_desc.cBuffers = 1;
1163 in_desc.pBuffers = ∈
1165 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1166 if (*pszAuthData == ' ')
1169 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1170 in.pvBuffer = heap_alloc(in.cbBuffer);
1171 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1174 buffer = heap_alloc(pAuthInfo->max_token);
1176 out.BufferType = SECBUFFER_TOKEN;
1177 out.cbBuffer = pAuthInfo->max_token;
1178 out.pvBuffer = buffer;
1180 out_desc.ulVersion = 0;
1181 out_desc.cBuffers = 1;
1182 out_desc.pBuffers = &out;
1184 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1185 first ? NULL : &pAuthInfo->ctx,
1186 first ? request->session->serverName : NULL,
1187 context_req, 0, SECURITY_NETWORK_DREP,
1188 in.pvBuffer ? &in_desc : NULL,
1189 0, &pAuthInfo->ctx, &out_desc,
1190 &pAuthInfo->attr, &pAuthInfo->exp);
1191 if (sec_status == SEC_E_OK)
1193 pAuthInfo->finished = TRUE;
1194 pAuthInfo->auth_data = out.pvBuffer;
1195 pAuthInfo->auth_data_len = out.cbBuffer;
1196 TRACE("sending last auth packet\n");
1198 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1200 pAuthInfo->auth_data = out.pvBuffer;
1201 pAuthInfo->auth_data_len = out.cbBuffer;
1202 TRACE("sending next auth packet\n");
1206 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1207 heap_free(out.pvBuffer);
1208 destroy_authinfo(pAuthInfo);
1217 /***********************************************************************
1218 * HTTP_HttpAddRequestHeadersW (internal)
1220 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1221 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1226 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1228 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1230 if( dwHeaderLength == ~0U )
1231 len = strlenW(lpszHeader);
1233 len = dwHeaderLength;
1234 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1235 lstrcpynW( buffer, lpszHeader, len + 1);
1241 LPWSTR * pFieldAndValue;
1243 lpszEnd = lpszStart;
1245 while (*lpszEnd != '\0')
1247 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1252 if (*lpszStart == '\0')
1255 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1258 lpszEnd++; /* Jump over newline */
1260 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1261 if (*lpszStart == '\0')
1263 /* Skip 0-length headers */
1264 lpszStart = lpszEnd;
1265 res = ERROR_SUCCESS;
1268 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1271 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1272 if (res == ERROR_SUCCESS)
1273 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1274 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1275 HTTP_FreeTokens(pFieldAndValue);
1278 lpszStart = lpszEnd;
1279 } while (res == ERROR_SUCCESS);
1285 /***********************************************************************
1286 * HttpAddRequestHeadersW (WININET.@)
1288 * Adds one or more HTTP header to the request handler
1291 * On Windows if dwHeaderLength includes the trailing '\0', then
1292 * HttpAddRequestHeadersW() adds it too. However this results in an
1293 * invalid Http header which is rejected by some servers so we probably
1294 * don't need to match Windows on that point.
1301 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1302 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1304 http_request_t *request;
1305 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1307 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1312 request = (http_request_t*) get_handle_object( hHttpRequest );
1313 if (request && request->hdr.htype == WH_HHTTPREQ)
1314 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1316 WININET_Release( &request->hdr );
1318 if(res != ERROR_SUCCESS)
1320 return res == ERROR_SUCCESS;
1323 /***********************************************************************
1324 * HttpAddRequestHeadersA (WININET.@)
1326 * Adds one or more HTTP header to the request handler
1333 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1334 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1340 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1342 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1343 hdr = heap_alloc(len*sizeof(WCHAR));
1344 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1345 if( dwHeaderLength != ~0U )
1346 dwHeaderLength = len;
1348 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1354 /***********************************************************************
1355 * HttpOpenRequestA (WININET.@)
1357 * Open a HTTP request handle
1360 * HINTERNET a HTTP request handle on success
1364 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1365 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1366 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1367 DWORD dwFlags, DWORD_PTR dwContext)
1369 LPWSTR szVerb = NULL, szObjectName = NULL;
1370 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1371 INT acceptTypesCount;
1372 HINTERNET rc = FALSE;
1375 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1376 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1377 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1378 dwFlags, dwContext);
1382 szVerb = heap_strdupAtoW(lpszVerb);
1389 szObjectName = heap_strdupAtoW(lpszObjectName);
1390 if ( !szObjectName )
1396 szVersion = heap_strdupAtoW(lpszVersion);
1403 szReferrer = heap_strdupAtoW(lpszReferrer);
1408 if (lpszAcceptTypes)
1410 acceptTypesCount = 0;
1411 types = lpszAcceptTypes;
1416 /* find out how many there are */
1417 if (*types && **types)
1419 TRACE("accept type: %s\n", debugstr_a(*types));
1425 WARN("invalid accept type pointer\n");
1430 szAcceptTypes = heap_alloc(sizeof(WCHAR *) * (acceptTypesCount+1));
1431 if (!szAcceptTypes) goto end;
1433 acceptTypesCount = 0;
1434 types = lpszAcceptTypes;
1439 if (*types && **types)
1440 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1444 /* ignore invalid pointer */
1449 szAcceptTypes[acceptTypesCount] = NULL;
1452 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1453 szVersion, szReferrer,
1454 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1459 acceptTypesCount = 0;
1460 while (szAcceptTypes[acceptTypesCount])
1462 heap_free(szAcceptTypes[acceptTypesCount]);
1465 heap_free(szAcceptTypes);
1467 heap_free(szReferrer);
1468 heap_free(szVersion);
1469 heap_free(szObjectName);
1474 /***********************************************************************
1477 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1480 static const CHAR HTTP_Base64Enc[] =
1481 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1485 /* first 6 bits, all from bin[0] */
1486 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1487 x = (bin[0] & 3) << 4;
1489 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1492 base64[n++] = HTTP_Base64Enc[x];
1497 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1498 x = ( bin[1] & 0x0f ) << 2;
1500 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1503 base64[n++] = HTTP_Base64Enc[x];
1507 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1509 /* last 6 bits, all from bin [2] */
1510 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1518 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1519 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1520 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1521 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1522 static const signed char HTTP_Base64Dec[256] =
1524 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1525 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1526 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1527 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1528 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1529 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1530 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1531 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1532 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1533 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1534 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1535 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1536 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1537 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1538 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1539 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1540 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1541 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1542 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1543 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1544 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1545 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1546 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1547 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1548 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1549 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1553 /***********************************************************************
1556 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1564 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1565 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1566 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1567 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1569 WARN("invalid base64: %s\n", debugstr_w(base64));
1573 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1576 if ((base64[2] == '=') && (base64[3] == '='))
1578 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1579 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1581 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1585 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1588 if (base64[3] == '=')
1590 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1591 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1593 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1597 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1606 /***********************************************************************
1607 * HTTP_InsertAuthorization
1609 * Insert or delete the authorization field in the request header.
1611 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1615 static const WCHAR wszSpace[] = {' ',0};
1616 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1618 WCHAR *authorization = NULL;
1620 if (pAuthInfo->auth_data_len)
1622 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1623 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1624 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1628 strcpyW(authorization, pAuthInfo->scheme);
1629 strcatW(authorization, wszSpace);
1630 HTTP_EncodeBase64(pAuthInfo->auth_data,
1631 pAuthInfo->auth_data_len,
1632 authorization+strlenW(authorization));
1634 /* clear the data as it isn't valid now that it has been sent to the
1635 * server, unless it's Basic authentication which doesn't do
1636 * connection tracking */
1637 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1639 heap_free(pAuthInfo->auth_data);
1640 pAuthInfo->auth_data = NULL;
1641 pAuthInfo->auth_data_len = 0;
1645 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1647 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1648 heap_free(authorization);
1653 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1655 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1658 size = sizeof(new_location);
1659 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1661 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1662 strcpyW( url, new_location );
1666 static const WCHAR slash[] = { '/',0 };
1667 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1668 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1669 http_session_t *session = req->session;
1671 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1672 size += strlenW( session->hostName ) + strlenW( req->path );
1674 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1676 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1677 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1679 sprintfW( url, format, session->hostName, session->hostPort );
1680 if (req->path[0] != '/') strcatW( url, slash );
1681 strcatW( url, req->path );
1683 TRACE("url=%s\n", debugstr_w(url));
1687 /***********************************************************************
1688 * HTTP_DealWithProxy
1690 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1692 WCHAR buf[MAXHOSTNAME];
1693 WCHAR protoProxy[MAXHOSTNAME + 15];
1694 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1695 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1696 static WCHAR szNul[] = { 0 };
1697 URL_COMPONENTSW UrlComponents;
1698 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1699 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1700 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1702 memset( &UrlComponents, 0, sizeof UrlComponents );
1703 UrlComponents.dwStructSize = sizeof UrlComponents;
1704 UrlComponents.lpszHostName = buf;
1705 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1707 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1709 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1710 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1711 sprintfW(proxy, szFormat, protoProxy);
1713 strcpyW(proxy, protoProxy);
1714 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1716 if( UrlComponents.dwHostNameLength == 0 )
1719 if( !request->path )
1720 request->path = szNul;
1722 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1723 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1725 heap_free(session->serverName);
1726 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1727 session->serverPort = UrlComponents.nPort;
1729 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1733 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
1738 if(server->addr_len)
1739 return ERROR_SUCCESS;
1741 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1742 INTERNET_STATUS_RESOLVING_NAME,
1744 (strlenW(server->name)+1) * sizeof(WCHAR));
1746 addr_len = sizeof(server->addr);
1747 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1748 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1750 switch(server->addr.ss_family) {
1752 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1755 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1758 WARN("unsupported family %d\n", server->addr.ss_family);
1759 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1762 server->addr_len = addr_len;
1763 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1764 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1765 INTERNET_STATUS_NAME_RESOLVED,
1766 server->addr_str, strlen(server->addr_str)+1);
1768 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1769 return ERROR_SUCCESS;
1772 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1774 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1775 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1776 static const WCHAR slash[] = { '/',0 };
1777 LPHTTPHEADERW host_header;
1780 host_header = HTTP_GetHeader(req, hostW);
1784 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1788 strcpyW(buf, scheme);
1789 strcatW(buf, host_header->lpszValue);
1790 if (req->path[0] != '/')
1791 strcatW(buf, slash);
1792 strcatW(buf, req->path);
1797 /***********************************************************************
1798 * HTTPREQ_Destroy (internal)
1800 * Deallocate request handle
1803 static void HTTPREQ_Destroy(object_header_t *hdr)
1805 http_request_t *request = (http_request_t*) hdr;
1810 if(request->hCacheFile) {
1811 WCHAR url[INTERNET_MAX_URL_LENGTH];
1813 CloseHandle(request->hCacheFile);
1815 if(HTTP_GetRequestURL(request, url)) {
1818 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1819 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1820 request->last_modified, NORMAL_CACHE_ENTRY,
1821 request->rawHeaders, headersLen, NULL, 0);
1824 heap_free(request->cacheFile);
1826 DeleteCriticalSection( &request->read_section );
1827 WININET_Release(&request->session->hdr);
1829 destroy_authinfo(request->authInfo);
1830 destroy_authinfo(request->proxyAuthInfo);
1832 heap_free(request->path);
1833 heap_free(request->verb);
1834 heap_free(request->rawHeaders);
1835 heap_free(request->version);
1836 heap_free(request->statusText);
1838 for (i = 0; i < request->nCustHeaders; i++)
1840 heap_free(request->custHeaders[i].lpszField);
1841 heap_free(request->custHeaders[i].lpszValue);
1843 destroy_data_stream(request->data_stream);
1844 heap_free(request->custHeaders);
1847 static void http_release_netconn(http_request_t *req, BOOL reuse)
1849 TRACE("%p %p\n",req, req->netconn);
1854 if(reuse && req->netconn->keep_alive) {
1857 EnterCriticalSection(&connection_pool_cs);
1859 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1860 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1861 req->netconn = NULL;
1863 run_collector = !collector_running;
1864 collector_running = TRUE;
1866 LeaveCriticalSection(&connection_pool_cs);
1869 HANDLE thread = NULL;
1872 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1874 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1876 EnterCriticalSection(&connection_pool_cs);
1877 collector_running = FALSE;
1878 LeaveCriticalSection(&connection_pool_cs);
1881 FreeLibrary(module);
1887 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1888 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1890 free_netconn(req->netconn);
1891 req->netconn = NULL;
1893 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1894 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1897 static void drain_content(http_request_t *req)
1901 if (!req->netconn) return;
1903 if (req->contentLength == -1)
1905 else if(!strcmpW(req->verb, szHEAD))
1908 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1910 http_release_netconn(req, try_reuse);
1913 static BOOL HTTP_KeepAlive(http_request_t *request)
1915 WCHAR szVersion[10];
1916 WCHAR szConnectionResponse[20];
1917 DWORD dwBufferSize = sizeof(szVersion);
1918 BOOL keepalive = FALSE;
1920 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1921 * the connection is keep-alive by default */
1922 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1923 && !strcmpiW(szVersion, g_szHttp1_1))
1928 dwBufferSize = sizeof(szConnectionResponse);
1929 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1930 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1932 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1938 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1940 http_request_t *req = (http_request_t*)hdr;
1945 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1947 http_request_t *req = (http_request_t*)hdr;
1950 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1952 http_session_t *session = req->session;
1953 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1955 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1957 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1958 return ERROR_INSUFFICIENT_BUFFER;
1959 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1960 /* FIXME: can't get a SOCKET from our connection since we don't use
1964 /* FIXME: get source port from req->netConnection */
1965 info->SourcePort = 0;
1966 info->DestPort = session->hostPort;
1968 if (HTTP_KeepAlive(req))
1969 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1970 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1971 info->Flags |= IDSI_FLAG_PROXY;
1972 if (req->netconn->useSSL)
1973 info->Flags |= IDSI_FLAG_SECURE;
1975 return ERROR_SUCCESS;
1978 case INTERNET_OPTION_SECURITY_FLAGS:
1982 if (*size < sizeof(ULONG))
1983 return ERROR_INSUFFICIENT_BUFFER;
1985 *size = sizeof(DWORD);
1987 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1988 flags |= SECURITY_FLAG_SECURE;
1989 flags |= req->security_flags;
1991 int bits = NETCON_GetCipherStrength(req->netconn);
1993 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1994 else if (bits >= 56)
1995 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1997 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1999 *(DWORD *)buffer = flags;
2000 return ERROR_SUCCESS;
2003 case INTERNET_OPTION_HANDLE_TYPE:
2004 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2006 if (*size < sizeof(ULONG))
2007 return ERROR_INSUFFICIENT_BUFFER;
2009 *size = sizeof(DWORD);
2010 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2011 return ERROR_SUCCESS;
2013 case INTERNET_OPTION_URL: {
2014 WCHAR url[INTERNET_MAX_URL_LENGTH];
2019 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2021 TRACE("INTERNET_OPTION_URL\n");
2023 host = HTTP_GetHeader(req, hostW);
2024 strcpyW(url, httpW);
2025 strcatW(url, host->lpszValue);
2026 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2028 strcatW(url, req->path);
2030 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2033 len = (strlenW(url)+1) * sizeof(WCHAR);
2035 return ERROR_INSUFFICIENT_BUFFER;
2038 strcpyW(buffer, url);
2039 return ERROR_SUCCESS;
2041 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2043 return ERROR_INSUFFICIENT_BUFFER;
2046 return ERROR_SUCCESS;
2050 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2051 INTERNET_CACHE_ENTRY_INFOW *info;
2052 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2053 WCHAR url[INTERNET_MAX_URL_LENGTH];
2054 DWORD nbytes, error;
2057 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2059 if (*size < sizeof(*ts))
2061 *size = sizeof(*ts);
2062 return ERROR_INSUFFICIENT_BUFFER;
2065 HTTP_GetRequestURL(req, url);
2066 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2067 error = GetLastError();
2068 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2070 if (!(info = heap_alloc(nbytes)))
2071 return ERROR_OUTOFMEMORY;
2073 GetUrlCacheEntryInfoW(url, info, &nbytes);
2075 ts->ftExpires = info->ExpireTime;
2076 ts->ftLastModified = info->LastModifiedTime;
2079 *size = sizeof(*ts);
2080 return ERROR_SUCCESS;
2085 case INTERNET_OPTION_DATAFILE_NAME: {
2088 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2090 if(!req->cacheFile) {
2092 return ERROR_INTERNET_ITEM_NOT_FOUND;
2096 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2097 if(*size < req_size)
2098 return ERROR_INSUFFICIENT_BUFFER;
2101 memcpy(buffer, req->cacheFile, *size);
2102 return ERROR_SUCCESS;
2104 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2105 if (req_size > *size)
2106 return ERROR_INSUFFICIENT_BUFFER;
2108 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2109 -1, buffer, *size, NULL, NULL);
2110 return ERROR_SUCCESS;
2114 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2115 PCCERT_CONTEXT context;
2117 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2118 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2119 return ERROR_INSUFFICIENT_BUFFER;
2122 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2124 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2127 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2128 info->ftExpiry = context->pCertInfo->NotAfter;
2129 info->ftStart = context->pCertInfo->NotBefore;
2130 len = CertNameToStrA(context->dwCertEncodingType,
2131 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2132 info->lpszSubjectInfo = LocalAlloc(0, len);
2133 if(info->lpszSubjectInfo)
2134 CertNameToStrA(context->dwCertEncodingType,
2135 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2136 info->lpszSubjectInfo, len);
2137 len = CertNameToStrA(context->dwCertEncodingType,
2138 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2139 info->lpszIssuerInfo = LocalAlloc(0, len);
2140 if(info->lpszIssuerInfo)
2141 CertNameToStrA(context->dwCertEncodingType,
2142 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2143 info->lpszIssuerInfo, len);
2144 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2145 CertFreeCertificateContext(context);
2146 return ERROR_SUCCESS;
2151 return INET_QueryOption(hdr, option, buffer, size, unicode);
2154 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2156 http_request_t *req = (http_request_t*)hdr;
2159 case INTERNET_OPTION_SECURITY_FLAGS:
2163 if (!buffer || size != sizeof(DWORD))
2164 return ERROR_INVALID_PARAMETER;
2165 flags = *(DWORD *)buffer;
2166 TRACE("%08x\n", flags);
2167 req->security_flags = flags;
2169 req->netconn->security_flags = flags;
2170 return ERROR_SUCCESS;
2172 case INTERNET_OPTION_SEND_TIMEOUT:
2173 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2174 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2176 if (size != sizeof(DWORD))
2177 return ERROR_INVALID_PARAMETER;
2180 FIXME("unsupported without active connection\n");
2181 return ERROR_SUCCESS;
2184 return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
2187 case INTERNET_OPTION_USERNAME:
2188 heap_free(req->session->userName);
2189 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2190 return ERROR_SUCCESS;
2192 case INTERNET_OPTION_PASSWORD:
2193 heap_free(req->session->password);
2194 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2195 return ERROR_SUCCESS;
2196 case INTERNET_OPTION_HTTP_DECODING:
2197 if(size != sizeof(BOOL))
2198 return ERROR_INVALID_PARAMETER;
2199 req->decoding = *(BOOL*)buffer;
2200 return ERROR_SUCCESS;
2203 return ERROR_INTERNET_INVALID_OPTION;
2206 /* read some more data into the read buffer (the read section must be held) */
2207 static DWORD read_more_data( http_request_t *req, int maxlen )
2214 /* move existing data to the start of the buffer */
2216 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2220 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2222 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2223 maxlen - req->read_size, 0, &len );
2224 if(res == ERROR_SUCCESS)
2225 req->read_size += len;
2230 /* remove some amount of data from the read buffer (the read section must be held) */
2231 static void remove_data( http_request_t *req, int count )
2233 if (!(req->read_size -= count)) req->read_pos = 0;
2234 else req->read_pos += count;
2237 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2239 int count, bytes_read, pos = 0;
2242 EnterCriticalSection( &req->read_section );
2245 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2249 count = eol - (req->read_buf + req->read_pos);
2250 bytes_read = count + 1;
2252 else count = bytes_read = req->read_size;
2254 count = min( count, *len - pos );
2255 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2257 remove_data( req, bytes_read );
2260 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2263 TRACE( "returning empty string %u\n", res);
2264 LeaveCriticalSection( &req->read_section );
2265 INTERNET_SetLastError(res);
2269 LeaveCriticalSection( &req->read_section );
2273 if (pos && buffer[pos - 1] == '\r') pos--;
2276 buffer[*len - 1] = 0;
2277 TRACE( "returning %s\n", debugstr_a(buffer));
2281 /* check if we have reached the end of the data to read (the read section must be held) */
2282 static BOOL end_of_read_data( http_request_t *req )
2284 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2287 /* fetch some more data into the read buffer (the read section must be held) */
2288 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2292 if(req->read_size == sizeof(req->read_buf))
2293 return ERROR_SUCCESS;
2297 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2301 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2302 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2303 req->read_size += read;
2305 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2311 /* return the size of data available to be read immediately (the read section must be held) */
2312 static DWORD get_avail_data( http_request_t *req )
2314 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2317 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2319 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2323 NETCON_query_data_available(req->netconn, &avail);
2324 return netconn_stream->content_length == ~0u
2326 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2329 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2331 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2332 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2335 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2336 DWORD *read, read_mode_t read_mode)
2338 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2341 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2343 if(read_mode == READMODE_NOBLOCK)
2344 size = min(size, netconn_get_avail_data(stream, req));
2346 if(size && req->netconn) {
2347 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2351 netconn_stream->content_read += *read = len;
2352 TRACE("read %u bytes\n", len);
2353 return ERROR_SUCCESS;
2356 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2358 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2363 if(netconn_end_of_data(stream, req))
2367 avail = netconn_get_avail_data(stream, req);
2371 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2374 netconn_stream->content_read += len;
2375 }while(netconn_stream->content_read < netconn_stream->content_length);
2380 static void netconn_destroy(data_stream_t *stream)
2384 static const data_stream_vtbl_t netconn_stream_vtbl = {
2385 netconn_get_avail_data,
2386 netconn_end_of_data,
2388 netconn_drain_content,
2392 /* read some more data into the read buffer (the read section must be held) */
2393 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2398 if (stream->buf_pos)
2400 /* move existing data to the start of the buffer */
2401 if(stream->buf_size)
2402 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2403 stream->buf_pos = 0;
2406 if (maxlen == -1) maxlen = sizeof(stream->buf);
2408 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2409 maxlen - stream->buf_size, 0, &len );
2410 if(res == ERROR_SUCCESS)
2411 stream->buf_size += len;
2416 /* remove some amount of data from the read buffer (the read section must be held) */
2417 static void remove_chunked_data(chunked_stream_t *stream, int count)
2419 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2420 else stream->buf_pos += count;
2423 /* discard data contents until we reach end of line (the read section must be held) */
2424 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2430 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2433 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2436 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2437 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2438 } while (stream->buf_size);
2439 return ERROR_SUCCESS;
2442 /* read the size of the next chunk (the read section must be held) */
2443 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2446 DWORD chunk_size = 0, res;
2448 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2453 while (stream->buf_size)
2455 char ch = stream->buf[stream->buf_pos];
2456 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2457 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2458 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2459 else if (ch == ';' || ch == '\r' || ch == '\n')
2461 TRACE( "reading %u byte chunk\n", chunk_size );
2462 stream->chunk_size = chunk_size;
2463 req->contentLength += chunk_size;
2464 return discard_chunked_eol(stream, req);
2466 remove_chunked_data(stream, 1);
2468 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2469 if (!stream->buf_size)
2471 stream->chunk_size = 0;
2472 return ERROR_SUCCESS;
2477 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2479 /* Allow reading only from read buffer */
2483 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2485 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2486 return !chunked_stream->chunk_size;
2489 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2490 DWORD *read, read_mode_t read_mode)
2492 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2493 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2495 if(chunked_stream->chunk_size == ~0u) {
2496 res = start_next_chunk(chunked_stream, req);
2497 if(res != ERROR_SUCCESS)
2501 while(size && chunked_stream->chunk_size) {
2502 if(chunked_stream->buf_size) {
2503 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2505 /* this could block */
2506 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2509 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2510 remove_chunked_data(chunked_stream, read_bytes);
2512 read_bytes = min(size, chunked_stream->chunk_size);
2514 if(read_mode == READMODE_NOBLOCK) {
2517 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2519 if(read_bytes > avail)
2522 /* this could block */
2523 if(read_bytes == chunked_stream->chunk_size)
2527 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2528 if(res != ERROR_SUCCESS)
2532 chunked_stream->chunk_size -= read_bytes;
2534 ret_read += read_bytes;
2535 if(!chunked_stream->chunk_size) {
2536 assert(read_mode != READMODE_NOBLOCK);
2537 res = start_next_chunk(chunked_stream, req);
2538 if(res != ERROR_SUCCESS)
2542 if(read_mode == READMODE_ASYNC)
2543 read_mode = READMODE_NOBLOCK;
2546 TRACE("read %u bytes\n", ret_read);
2551 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2553 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2555 /* FIXME: we can do better */
2556 return !chunked_stream->chunk_size;
2559 static void chunked_destroy(data_stream_t *stream)
2561 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2562 heap_free(chunked_stream);
2565 static const data_stream_vtbl_t chunked_stream_vtbl = {
2566 chunked_get_avail_data,
2567 chunked_end_of_data,
2569 chunked_drain_content,
2573 /* set the request content length based on the headers */
2574 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2576 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2580 if(status_code == HTTP_STATUS_NO_CONTENT) {
2581 request->contentLength = request->netconn_stream.content_length = 0;
2582 return ERROR_SUCCESS;
2585 size = sizeof(request->contentLength);
2586 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2587 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2588 request->contentLength = ~0u;
2589 request->netconn_stream.content_length = request->contentLength;
2590 request->netconn_stream.content_read = request->read_size;
2592 size = sizeof(encoding);
2593 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2594 !strcmpiW(encoding, szChunked))
2596 chunked_stream_t *chunked_stream;
2598 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2600 return ERROR_OUTOFMEMORY;
2602 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2603 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2604 chunked_stream->chunk_size = ~0u;
2606 if(request->read_size) {
2607 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2608 chunked_stream->buf_size = request->read_size;
2609 request->read_size = request->read_pos = 0;
2612 request->data_stream = &chunked_stream->data_stream;
2613 request->contentLength = ~0u;
2614 request->read_chunked = TRUE;
2617 if(request->decoding) {
2620 static const WCHAR gzipW[] = {'g','z','i','p',0};
2622 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2623 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2624 return init_gzip_stream(request);
2627 return ERROR_SUCCESS;
2630 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2632 INTERNET_ASYNC_RESULT iar;
2633 DWORD res, read = 0;
2638 EnterCriticalSection( &req->read_section );
2640 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2641 res = refill_read_buffer(req, mode, &read);
2642 if(res == ERROR_SUCCESS) {
2643 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2644 iar.dwError = first_notif ? 0 : get_avail_data(req);
2650 LeaveCriticalSection( &req->read_section );
2652 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2653 WARN("res %u read %u, closing connection\n", res, read);
2654 http_release_netconn(req, FALSE);
2657 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2658 sizeof(INTERNET_ASYNC_RESULT));
2661 /* read data from the http connection (the read section must be held) */
2662 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2664 DWORD current_read = 0, ret_read = 0;
2665 read_mode_t read_mode;
2666 DWORD res = ERROR_SUCCESS;
2668 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2670 EnterCriticalSection( &req->read_section );
2672 if(req->read_size) {
2673 ret_read = min(size, req->read_size);
2674 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2675 req->read_size -= ret_read;
2676 req->read_pos += ret_read;
2677 if(read_mode == READMODE_ASYNC)
2678 read_mode = READMODE_NOBLOCK;
2681 if(ret_read < size) {
2682 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2683 ret_read += current_read;
2686 LeaveCriticalSection( &req->read_section );
2689 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2691 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2695 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2697 WARN("WriteFile failed: %u\n", GetLastError());
2700 if(size && !ret_read)
2701 http_release_netconn(req, res == ERROR_SUCCESS);
2707 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2709 http_request_t *req = (http_request_t*)hdr;
2712 EnterCriticalSection( &req->read_section );
2713 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2714 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2716 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2717 if(res == ERROR_SUCCESS)
2719 LeaveCriticalSection( &req->read_section );
2724 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2726 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2727 http_request_t *req = (http_request_t*)workRequest->hdr;
2728 INTERNET_ASYNC_RESULT iar;
2731 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2733 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2734 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2736 iar.dwResult = res == ERROR_SUCCESS;
2739 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2740 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2741 sizeof(INTERNET_ASYNC_RESULT));
2744 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2745 DWORD flags, DWORD_PTR context)
2747 http_request_t *req = (http_request_t*)hdr;
2748 DWORD res, size, read, error = ERROR_SUCCESS;
2750 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2751 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2753 if (buffers->dwStructSize != sizeof(*buffers))
2754 return ERROR_INVALID_PARAMETER;
2756 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2758 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2760 WORKREQUEST workRequest;
2762 if (TryEnterCriticalSection( &req->read_section ))
2764 if (get_avail_data(req))
2766 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2767 &buffers->dwBufferLength, FALSE);
2768 size = buffers->dwBufferLength;
2769 LeaveCriticalSection( &req->read_section );
2772 LeaveCriticalSection( &req->read_section );
2775 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2776 workRequest.hdr = WININET_AddRef(&req->hdr);
2777 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2779 INTERNET_AsyncCall(&workRequest);
2781 return ERROR_IO_PENDING;
2785 size = buffers->dwBufferLength;
2787 EnterCriticalSection( &req->read_section );
2788 if(hdr->dwError == ERROR_SUCCESS)
2789 hdr->dwError = INTERNET_HANDLE_IN_USE;
2790 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2791 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2794 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2795 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2796 if(res != ERROR_SUCCESS)
2799 read += buffers->dwBufferLength;
2800 if(read == size || end_of_read_data(req))
2803 LeaveCriticalSection( &req->read_section );
2805 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2806 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2807 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2808 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2810 EnterCriticalSection( &req->read_section );
2813 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2814 hdr->dwError = ERROR_SUCCESS;
2816 error = hdr->dwError;
2818 LeaveCriticalSection( &req->read_section );
2819 size = buffers->dwBufferLength;
2820 buffers->dwBufferLength = read;
2823 if (res == ERROR_SUCCESS) {
2824 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2825 &size, sizeof(size));
2828 return res==ERROR_SUCCESS ? error : res;
2831 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2833 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2834 http_request_t *req = (http_request_t*)workRequest->hdr;
2835 INTERNET_ASYNC_RESULT iar;
2838 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2840 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2841 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2843 iar.dwResult = res == ERROR_SUCCESS;
2846 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2847 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2848 sizeof(INTERNET_ASYNC_RESULT));
2851 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2852 DWORD flags, DWORD_PTR context)
2855 http_request_t *req = (http_request_t*)hdr;
2856 DWORD res, size, read, error = ERROR_SUCCESS;
2858 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2859 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2861 if (buffers->dwStructSize != sizeof(*buffers))
2862 return ERROR_INVALID_PARAMETER;
2864 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2866 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2868 WORKREQUEST workRequest;
2870 if (TryEnterCriticalSection( &req->read_section ))
2872 if (get_avail_data(req))
2874 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2875 &buffers->dwBufferLength, FALSE);
2876 size = buffers->dwBufferLength;
2877 LeaveCriticalSection( &req->read_section );
2880 LeaveCriticalSection( &req->read_section );
2883 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2884 workRequest.hdr = WININET_AddRef(&req->hdr);
2885 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2887 INTERNET_AsyncCall(&workRequest);
2889 return ERROR_IO_PENDING;
2893 size = buffers->dwBufferLength;
2895 EnterCriticalSection( &req->read_section );
2896 if(hdr->dwError == ERROR_SUCCESS)
2897 hdr->dwError = INTERNET_HANDLE_IN_USE;
2898 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2899 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2902 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2903 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2904 if(res != ERROR_SUCCESS)
2907 read += buffers->dwBufferLength;
2908 if(read == size || end_of_read_data(req))
2911 LeaveCriticalSection( &req->read_section );
2913 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2914 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2915 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2916 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2918 EnterCriticalSection( &req->read_section );
2921 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2922 hdr->dwError = ERROR_SUCCESS;
2924 error = hdr->dwError;
2926 LeaveCriticalSection( &req->read_section );
2927 size = buffers->dwBufferLength;
2928 buffers->dwBufferLength = read;
2931 if (res == ERROR_SUCCESS) {
2932 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2933 &size, sizeof(size));
2936 return res==ERROR_SUCCESS ? error : res;
2939 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2942 http_request_t *request = (http_request_t*)hdr;
2944 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2947 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2948 if (res == ERROR_SUCCESS)
2949 request->bytesWritten += *written;
2951 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2955 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2957 http_request_t *req = (http_request_t*)workRequest->hdr;
2959 HTTP_ReceiveRequestData(req, FALSE);
2962 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2964 http_request_t *req = (http_request_t*)hdr;
2966 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2968 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2970 WORKREQUEST workRequest;
2972 /* never wait, if we can't enter the section we queue an async request right away */
2973 if (TryEnterCriticalSection( &req->read_section ))
2975 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2976 if ((*available = get_avail_data( req ))) goto done;
2977 if (end_of_read_data( req )) goto done;
2978 LeaveCriticalSection( &req->read_section );
2981 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2982 workRequest.hdr = WININET_AddRef( &req->hdr );
2984 INTERNET_AsyncCall(&workRequest);
2986 return ERROR_IO_PENDING;
2989 EnterCriticalSection( &req->read_section );
2991 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2993 refill_read_buffer( req, READMODE_ASYNC, NULL );
2994 *available = get_avail_data( req );
2998 LeaveCriticalSection( &req->read_section );
3000 TRACE( "returning %u\n", *available );
3001 return ERROR_SUCCESS;
3004 static const object_vtbl_t HTTPREQVtbl = {
3006 HTTPREQ_CloseConnection,
3007 HTTPREQ_QueryOption,
3010 HTTPREQ_ReadFileExA,
3011 HTTPREQ_ReadFileExW,
3013 HTTPREQ_QueryDataAvailable,
3017 /***********************************************************************
3018 * HTTP_HttpOpenRequestW (internal)
3020 * Open a HTTP request handle
3023 * HINTERNET a HTTP request handle on success
3027 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3028 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3029 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3030 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3032 appinfo_t *hIC = session->appInfo;
3033 http_request_t *request;
3034 DWORD len, res = ERROR_SUCCESS;
3038 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3040 return ERROR_OUTOFMEMORY;
3042 request->hdr.htype = WH_HHTTPREQ;
3043 request->hdr.dwFlags = dwFlags;
3044 request->hdr.dwContext = dwContext;
3045 request->contentLength = ~0u;
3047 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3048 request->data_stream = &request->netconn_stream.data_stream;
3050 InitializeCriticalSection( &request->read_section );
3052 WININET_AddRef( &session->hdr );
3053 request->session = session;
3054 list_add_head( &session->hdr.children, &request->hdr.entry );
3056 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3057 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3058 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3059 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3061 if (lpszObjectName && *lpszObjectName) {
3065 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3066 if (rc != E_POINTER)
3067 len = strlenW(lpszObjectName)+1;
3068 request->path = heap_alloc(len*sizeof(WCHAR));
3069 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3070 URL_ESCAPE_SPACES_ONLY);
3073 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3074 strcpyW(request->path,lpszObjectName);
3077 static const WCHAR slashW[] = {'/',0};
3079 request->path = heap_strdupW(slashW);
3082 if (lpszReferrer && *lpszReferrer)
3083 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3085 if (lpszAcceptTypes)
3088 for (i = 0; lpszAcceptTypes[i]; i++)
3090 if (!*lpszAcceptTypes[i]) continue;
3091 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3092 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3093 HTTP_ADDHDR_FLAG_REQ |
3094 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3098 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3099 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3101 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3102 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3103 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3107 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3109 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3111 res = ERROR_OUTOFMEMORY;
3115 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3116 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3117 heap_free(host_name);
3120 HTTP_ProcessHeader(request, hostW, session->hostName,
3121 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3123 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3124 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3125 INTERNET_DEFAULT_HTTPS_PORT :
3126 INTERNET_DEFAULT_HTTP_PORT);
3128 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3129 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3130 INTERNET_DEFAULT_HTTPS_PORT :
3131 INTERNET_DEFAULT_HTTP_PORT);
3133 if (hIC->proxy && hIC->proxy[0])
3134 HTTP_DealWithProxy( hIC, session, request );
3136 INTERNET_SendCallback(&session->hdr, dwContext,
3137 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3141 TRACE("<-- %u (%p)\n", res, request);
3143 if(res != ERROR_SUCCESS) {
3144 WININET_Release( &request->hdr );
3149 *ret = request->hdr.hInternet;
3150 return ERROR_SUCCESS;
3153 /***********************************************************************
3154 * HttpOpenRequestW (WININET.@)
3156 * Open a HTTP request handle
3159 * HINTERNET a HTTP request handle on success
3163 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3164 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3165 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3166 DWORD dwFlags, DWORD_PTR dwContext)
3168 http_session_t *session;
3169 HINTERNET handle = NULL;
3172 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3173 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3174 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3175 dwFlags, dwContext);
3176 if(lpszAcceptTypes!=NULL)
3179 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3180 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3183 session = (http_session_t*) get_handle_object( hHttpSession );
3184 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3186 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3191 * My tests seem to show that the windows version does not
3192 * become asynchronous until after this point. And anyhow
3193 * if this call was asynchronous then how would you get the
3194 * necessary HINTERNET pointer returned by this function.
3197 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3198 lpszVersion, lpszReferrer, lpszAcceptTypes,
3199 dwFlags, dwContext, &handle);
3202 WININET_Release( &session->hdr );
3203 TRACE("returning %p\n", handle);
3204 if(res != ERROR_SUCCESS)
3209 static const LPCWSTR header_lookup[] = {
3210 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3211 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3212 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3213 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3214 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3215 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3216 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3217 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3218 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3219 szDate, /* HTTP_QUERY_DATE = 9 */
3220 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3221 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3222 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3223 szURI, /* HTTP_QUERY_URI = 13 */
3224 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3225 NULL, /* HTTP_QUERY_COST = 15 */
3226 NULL, /* HTTP_QUERY_LINK = 16 */
3227 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3228 NULL, /* HTTP_QUERY_VERSION = 18 */
3229 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3230 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3231 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3232 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3233 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3234 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3235 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3236 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3237 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3238 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3239 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3240 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3241 NULL, /* HTTP_QUERY_FROM = 31 */
3242 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3243 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3244 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3245 szReferer, /* HTTP_QUERY_REFERER = 35 */
3246 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3247 szServer, /* HTTP_QUERY_SERVER = 37 */
3248 NULL, /* HTTP_TITLE = 38 */
3249 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3250 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3251 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3252 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3253 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3254 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3255 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3256 NULL, /* HTTP_QUERY_REFRESH = 46 */
3257 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3258 szAge, /* HTTP_QUERY_AGE = 48 */
3259 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3260 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3261 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3262 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3263 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3264 szETag, /* HTTP_QUERY_ETAG = 54 */
3265 hostW, /* HTTP_QUERY_HOST = 55 */
3266 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3267 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3268 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3269 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3270 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3271 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3272 szRange, /* HTTP_QUERY_RANGE = 62 */
3273 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3274 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3275 szVary, /* HTTP_QUERY_VARY = 65 */
3276 szVia, /* HTTP_QUERY_VIA = 66 */
3277 szWarning, /* HTTP_QUERY_WARNING = 67 */
3278 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3279 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3280 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3283 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3285 /***********************************************************************
3286 * HTTP_HttpQueryInfoW (internal)
3288 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3289 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3291 LPHTTPHEADERW lphttpHdr = NULL;
3292 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3293 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3294 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3297 /* Find requested header structure */
3300 case HTTP_QUERY_CUSTOM:
3301 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3302 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3304 case HTTP_QUERY_RAW_HEADERS_CRLF:
3308 DWORD res = ERROR_INVALID_PARAMETER;
3311 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3313 headers = request->rawHeaders;
3316 len = strlenW(headers) * sizeof(WCHAR);
3318 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3320 len += sizeof(WCHAR);
3321 res = ERROR_INSUFFICIENT_BUFFER;
3326 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3329 len = strlenW(szCrLf) * sizeof(WCHAR);
3330 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3332 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3333 res = ERROR_SUCCESS;
3335 *lpdwBufferLength = len;
3337 if (request_only) heap_free(headers);
3340 case HTTP_QUERY_RAW_HEADERS:
3342 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3344 LPWSTR pszString = lpBuffer;
3346 for (i = 0; ppszRawHeaderLines[i]; i++)
3347 size += strlenW(ppszRawHeaderLines[i]) + 1;
3349 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3351 HTTP_FreeTokens(ppszRawHeaderLines);
3352 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3353 return ERROR_INSUFFICIENT_BUFFER;
3357 for (i = 0; ppszRawHeaderLines[i]; i++)
3359 DWORD len = strlenW(ppszRawHeaderLines[i]);
3360 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3364 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3366 *lpdwBufferLength = size * sizeof(WCHAR);
3367 HTTP_FreeTokens(ppszRawHeaderLines);
3369 return ERROR_SUCCESS;
3371 case HTTP_QUERY_STATUS_TEXT:
3372 if (request->statusText)
3374 DWORD len = strlenW(request->statusText);
3375 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3377 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3378 return ERROR_INSUFFICIENT_BUFFER;
3382 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3383 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3385 *lpdwBufferLength = len * sizeof(WCHAR);
3386 return ERROR_SUCCESS;
3389 case HTTP_QUERY_VERSION:
3390 if (request->version)
3392 DWORD len = strlenW(request->version);
3393 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3395 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3396 return ERROR_INSUFFICIENT_BUFFER;
3400 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3401 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3403 *lpdwBufferLength = len * sizeof(WCHAR);
3404 return ERROR_SUCCESS;
3407 case HTTP_QUERY_CONTENT_ENCODING:
3408 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3409 requested_index,request_only);
3412 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3414 if (level < LAST_TABLE_HEADER && header_lookup[level])
3415 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3416 requested_index,request_only);
3420 lphttpHdr = &request->custHeaders[index];
3422 /* Ensure header satisfies requested attributes */
3424 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3425 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3427 return ERROR_HTTP_HEADER_NOT_FOUND;
3430 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3432 /* coalesce value to requested type */
3433 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3435 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3436 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3438 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3444 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3446 tmpTM = *gmtime(&tmpTime);
3447 STHook = (SYSTEMTIME *)lpBuffer;
3448 STHook->wDay = tmpTM.tm_mday;
3449 STHook->wHour = tmpTM.tm_hour;
3450 STHook->wMilliseconds = 0;
3451 STHook->wMinute = tmpTM.tm_min;
3452 STHook->wDayOfWeek = tmpTM.tm_wday;
3453 STHook->wMonth = tmpTM.tm_mon + 1;
3454 STHook->wSecond = tmpTM.tm_sec;
3455 STHook->wYear = tmpTM.tm_year;
3457 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3458 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3459 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3461 else if (lphttpHdr->lpszValue)
3463 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3465 if (len > *lpdwBufferLength)
3467 *lpdwBufferLength = len;
3468 return ERROR_INSUFFICIENT_BUFFER;
3472 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3473 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3475 *lpdwBufferLength = len - sizeof(WCHAR);
3477 return ERROR_SUCCESS;
3480 /***********************************************************************
3481 * HttpQueryInfoW (WININET.@)
3483 * Queries for information about an HTTP request
3490 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3491 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3493 http_request_t *request;
3496 if (TRACE_ON(wininet)) {
3497 #define FE(x) { x, #x }
3498 static const wininet_flag_info query_flags[] = {
3499 FE(HTTP_QUERY_MIME_VERSION),
3500 FE(HTTP_QUERY_CONTENT_TYPE),
3501 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3502 FE(HTTP_QUERY_CONTENT_ID),
3503 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3504 FE(HTTP_QUERY_CONTENT_LENGTH),
3505 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3506 FE(HTTP_QUERY_ALLOW),
3507 FE(HTTP_QUERY_PUBLIC),
3508 FE(HTTP_QUERY_DATE),
3509 FE(HTTP_QUERY_EXPIRES),
3510 FE(HTTP_QUERY_LAST_MODIFIED),
3511 FE(HTTP_QUERY_MESSAGE_ID),
3513 FE(HTTP_QUERY_DERIVED_FROM),
3514 FE(HTTP_QUERY_COST),
3515 FE(HTTP_QUERY_LINK),
3516 FE(HTTP_QUERY_PRAGMA),
3517 FE(HTTP_QUERY_VERSION),
3518 FE(HTTP_QUERY_STATUS_CODE),
3519 FE(HTTP_QUERY_STATUS_TEXT),
3520 FE(HTTP_QUERY_RAW_HEADERS),
3521 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3522 FE(HTTP_QUERY_CONNECTION),
3523 FE(HTTP_QUERY_ACCEPT),
3524 FE(HTTP_QUERY_ACCEPT_CHARSET),
3525 FE(HTTP_QUERY_ACCEPT_ENCODING),
3526 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3527 FE(HTTP_QUERY_AUTHORIZATION),
3528 FE(HTTP_QUERY_CONTENT_ENCODING),
3529 FE(HTTP_QUERY_FORWARDED),
3530 FE(HTTP_QUERY_FROM),
3531 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3532 FE(HTTP_QUERY_LOCATION),
3533 FE(HTTP_QUERY_ORIG_URI),
3534 FE(HTTP_QUERY_REFERER),
3535 FE(HTTP_QUERY_RETRY_AFTER),
3536 FE(HTTP_QUERY_SERVER),
3537 FE(HTTP_QUERY_TITLE),
3538 FE(HTTP_QUERY_USER_AGENT),
3539 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3540 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3541 FE(HTTP_QUERY_ACCEPT_RANGES),
3542 FE(HTTP_QUERY_SET_COOKIE),
3543 FE(HTTP_QUERY_COOKIE),
3544 FE(HTTP_QUERY_REQUEST_METHOD),
3545 FE(HTTP_QUERY_REFRESH),
3546 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3548 FE(HTTP_QUERY_CACHE_CONTROL),
3549 FE(HTTP_QUERY_CONTENT_BASE),
3550 FE(HTTP_QUERY_CONTENT_LOCATION),
3551 FE(HTTP_QUERY_CONTENT_MD5),
3552 FE(HTTP_QUERY_CONTENT_RANGE),
3553 FE(HTTP_QUERY_ETAG),
3554 FE(HTTP_QUERY_HOST),
3555 FE(HTTP_QUERY_IF_MATCH),
3556 FE(HTTP_QUERY_IF_NONE_MATCH),
3557 FE(HTTP_QUERY_IF_RANGE),
3558 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3559 FE(HTTP_QUERY_MAX_FORWARDS),
3560 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3561 FE(HTTP_QUERY_RANGE),
3562 FE(HTTP_QUERY_TRANSFER_ENCODING),
3563 FE(HTTP_QUERY_UPGRADE),
3564 FE(HTTP_QUERY_VARY),
3566 FE(HTTP_QUERY_WARNING),
3567 FE(HTTP_QUERY_CUSTOM)
3569 static const wininet_flag_info modifier_flags[] = {
3570 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3571 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3572 FE(HTTP_QUERY_FLAG_NUMBER),
3573 FE(HTTP_QUERY_FLAG_COALESCE)
3576 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3577 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3580 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3581 TRACE(" Attribute:");
3582 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3583 if (query_flags[i].val == info) {
3584 TRACE(" %s", query_flags[i].name);
3588 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3589 TRACE(" Unknown (%08x)", info);
3592 TRACE(" Modifier:");
3593 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3594 if (modifier_flags[i].val & info_mod) {
3595 TRACE(" %s", modifier_flags[i].name);
3596 info_mod &= ~ modifier_flags[i].val;
3601 TRACE(" Unknown (%08x)", info_mod);
3606 request = (http_request_t*) get_handle_object( hHttpRequest );
3607 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3609 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3613 if (lpBuffer == NULL)
3614 *lpdwBufferLength = 0;
3615 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3616 lpBuffer, lpdwBufferLength, lpdwIndex);
3620 WININET_Release( &request->hdr );
3622 TRACE("%u <--\n", res);
3623 if(res != ERROR_SUCCESS)
3625 return res == ERROR_SUCCESS;
3628 /***********************************************************************
3629 * HttpQueryInfoA (WININET.@)
3631 * Queries for information about an HTTP request
3638 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3639 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3645 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3646 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3648 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3649 lpdwBufferLength, lpdwIndex );
3655 len = (*lpdwBufferLength)*sizeof(WCHAR);
3656 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3658 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3664 bufferW = heap_alloc(alloclen);
3665 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3666 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3667 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3674 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3678 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3679 lpBuffer, *lpdwBufferLength, NULL, NULL );
3680 *lpdwBufferLength = len - 1;
3682 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3685 /* since the strings being returned from HttpQueryInfoW should be
3686 * only ASCII characters, it is reasonable to assume that all of
3687 * the Unicode characters can be reduced to a single byte */
3688 *lpdwBufferLength = len / sizeof(WCHAR);
3690 heap_free( bufferW );
3694 /***********************************************************************
3695 * HTTP_GetRedirectURL (internal)
3697 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3699 static WCHAR szHttp[] = {'h','t','t','p',0};
3700 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3701 http_session_t *session = request->session;
3702 URL_COMPONENTSW urlComponents;
3703 DWORD url_length = 0;
3705 LPWSTR combined_url;
3707 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3708 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3709 urlComponents.dwSchemeLength = 0;
3710 urlComponents.lpszHostName = session->hostName;
3711 urlComponents.dwHostNameLength = 0;
3712 urlComponents.nPort = session->hostPort;
3713 urlComponents.lpszUserName = session->userName;
3714 urlComponents.dwUserNameLength = 0;
3715 urlComponents.lpszPassword = NULL;
3716 urlComponents.dwPasswordLength = 0;
3717 urlComponents.lpszUrlPath = request->path;
3718 urlComponents.dwUrlPathLength = 0;
3719 urlComponents.lpszExtraInfo = NULL;
3720 urlComponents.dwExtraInfoLength = 0;
3722 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3723 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3726 orig_url = heap_alloc(url_length);
3728 /* convert from bytes to characters */
3729 url_length = url_length / sizeof(WCHAR) - 1;
3730 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3732 heap_free(orig_url);
3737 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3738 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3740 heap_free(orig_url);
3743 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3745 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3747 heap_free(orig_url);
3748 heap_free(combined_url);
3751 heap_free(orig_url);
3752 return combined_url;
3756 /***********************************************************************
3757 * HTTP_HandleRedirect (internal)
3759 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3761 http_session_t *session = request->session;
3762 appinfo_t *hIC = session->appInfo;
3763 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3764 WCHAR path[INTERNET_MAX_URL_LENGTH];
3769 /* if it's an absolute path, keep the same session info */
3770 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3774 URL_COMPONENTSW urlComponents;
3775 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3776 static WCHAR szHttp[] = {'h','t','t','p',0};
3777 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3783 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3784 urlComponents.lpszScheme = protocol;
3785 urlComponents.dwSchemeLength = 32;
3786 urlComponents.lpszHostName = hostName;
3787 urlComponents.dwHostNameLength = MAXHOSTNAME;
3788 urlComponents.lpszUserName = userName;
3789 urlComponents.dwUserNameLength = 1024;
3790 urlComponents.lpszPassword = NULL;
3791 urlComponents.dwPasswordLength = 0;
3792 urlComponents.lpszUrlPath = path;
3793 urlComponents.dwUrlPathLength = 2048;
3794 urlComponents.lpszExtraInfo = NULL;
3795 urlComponents.dwExtraInfoLength = 0;
3796 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3797 return INTERNET_GetLastError();
3799 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3800 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3802 TRACE("redirect from secure page to non-secure page\n");
3803 /* FIXME: warn about from secure redirect to non-secure page */
3804 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3806 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3807 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3809 TRACE("redirect from non-secure page to secure page\n");
3810 /* FIXME: notify about redirect to secure page */
3811 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3814 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3816 if (lstrlenW(protocol)>4) /*https*/
3817 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3819 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3824 * This upsets redirects to binary files on sourceforge.net
3825 * and gives an html page instead of the target file
3826 * Examination of the HTTP request sent by native wininet.dll
3827 * reveals that it doesn't send a referrer in that case.
3828 * Maybe there's a flag that enables this, or maybe a referrer
3829 * shouldn't be added in case of a redirect.
3832 /* consider the current host as the referrer */
3833 if (session->lpszServerName && *session->lpszServerName)
3834 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3835 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3836 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3839 heap_free(session->hostName);
3840 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3841 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3844 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3845 len = lstrlenW(hostName);
3846 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3847 session->hostName = heap_alloc(len*sizeof(WCHAR));
3848 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3851 session->hostName = heap_strdupW(hostName);
3853 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3855 heap_free(session->userName);
3856 session->userName = NULL;
3858 session->userName = heap_strdupW(userName);
3860 reset_data_stream(request);
3863 if(strcmpiW(session->serverName, hostName)) {
3864 heap_free(session->serverName);
3865 session->serverName = heap_strdupW(hostName);
3867 session->serverPort = urlComponents.nPort;
3870 heap_free(request->path);
3877 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3878 if (rc != E_POINTER)
3879 needed = strlenW(path)+1;
3880 request->path = heap_alloc(needed*sizeof(WCHAR));
3881 rc = UrlEscapeW(path, request->path, &needed,
3882 URL_ESCAPE_SPACES_ONLY);
3885 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3886 strcpyW(request->path,path);
3890 /* Remove custom content-type/length headers on redirects. */
3891 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3893 HTTP_DeleteCustomHeader(request, index);
3894 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3896 HTTP_DeleteCustomHeader(request, index);
3898 return ERROR_SUCCESS;
3901 /***********************************************************************
3902 * HTTP_build_req (internal)
3904 * concatenate all the strings in the request together
3906 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3911 for( t = list; *t ; t++ )
3912 len += strlenW( *t );
3915 str = heap_alloc(len*sizeof(WCHAR));
3918 for( t = list; *t ; t++ )
3924 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3927 LPWSTR requestString;
3933 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3934 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3935 http_session_t *session = request->session;
3939 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3940 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3941 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3942 heap_free( lpszPath );
3944 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3945 NULL, 0, NULL, NULL );
3946 len--; /* the nul terminator isn't needed */
3947 ascii_req = heap_alloc(len);
3948 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3949 heap_free( requestString );
3951 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3953 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3954 heap_free( ascii_req );
3955 if (res != ERROR_SUCCESS)
3958 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3960 return ERROR_HTTP_INVALID_HEADER;
3962 return ERROR_SUCCESS;
3965 static void HTTP_InsertCookies(http_request_t *request)
3967 DWORD cookie_size, size, cnt = 0;
3971 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3973 host = HTTP_GetHeader(request, hostW);
3977 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3980 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
3981 if(!(cookies = heap_alloc(size)))
3984 cnt += sprintfW(cookies, cookieW);
3985 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
3986 strcatW(cookies, szCrLf);
3988 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
3993 static WORD HTTP_ParseDay(LPCWSTR day)
3995 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4003 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4004 if (!strcmpiW(day, days[i]))
4011 static WORD HTTP_ParseMonth(LPCWSTR month)
4013 static const WCHAR jan[] = { 'j','a','n',0 };
4014 static const WCHAR feb[] = { 'f','e','b',0 };
4015 static const WCHAR mar[] = { 'm','a','r',0 };
4016 static const WCHAR apr[] = { 'a','p','r',0 };
4017 static const WCHAR may[] = { 'm','a','y',0 };
4018 static const WCHAR jun[] = { 'j','u','n',0 };
4019 static const WCHAR jul[] = { 'j','u','l',0 };
4020 static const WCHAR aug[] = { 'a','u','g',0 };
4021 static const WCHAR sep[] = { 's','e','p',0 };
4022 static const WCHAR oct[] = { 'o','c','t',0 };
4023 static const WCHAR nov[] = { 'n','o','v',0 };
4024 static const WCHAR dec[] = { 'd','e','c',0 };
4026 if (!strcmpiW(month, jan)) return 1;
4027 if (!strcmpiW(month, feb)) return 2;
4028 if (!strcmpiW(month, mar)) return 3;
4029 if (!strcmpiW(month, apr)) return 4;
4030 if (!strcmpiW(month, may)) return 5;
4031 if (!strcmpiW(month, jun)) return 6;
4032 if (!strcmpiW(month, jul)) return 7;
4033 if (!strcmpiW(month, aug)) return 8;
4034 if (!strcmpiW(month, sep)) return 9;
4035 if (!strcmpiW(month, oct)) return 10;
4036 if (!strcmpiW(month, nov)) return 11;
4037 if (!strcmpiW(month, dec)) return 12;
4042 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4043 * optionally preceded by whitespace.
4044 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4045 * st, and sets *str to the first character after the time format.
4047 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4053 while (isspaceW(*ptr))
4056 num = strtoulW(ptr, &nextPtr, 10);
4057 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4059 ERR("unexpected time format %s\n", debugstr_w(ptr));
4064 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4068 st->wHour = (WORD)num;
4069 num = strtoulW(ptr, &nextPtr, 10);
4070 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4072 ERR("unexpected time format %s\n", debugstr_w(ptr));
4077 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4081 st->wMinute = (WORD)num;
4082 num = strtoulW(ptr, &nextPtr, 10);
4083 if (!nextPtr || nextPtr <= ptr)
4085 ERR("unexpected time format %s\n", debugstr_w(ptr));
4090 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4095 st->wSecond = (WORD)num;
4099 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4101 static const WCHAR gmt[]= { 'G','M','T',0 };
4102 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4104 SYSTEMTIME st = { 0 };
4107 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4108 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4111 st.wDayOfWeek = HTTP_ParseDay(day);
4112 if (st.wDayOfWeek >= 7)
4114 ERR("unexpected weekday %s\n", debugstr_w(day));
4118 while (isspaceW(*ptr))
4121 for (monthPtr = month; !isspace(*ptr) &&
4122 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4126 st.wMonth = HTTP_ParseMonth(month);
4127 if (!st.wMonth || st.wMonth > 12)
4129 ERR("unexpected month %s\n", debugstr_w(month));
4133 while (isspaceW(*ptr))
4136 num = strtoulW(ptr, &nextPtr, 10);
4137 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4139 ERR("unexpected day %s\n", debugstr_w(ptr));
4143 st.wDay = (WORD)num;
4145 while (isspaceW(*ptr))
4148 if (!HTTP_ParseTime(&st, &ptr))
4151 while (isspaceW(*ptr))
4154 num = strtoulW(ptr, &nextPtr, 10);
4155 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4157 ERR("unexpected year %s\n", debugstr_w(ptr));
4161 st.wYear = (WORD)num;
4163 while (isspaceW(*ptr))
4166 /* asctime() doesn't report a timezone, but some web servers do, so accept
4167 * with or without GMT.
4169 if (*ptr && strcmpW(ptr, gmt))
4171 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4174 return SystemTimeToFileTime(&st, ft);
4177 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4179 static const WCHAR gmt[]= { 'G','M','T',0 };
4180 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4183 SYSTEMTIME st = { 0 };
4185 ptr = strchrW(value, ',');
4188 if (ptr - value != 3)
4190 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4193 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4195 st.wDayOfWeek = HTTP_ParseDay(day);
4196 if (st.wDayOfWeek > 6)
4198 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4203 while (isspaceW(*ptr))
4206 num = strtoulW(ptr, &nextPtr, 10);
4207 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4209 ERR("unexpected day %s\n", debugstr_w(value));
4213 st.wDay = (WORD)num;
4215 while (isspaceW(*ptr))
4218 for (monthPtr = month; !isspace(*ptr) &&
4219 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4223 st.wMonth = HTTP_ParseMonth(month);
4224 if (!st.wMonth || st.wMonth > 12)
4226 ERR("unexpected month %s\n", debugstr_w(month));
4230 while (isspaceW(*ptr))
4233 num = strtoulW(ptr, &nextPtr, 10);
4234 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4236 ERR("unexpected year %s\n", debugstr_w(value));
4240 st.wYear = (WORD)num;
4242 if (!HTTP_ParseTime(&st, &ptr))
4245 while (isspaceW(*ptr))
4248 if (strcmpW(ptr, gmt))
4250 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4253 return SystemTimeToFileTime(&st, ft);
4256 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4257 * which may not be the only formats actually seen in the wild.
4258 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4259 * should be accepted as well.
4261 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4263 static const WCHAR zero[] = { '0',0 };
4266 if (!strcmpW(value, zero))
4268 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4271 else if (strchrW(value, ','))
4272 ret = HTTP_ParseRfc1123Date(value, ft);
4275 ret = HTTP_ParseDateAsAsctime(value, ft);
4277 ERR("unexpected date format %s\n", debugstr_w(value));
4282 static void HTTP_ProcessExpires(http_request_t *request)
4284 BOOL expirationFound = FALSE;
4287 /* Look for a Cache-Control header with a max-age directive, as it takes
4288 * precedence over the Expires header.
4290 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4291 if (headerIndex != -1)
4293 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4296 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4298 LPWSTR comma = strchrW(ptr, ','), end, equal;
4303 end = ptr + strlenW(ptr);
4304 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4308 static const WCHAR max_age[] = {
4309 'm','a','x','-','a','g','e',0 };
4311 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4316 age = strtoulW(equal + 1, &nextPtr, 10);
4317 if (nextPtr > equal + 1)
4321 NtQuerySystemTime( &ft );
4322 /* Age is in seconds, FILETIME resolution is in
4323 * 100 nanosecond intervals.
4325 ft.QuadPart += age * (ULONGLONG)1000000;
4326 request->expires.dwLowDateTime = ft.u.LowPart;
4327 request->expires.dwHighDateTime = ft.u.HighPart;
4328 expirationFound = TRUE;
4335 while (isspaceW(*ptr))
4342 if (!expirationFound)
4344 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4345 if (headerIndex != -1)
4347 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4350 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4352 expirationFound = TRUE;
4353 request->expires = ft;
4357 if (!expirationFound)
4361 /* With no known age, default to 10 minutes until expiration. */
4362 NtQuerySystemTime( &t );
4363 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4364 request->expires.dwLowDateTime = t.u.LowPart;
4365 request->expires.dwHighDateTime = t.u.HighPart;
4369 static void HTTP_ProcessLastModified(http_request_t *request)
4373 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4374 if (headerIndex != -1)
4376 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4379 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4380 request->last_modified = ft;
4384 static void http_process_keep_alive(http_request_t *req)
4388 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4390 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4392 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4395 static void HTTP_CacheRequest(http_request_t *request)
4397 WCHAR url[INTERNET_MAX_URL_LENGTH];
4398 WCHAR cacheFileName[MAX_PATH+1];
4401 b = HTTP_GetRequestURL(request, url);
4403 WARN("Could not get URL\n");
4407 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4409 heap_free(request->cacheFile);
4410 CloseHandle(request->hCacheFile);
4412 request->cacheFile = heap_strdupW(cacheFileName);
4413 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4414 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4415 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4416 WARN("Could not create file: %u\n", GetLastError());
4417 request->hCacheFile = NULL;
4420 WARN("Could not create cache entry: %08x\n", GetLastError());
4424 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4426 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4427 http_session_t *session = request->session;
4428 netconn_t *netconn = NULL;
4432 assert(!request->netconn);
4433 reset_data_stream(request);
4435 server = get_server(session->serverName, session->serverPort);
4437 return ERROR_OUTOFMEMORY;
4439 res = HTTP_ResolveName(request, server);
4440 if(res != ERROR_SUCCESS) {
4441 server_release(server);
4445 EnterCriticalSection(&connection_pool_cs);
4447 while(!list_empty(&server->conn_pool)) {
4448 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4449 list_remove(&netconn->pool_entry);
4451 if(NETCON_is_alive(netconn))
4454 TRACE("connection %p closed during idle\n", netconn);
4455 free_netconn(netconn);
4459 LeaveCriticalSection(&connection_pool_cs);
4462 TRACE("<-- reusing %p netconn\n", netconn);
4463 request->netconn = netconn;
4465 return ERROR_SUCCESS;
4468 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4469 INTERNET_STATUS_CONNECTING_TO_SERVER,
4471 strlen(server->addr_str)+1);
4473 res = create_netconn(is_https, server, request->security_flags, &netconn);
4474 server_release(server);
4475 if(res != ERROR_SUCCESS) {
4476 ERR("create_netconn failed: %u\n", res);
4480 request->netconn = netconn;
4482 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4483 INTERNET_STATUS_CONNECTED_TO_SERVER,
4484 server->addr_str, strlen(server->addr_str)+1);
4487 /* Note: we differ from Microsoft's WinINet here. they seem to have
4488 * a bug that causes no status callbacks to be sent when starting
4489 * a tunnel to a proxy server using the CONNECT verb. i believe our
4490 * behaviour to be more correct and to not cause any incompatibilities
4491 * because using a secure connection through a proxy server is a rare
4492 * case that would be hard for anyone to depend on */
4493 if(session->appInfo->proxy)
4494 res = HTTP_SecureProxyConnect(request);
4495 if(res == ERROR_SUCCESS)
4496 res = NETCON_secure_connect(request->netconn, session->hostName);
4497 if(res != ERROR_SUCCESS)
4499 WARN("Couldn't connect securely to host\n");
4501 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4502 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4503 || res == ERROR_INTERNET_INVALID_CA
4504 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4505 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4506 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4507 || res == ERROR_INTERNET_SEC_INVALID_CERT
4508 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4509 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4513 if(res != ERROR_SUCCESS) {
4514 http_release_netconn(request, FALSE);
4519 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4520 return ERROR_SUCCESS;
4523 /***********************************************************************
4524 * HTTP_HttpSendRequestW (internal)
4526 * Sends the specified request to the HTTP server
4529 * ERROR_SUCCESS on success
4530 * win32 error code on failure
4533 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4534 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4535 DWORD dwContentLength, BOOL bEndRequest)
4538 BOOL redirected = FALSE;
4539 LPWSTR requestString = NULL;
4542 INTERNET_ASYNC_RESULT iar;
4543 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4544 static const WCHAR szContentLength[] =
4545 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4546 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4549 TRACE("--> %p\n", request);
4551 assert(request->hdr.htype == WH_HHTTPREQ);
4553 /* if the verb is NULL default to GET */
4555 request->verb = heap_strdupW(szGET);
4557 if (dwContentLength || strcmpW(request->verb, szGET))
4559 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4560 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4561 request->bytesToWrite = dwContentLength;
4563 if (request->session->appInfo->agent)
4565 WCHAR *agent_header;
4566 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4569 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4570 agent_header = heap_alloc(len * sizeof(WCHAR));
4571 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4573 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4574 heap_free(agent_header);
4576 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4578 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4579 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4581 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4583 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4584 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4585 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4591 BOOL reusing_connection;
4596 /* like native, just in case the caller forgot to call InternetReadFile
4597 * for all the data */
4598 drain_content(request);
4600 request->contentLength = ~0u;
4601 request->bytesToWrite = 0;
4604 if (TRACE_ON(wininet))
4606 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4607 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4610 HTTP_FixURL(request);
4611 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4613 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4615 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4616 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4618 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4619 HTTP_InsertCookies(request);
4621 /* add the headers the caller supplied */
4622 if( lpszHeaders && dwHeaderLength )
4624 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4625 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4628 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4630 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4631 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4635 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4638 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4640 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4643 /* send the request as ASCII, tack on the optional data */
4644 if (!lpOptional || redirected)
4645 dwOptionalLength = 0;
4646 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4647 NULL, 0, NULL, NULL );
4648 ascii_req = heap_alloc(len + dwOptionalLength);
4649 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4650 ascii_req, len, NULL, NULL );
4652 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4653 len = (len + dwOptionalLength - 1);
4655 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4657 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4658 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4660 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4661 heap_free( ascii_req );
4662 if(res != ERROR_SUCCESS) {
4663 TRACE("send failed: %u\n", res);
4664 if(!reusing_connection)
4666 http_release_netconn(request, FALSE);
4671 request->bytesWritten = dwOptionalLength;
4673 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4674 INTERNET_STATUS_REQUEST_SENT,
4675 &len, sizeof(DWORD));
4682 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4683 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4685 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4686 /* FIXME: We should know that connection is closed before sending
4687 * headers. Otherwise wrong callbacks are executed */
4688 if(!responseLen && reusing_connection) {
4689 TRACE("Connection closed by server, reconnecting\n");
4690 http_release_netconn(request, FALSE);
4695 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4696 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4699 http_process_keep_alive(request);
4700 HTTP_ProcessCookies(request);
4701 HTTP_ProcessExpires(request);
4702 HTTP_ProcessLastModified(request);
4704 dwBufferSize = sizeof(dwStatusCode);
4705 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4706 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4709 res = set_content_length(request, dwStatusCode);
4710 if(res != ERROR_SUCCESS)
4712 if(!request->contentLength)
4713 http_release_netconn(request, TRUE);
4715 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4717 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4718 dwBufferSize=sizeof(szNewLocation);
4719 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4720 dwStatusCode == HTTP_STATUS_MOVED ||
4721 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4722 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4723 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4725 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4726 dwStatusCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4728 heap_free(request->verb);
4729 request->verb = heap_strdupW(szGET);
4731 drain_content(request);
4732 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4734 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4735 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4736 res = HTTP_HandleRedirect(request, new_url);
4737 if (res == ERROR_SUCCESS)
4739 heap_free(requestString);
4742 heap_free( new_url );
4747 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4749 WCHAR szAuthValue[2048];
4751 if (dwStatusCode == HTTP_STATUS_DENIED)
4753 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4755 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4757 if (HTTP_DoAuthorization(request, szAuthValue,
4759 request->session->userName,
4760 request->session->password,
4763 heap_free(requestString);
4770 TRACE("Cleaning wrong authorization data\n");
4771 destroy_authinfo(request->authInfo);
4772 request->authInfo = NULL;
4775 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4778 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4780 if (HTTP_DoAuthorization(request, szAuthValue,
4781 &request->proxyAuthInfo,
4782 request->session->appInfo->proxyUsername,
4783 request->session->appInfo->proxyPassword,
4792 TRACE("Cleaning wrong proxy authorization data\n");
4793 destroy_authinfo(request->proxyAuthInfo);
4794 request->proxyAuthInfo = NULL;
4800 res = ERROR_SUCCESS;
4804 if(res == ERROR_SUCCESS)
4805 HTTP_CacheRequest(request);
4808 heap_free(requestString);
4810 /* TODO: send notification for P3P header */
4812 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4814 if (res == ERROR_SUCCESS && request->contentLength && request->bytesWritten == request->bytesToWrite)
4815 HTTP_ReceiveRequestData(request, TRUE);
4818 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4821 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4822 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4823 sizeof(INTERNET_ASYNC_RESULT));
4831 /***********************************************************************
4833 * Helper functions for the HttpSendRequest(Ex) functions
4836 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4838 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4839 http_request_t *request = (http_request_t*) workRequest->hdr;
4841 TRACE("%p\n", request);
4843 HTTP_HttpSendRequestW(request, req->lpszHeader,
4844 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4845 req->dwContentLength, req->bEndRequest);
4847 heap_free(req->lpszHeader);
4851 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4854 DWORD dwCode, dwCodeLength;
4856 DWORD res = ERROR_SUCCESS;
4858 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4859 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4861 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4863 res = ERROR_HTTP_HEADER_NOT_FOUND;
4865 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4866 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4868 /* process cookies here. Is this right? */
4869 http_process_keep_alive(request);
4870 HTTP_ProcessCookies(request);
4871 HTTP_ProcessExpires(request);
4872 HTTP_ProcessLastModified(request);
4874 dwCodeLength = sizeof(dwCode);
4875 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4876 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
4879 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
4880 if(!request->contentLength)
4881 http_release_netconn(request, TRUE);
4884 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4886 if (dwCode == HTTP_STATUS_REDIRECT ||
4887 dwCode == HTTP_STATUS_MOVED ||
4888 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
4889 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
4891 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4892 dwBufferSize=sizeof(szNewLocation);
4893 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4895 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4896 dwCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4898 heap_free(request->verb);
4899 request->verb = heap_strdupW(szGET);
4901 drain_content(request);
4902 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4904 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4905 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4906 res = HTTP_HandleRedirect(request, new_url);
4907 if (res == ERROR_SUCCESS)
4908 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4909 heap_free( new_url );
4915 if (res == ERROR_SUCCESS && request->contentLength) {
4916 HTTP_ReceiveRequestData(request, TRUE);
4918 INTERNET_ASYNC_RESULT iar = {0, res};
4920 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4921 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4922 sizeof(INTERNET_ASYNC_RESULT));
4928 /***********************************************************************
4929 * HttpEndRequestA (WININET.@)
4931 * Ends an HTTP request that was started by HttpSendRequestEx
4934 * TRUE if successful
4938 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4939 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4941 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4945 SetLastError(ERROR_INVALID_PARAMETER);
4949 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4952 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4954 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4955 http_request_t *request = (http_request_t*)work->hdr;
4957 TRACE("%p\n", request);
4959 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4962 /***********************************************************************
4963 * HttpEndRequestW (WININET.@)
4965 * Ends an HTTP request that was started by HttpSendRequestEx
4968 * TRUE if successful
4972 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4973 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4975 http_request_t *request;
4982 SetLastError(ERROR_INVALID_PARAMETER);
4986 request = (http_request_t*) get_handle_object( hRequest );
4988 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4990 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4992 WININET_Release( &request->hdr );
4995 request->hdr.dwFlags |= dwFlags;
4997 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5000 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5002 work.asyncproc = AsyncHttpEndRequestProc;
5003 work.hdr = WININET_AddRef( &request->hdr );
5005 work_endrequest = &work.u.HttpEndRequestW;
5006 work_endrequest->dwFlags = dwFlags;
5007 work_endrequest->dwContext = dwContext;
5009 INTERNET_AsyncCall(&work);
5010 res = ERROR_IO_PENDING;
5013 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5015 WININET_Release( &request->hdr );
5016 TRACE("%u <--\n", res);
5017 if(res != ERROR_SUCCESS)
5019 return res == ERROR_SUCCESS;
5022 /***********************************************************************
5023 * HttpSendRequestExA (WININET.@)
5025 * Sends the specified request to the HTTP server and allows chunked
5030 * Failure: FALSE, call GetLastError() for more information.
5032 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5033 LPINTERNET_BUFFERSA lpBuffersIn,
5034 LPINTERNET_BUFFERSA lpBuffersOut,
5035 DWORD dwFlags, DWORD_PTR dwContext)
5037 INTERNET_BUFFERSW BuffersInW;
5040 LPWSTR header = NULL;
5042 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5043 lpBuffersOut, dwFlags, dwContext);
5047 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5048 if (lpBuffersIn->lpcszHeader)
5050 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5051 lpBuffersIn->dwHeadersLength,0,0);
5052 header = heap_alloc(headerlen*sizeof(WCHAR));
5053 if (!(BuffersInW.lpcszHeader = header))
5055 SetLastError(ERROR_OUTOFMEMORY);
5058 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5059 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5063 BuffersInW.lpcszHeader = NULL;
5064 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5065 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5066 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5067 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5068 BuffersInW.Next = NULL;
5071 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5077 /***********************************************************************
5078 * HttpSendRequestExW (WININET.@)
5080 * Sends the specified request to the HTTP server and allows chunked
5085 * Failure: FALSE, call GetLastError() for more information.
5087 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5088 LPINTERNET_BUFFERSW lpBuffersIn,
5089 LPINTERNET_BUFFERSW lpBuffersOut,
5090 DWORD dwFlags, DWORD_PTR dwContext)
5092 http_request_t *request;
5093 http_session_t *session;
5097 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5098 lpBuffersOut, dwFlags, dwContext);
5100 request = (http_request_t*) get_handle_object( hRequest );
5102 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5104 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5108 session = request->session;
5109 assert(session->hdr.htype == WH_HHTTPSESSION);
5110 hIC = session->appInfo;
5111 assert(hIC->hdr.htype == WH_HINIT);
5113 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5115 WORKREQUEST workRequest;
5116 struct WORKREQ_HTTPSENDREQUESTW *req;
5118 workRequest.asyncproc = AsyncHttpSendRequestProc;
5119 workRequest.hdr = WININET_AddRef( &request->hdr );
5120 req = &workRequest.u.HttpSendRequestW;
5125 if (lpBuffersIn->lpcszHeader)
5127 if (lpBuffersIn->dwHeadersLength == ~0u)
5128 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5130 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5132 req->lpszHeader = heap_alloc(size);
5133 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5135 else req->lpszHeader = NULL;
5137 req->dwHeaderLength = size / sizeof(WCHAR);
5138 req->lpOptional = lpBuffersIn->lpvBuffer;
5139 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5140 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5144 req->lpszHeader = NULL;
5145 req->dwHeaderLength = 0;
5146 req->lpOptional = NULL;
5147 req->dwOptionalLength = 0;
5148 req->dwContentLength = 0;
5151 req->bEndRequest = FALSE;
5153 INTERNET_AsyncCall(&workRequest);
5155 * This is from windows.
5157 res = ERROR_IO_PENDING;
5162 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5163 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5164 lpBuffersIn->dwBufferTotal, FALSE);
5166 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5171 WININET_Release( &request->hdr );
5175 return res == ERROR_SUCCESS;
5178 /***********************************************************************
5179 * HttpSendRequestW (WININET.@)
5181 * Sends the specified request to the HTTP server
5188 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5189 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5191 http_request_t *request;
5192 http_session_t *session = NULL;
5193 appinfo_t *hIC = NULL;
5194 DWORD res = ERROR_SUCCESS;
5196 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5197 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5199 request = (http_request_t*) get_handle_object( hHttpRequest );
5200 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5202 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5206 session = request->session;
5207 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5209 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5213 hIC = session->appInfo;
5214 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5216 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5220 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5222 WORKREQUEST workRequest;
5223 struct WORKREQ_HTTPSENDREQUESTW *req;
5225 workRequest.asyncproc = AsyncHttpSendRequestProc;
5226 workRequest.hdr = WININET_AddRef( &request->hdr );
5227 req = &workRequest.u.HttpSendRequestW;
5232 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5233 else size = dwHeaderLength * sizeof(WCHAR);
5235 req->lpszHeader = heap_alloc(size);
5236 memcpy(req->lpszHeader, lpszHeaders, size);
5239 req->lpszHeader = 0;
5240 req->dwHeaderLength = dwHeaderLength;
5241 req->lpOptional = lpOptional;
5242 req->dwOptionalLength = dwOptionalLength;
5243 req->dwContentLength = dwOptionalLength;
5244 req->bEndRequest = TRUE;
5246 INTERNET_AsyncCall(&workRequest);
5248 * This is from windows.
5250 res = ERROR_IO_PENDING;
5254 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5255 dwHeaderLength, lpOptional, dwOptionalLength,
5256 dwOptionalLength, TRUE);
5260 WININET_Release( &request->hdr );
5263 return res == ERROR_SUCCESS;
5266 /***********************************************************************
5267 * HttpSendRequestA (WININET.@)
5269 * Sends the specified request to the HTTP server
5276 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5277 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5280 LPWSTR szHeaders=NULL;
5281 DWORD nLen=dwHeaderLength;
5282 if(lpszHeaders!=NULL)
5284 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5285 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5286 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5288 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5289 heap_free(szHeaders);
5293 /***********************************************************************
5294 * HTTPSESSION_Destroy (internal)
5296 * Deallocate session handle
5299 static void HTTPSESSION_Destroy(object_header_t *hdr)
5301 http_session_t *session = (http_session_t*) hdr;
5303 TRACE("%p\n", session);
5305 WININET_Release(&session->appInfo->hdr);
5307 heap_free(session->hostName);
5308 heap_free(session->serverName);
5309 heap_free(session->password);
5310 heap_free(session->userName);
5313 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5316 case INTERNET_OPTION_HANDLE_TYPE:
5317 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5319 if (*size < sizeof(ULONG))
5320 return ERROR_INSUFFICIENT_BUFFER;
5322 *size = sizeof(DWORD);
5323 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5324 return ERROR_SUCCESS;
5327 return INET_QueryOption(hdr, option, buffer, size, unicode);
5330 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5332 http_session_t *ses = (http_session_t*)hdr;
5335 case INTERNET_OPTION_USERNAME:
5337 heap_free(ses->userName);
5338 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5339 return ERROR_SUCCESS;
5341 case INTERNET_OPTION_PASSWORD:
5343 heap_free(ses->password);
5344 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5345 return ERROR_SUCCESS;
5350 return ERROR_INTERNET_INVALID_OPTION;
5353 static const object_vtbl_t HTTPSESSIONVtbl = {
5354 HTTPSESSION_Destroy,
5356 HTTPSESSION_QueryOption,
5357 HTTPSESSION_SetOption,
5366 /***********************************************************************
5367 * HTTP_Connect (internal)
5369 * Create http session handle
5372 * HINTERNET a session handle on success
5376 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5377 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5378 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5379 DWORD dwInternalFlags, HINTERNET *ret)
5381 http_session_t *session = NULL;
5385 if (!lpszServerName || !lpszServerName[0])
5386 return ERROR_INVALID_PARAMETER;
5388 assert( hIC->hdr.htype == WH_HINIT );
5390 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5392 return ERROR_OUTOFMEMORY;
5395 * According to my tests. The name is not resolved until a request is sent
5398 session->hdr.htype = WH_HHTTPSESSION;
5399 session->hdr.dwFlags = dwFlags;
5400 session->hdr.dwContext = dwContext;
5401 session->hdr.dwInternalFlags |= dwInternalFlags;
5403 WININET_AddRef( &hIC->hdr );
5404 session->appInfo = hIC;
5405 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5407 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5408 if(hIC->proxyBypass)
5409 FIXME("Proxy bypass is ignored.\n");
5411 session->serverName = heap_strdupW(lpszServerName);
5412 session->hostName = heap_strdupW(lpszServerName);
5413 if (lpszUserName && lpszUserName[0])
5414 session->userName = heap_strdupW(lpszUserName);
5415 if (lpszPassword && lpszPassword[0])
5416 session->password = heap_strdupW(lpszPassword);
5417 session->serverPort = serverPort;
5418 session->hostPort = serverPort;
5420 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5421 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5423 INTERNET_SendCallback(&hIC->hdr, dwContext,
5424 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5429 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5433 TRACE("%p --> %p\n", hIC, session);
5435 *ret = session->hdr.hInternet;
5436 return ERROR_SUCCESS;
5439 /***********************************************************************
5440 * HTTP_clear_response_headers (internal)
5442 * clear out any old response headers
5444 static void HTTP_clear_response_headers( http_request_t *request )
5448 for( i=0; i<request->nCustHeaders; i++)
5450 if( !request->custHeaders[i].lpszField )
5452 if( !request->custHeaders[i].lpszValue )
5454 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5456 HTTP_DeleteCustomHeader( request, i );
5461 /***********************************************************************
5462 * HTTP_GetResponseHeaders (internal)
5464 * Read server response
5471 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5474 WCHAR buffer[MAX_REPLY_LEN];
5475 DWORD buflen = MAX_REPLY_LEN;
5476 BOOL bSuccess = FALSE;
5478 char bufferA[MAX_REPLY_LEN];
5479 LPWSTR status_code = NULL, status_text = NULL;
5480 DWORD cchMaxRawHeaders = 1024;
5481 LPWSTR lpszRawHeaders = NULL;
5483 DWORD cchRawHeaders = 0;
5484 BOOL codeHundred = FALSE;
5488 if(!request->netconn)
5492 static const WCHAR szHundred[] = {'1','0','0',0};
5494 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5496 buflen = MAX_REPLY_LEN;
5497 if (!read_line(request, bufferA, &buflen))
5500 /* clear old response headers (eg. from a redirect response) */
5502 HTTP_clear_response_headers( request );
5507 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5508 /* check is this a status code line? */
5509 if (!strncmpW(buffer, g_szHttp1_0, 4))
5511 /* split the version from the status code */
5512 status_code = strchrW( buffer, ' ' );
5517 /* split the status code from the status text */
5518 status_text = strchrW( status_code, ' ' );
5523 TRACE("version [%s] status code [%s] status text [%s]\n",
5524 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5526 codeHundred = (!strcmpW(status_code, szHundred));
5528 else if (!codeHundred)
5530 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5532 heap_free(request->version);
5533 heap_free(request->statusText);
5535 request->version = heap_strdupW(g_szHttp1_0);
5536 request->statusText = heap_strdupW(szOK);
5538 heap_free(request->rawHeaders);
5539 request->rawHeaders = heap_strdupW(szDefaultHeader);
5544 } while (codeHundred);
5546 /* Add status code */
5547 HTTP_ProcessHeader(request, szStatus, status_code,
5548 HTTP_ADDHDR_FLAG_REPLACE);
5550 heap_free(request->version);
5551 heap_free(request->statusText);
5553 request->version = heap_strdupW(buffer);
5554 request->statusText = heap_strdupW(status_text);
5556 /* Restore the spaces */
5557 *(status_code-1) = ' ';
5558 *(status_text-1) = ' ';
5560 /* regenerate raw headers */
5561 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5562 if (!lpszRawHeaders) goto lend;
5564 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5565 cchMaxRawHeaders *= 2;
5566 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5567 if (temp == NULL) goto lend;
5568 lpszRawHeaders = temp;
5569 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5570 cchRawHeaders += (buflen-1);
5571 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5572 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5573 lpszRawHeaders[cchRawHeaders] = '\0';
5575 /* Parse each response line */
5578 buflen = MAX_REPLY_LEN;
5579 if (read_line(request, bufferA, &buflen))
5581 LPWSTR * pFieldAndValue;
5583 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5585 if (!bufferA[0]) break;
5586 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5588 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5591 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5592 cchMaxRawHeaders *= 2;
5593 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5594 if (temp == NULL) goto lend;
5595 lpszRawHeaders = temp;
5596 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5597 cchRawHeaders += (buflen-1);
5598 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5599 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5600 lpszRawHeaders[cchRawHeaders] = '\0';
5602 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5603 HTTP_ADDREQ_FLAG_ADD );
5605 HTTP_FreeTokens(pFieldAndValue);
5616 /* make sure the response header is terminated with an empty line. Some apps really
5617 truly care about that empty line being there for some reason. Just add it to the
5619 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5621 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5622 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5623 if (temp == NULL) goto lend;
5624 lpszRawHeaders = temp;
5627 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5629 heap_free(request->rawHeaders);
5630 request->rawHeaders = lpszRawHeaders;
5631 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5641 heap_free(lpszRawHeaders);
5646 /***********************************************************************
5647 * HTTP_InterpretHttpHeader (internal)
5649 * Parse server response
5653 * Pointer to array of field, value, NULL on success.
5656 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5658 LPWSTR * pTokenPair;
5662 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5664 pszColon = strchrW(buffer, ':');
5665 /* must have two tokens */
5668 HTTP_FreeTokens(pTokenPair);
5670 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5674 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5677 HTTP_FreeTokens(pTokenPair);
5680 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5681 pTokenPair[0][pszColon - buffer] = '\0';
5685 len = strlenW(pszColon);
5686 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5689 HTTP_FreeTokens(pTokenPair);
5692 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5694 strip_spaces(pTokenPair[0]);
5695 strip_spaces(pTokenPair[1]);
5697 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5701 /***********************************************************************
5702 * HTTP_ProcessHeader (internal)
5704 * Stuff header into header tables according to <dwModifier>
5708 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5710 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5712 LPHTTPHEADERW lphttpHdr = NULL;
5714 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5715 DWORD res = ERROR_HTTP_INVALID_HEADER;
5717 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5719 /* REPLACE wins out over ADD */
5720 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5721 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5723 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5726 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5730 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5731 return ERROR_HTTP_INVALID_HEADER;
5732 lphttpHdr = &request->custHeaders[index];
5738 hdr.lpszField = (LPWSTR)field;
5739 hdr.lpszValue = (LPWSTR)value;
5740 hdr.wFlags = hdr.wCount = 0;
5742 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5743 hdr.wFlags |= HDR_ISREQUEST;
5745 return HTTP_InsertCustomHeader(request, &hdr);
5747 /* no value to delete */
5748 else return ERROR_SUCCESS;
5750 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5751 lphttpHdr->wFlags |= HDR_ISREQUEST;
5753 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5755 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5757 HTTP_DeleteCustomHeader( request, index );
5763 hdr.lpszField = (LPWSTR)field;
5764 hdr.lpszValue = (LPWSTR)value;
5765 hdr.wFlags = hdr.wCount = 0;
5767 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5768 hdr.wFlags |= HDR_ISREQUEST;
5770 return HTTP_InsertCustomHeader(request, &hdr);
5773 return ERROR_SUCCESS;
5775 else if (dwModifier & COALESCEFLAGS)
5780 INT origlen = strlenW(lphttpHdr->lpszValue);
5781 INT valuelen = strlenW(value);
5783 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5786 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5788 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5791 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5794 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5796 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5799 lphttpHdr->lpszValue = lpsztmp;
5800 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5803 lphttpHdr->lpszValue[origlen] = ch;
5805 lphttpHdr->lpszValue[origlen] = ' ';
5809 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5810 lphttpHdr->lpszValue[len] = '\0';
5811 res = ERROR_SUCCESS;
5815 WARN("heap_realloc (%d bytes) failed\n",len+1);
5816 res = ERROR_OUTOFMEMORY;
5819 TRACE("<-- %d\n", res);
5823 /***********************************************************************
5824 * HTTP_GetCustomHeaderIndex (internal)
5826 * Return index of custom header from header array
5829 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5830 int requested_index, BOOL request_only)
5834 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5836 for (index = 0; index < request->nCustHeaders; index++)
5838 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5841 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5844 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5847 if (requested_index == 0)
5852 if (index >= request->nCustHeaders)
5855 TRACE("Return: %d\n", index);
5860 /***********************************************************************
5861 * HTTP_InsertCustomHeader (internal)
5863 * Insert header into array
5866 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5869 LPHTTPHEADERW lph = NULL;
5871 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5872 count = request->nCustHeaders + 1;
5874 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5876 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5879 return ERROR_OUTOFMEMORY;
5881 request->custHeaders = lph;
5882 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5883 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5884 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5885 request->custHeaders[count-1].wCount= lpHdr->wCount;
5886 request->nCustHeaders++;
5888 return ERROR_SUCCESS;
5892 /***********************************************************************
5893 * HTTP_DeleteCustomHeader (internal)
5895 * Delete header from array
5896 * If this function is called, the indexs may change.
5898 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5900 if( request->nCustHeaders <= 0 )
5902 if( index >= request->nCustHeaders )
5904 request->nCustHeaders--;
5906 heap_free(request->custHeaders[index].lpszField);
5907 heap_free(request->custHeaders[index].lpszValue);
5909 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5910 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5911 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5917 /***********************************************************************
5918 * HTTP_VerifyValidHeader (internal)
5920 * Verify the given header is not invalid for the given http request
5923 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5925 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5926 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5927 return ERROR_HTTP_INVALID_HEADER;
5929 return ERROR_SUCCESS;
5932 /***********************************************************************
5933 * IsHostInProxyBypassList (@)
5938 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5940 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5944 /***********************************************************************
5945 * InternetShowSecurityInfoByURLA (@)
5947 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5949 FIXME("stub: %s %p\n", url, window);
5953 /***********************************************************************
5954 * InternetShowSecurityInfoByURLW (@)
5956 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5958 FIXME("stub: %s %p\n", debugstr_w(url), window);