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)
509 HeapFree(GetProcessHeap(), 0, 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 HeapFree(GetProcessHeap(), 0, 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++)
619 HeapFree(GetProcessHeap(), 0, token_array[i]);
620 HeapFree(GetProcessHeap(), 0, token_array);
623 static void HTTP_FixURL(http_request_t *request)
625 static const WCHAR szSlash[] = { '/',0 };
626 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
628 /* If we don't have a path we set it to root */
629 if (NULL == request->path)
630 request->path = heap_strdupW(szSlash);
631 else /* remove \r and \n*/
633 int nLen = strlenW(request->path);
634 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
637 request->path[nLen]='\0';
639 /* Replace '\' with '/' */
642 if (request->path[nLen] == '\\') request->path[nLen]='/';
646 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
647 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
648 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
650 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
652 strcpyW(fixurl + 1, request->path);
653 HeapFree( GetProcessHeap(), 0, request->path );
654 request->path = fixurl;
658 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
660 LPWSTR requestString;
666 static const WCHAR szSpace[] = { ' ',0 };
667 static const WCHAR szColon[] = { ':',' ',0 };
668 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
670 /* allocate space for an array of all the string pointers to be added */
671 len = (request->nCustHeaders)*4 + 10;
672 req = heap_alloc(len*sizeof(LPCWSTR));
674 /* add the verb, path and HTTP version string */
682 /* Append custom request headers */
683 for (i = 0; i < request->nCustHeaders; i++)
685 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
688 req[n++] = request->custHeaders[i].lpszField;
690 req[n++] = request->custHeaders[i].lpszValue;
692 TRACE("Adding custom header %s (%s)\n",
693 debugstr_w(request->custHeaders[i].lpszField),
694 debugstr_w(request->custHeaders[i].lpszValue));
699 ERR("oops. buffer overrun\n");
702 requestString = HTTP_build_req( req, 4 );
703 HeapFree( GetProcessHeap(), 0, req );
706 * Set (header) termination string for request
707 * Make sure there's exactly two new lines at the end of the request
709 p = &requestString[strlenW(requestString)-1];
710 while ( (*p == '\n') || (*p == '\r') )
712 strcpyW( p+1, sztwocrlf );
714 return requestString;
717 static void HTTP_ProcessCookies( http_request_t *request )
721 LPHTTPHEADERW setCookieHeader;
723 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
726 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
732 setCookieHeader = &request->custHeaders[HeaderIndex];
734 if (!setCookieHeader->lpszValue)
737 host = HTTP_GetHeader(request, hostW);
741 data = strchrW(setCookieHeader->lpszValue, '=');
745 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
750 set_cookie(host->lpszValue, request->path, name, data);
755 static void strip_spaces(LPWSTR start)
760 while (*str == ' ' && *str != '\0')
764 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
766 end = start + strlenW(start) - 1;
767 while (end >= start && *end == ' ')
774 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
776 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
777 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
779 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
780 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
781 if (is_basic && pszRealm)
784 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
788 token = strchrW(ptr,'=');
792 while (*realm == ' ' && *realm != '\0')
794 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
795 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
798 while (*token == ' ' && *token != '\0')
802 *pszRealm = heap_strdupW(token);
803 strip_spaces(*pszRealm);
810 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
812 if (!authinfo) return;
814 if (SecIsValidHandle(&authinfo->ctx))
815 DeleteSecurityContext(&authinfo->ctx);
816 if (SecIsValidHandle(&authinfo->cred))
817 FreeCredentialsHandle(&authinfo->cred);
819 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
820 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
821 HeapFree(GetProcessHeap(), 0, authinfo);
824 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
826 basicAuthorizationData *ad;
829 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
831 EnterCriticalSection(&authcache_cs);
832 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
834 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
836 TRACE("Authorization found in cache\n");
837 *auth_data = heap_alloc(ad->authorizationLen);
838 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
839 rc = ad->authorizationLen;
843 LeaveCriticalSection(&authcache_cs);
847 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
850 basicAuthorizationData* ad = NULL;
852 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
854 EnterCriticalSection(&authcache_cs);
855 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
857 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
858 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
867 TRACE("Found match in cache, replacing\n");
868 HeapFree(GetProcessHeap(),0,ad->authorization);
869 ad->authorization = heap_alloc(auth_data_len);
870 memcpy(ad->authorization, auth_data, auth_data_len);
871 ad->authorizationLen = auth_data_len;
875 ad = heap_alloc(sizeof(basicAuthorizationData));
876 ad->host = heap_strdupW(host);
877 ad->host = heap_strdupW(realm);
878 ad->authorization = heap_alloc(auth_data_len);
879 memcpy(ad->authorization, auth_data, auth_data_len);
880 ad->authorizationLen = auth_data_len;
881 list_add_head(&basicAuthorizationCache,&ad->entry);
882 TRACE("authorization cached\n");
884 LeaveCriticalSection(&authcache_cs);
887 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
888 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
890 authorizationData *ad;
892 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
894 EnterCriticalSection(&authcache_cs);
895 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
896 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
897 TRACE("Authorization found in cache\n");
899 nt_auth_identity->User = heap_strdupW(ad->user);
900 nt_auth_identity->Password = heap_strdupW(ad->password);
901 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
902 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
903 (!nt_auth_identity->Domain && ad->domain_len)) {
904 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
905 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
906 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
910 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
911 nt_auth_identity->UserLength = ad->user_len;
912 nt_auth_identity->PasswordLength = ad->password_len;
913 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
914 nt_auth_identity->DomainLength = ad->domain_len;
915 LeaveCriticalSection(&authcache_cs);
919 LeaveCriticalSection(&authcache_cs);
924 static void cache_authorization(LPWSTR host, LPWSTR scheme,
925 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
927 authorizationData *ad;
930 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
932 EnterCriticalSection(&authcache_cs);
933 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
934 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
940 HeapFree(GetProcessHeap(), 0, ad->user);
941 HeapFree(GetProcessHeap(), 0, ad->password);
942 HeapFree(GetProcessHeap(), 0, ad->domain);
944 ad = heap_alloc(sizeof(authorizationData));
946 LeaveCriticalSection(&authcache_cs);
950 ad->host = heap_strdupW(host);
951 ad->scheme = heap_strdupW(scheme);
952 list_add_head(&authorizationCache, &ad->entry);
955 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
956 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
957 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
958 ad->user_len = nt_auth_identity->UserLength;
959 ad->password_len = nt_auth_identity->PasswordLength;
960 ad->domain_len = nt_auth_identity->DomainLength;
962 if(!ad->host || !ad->scheme || !ad->user || !ad->password
963 || (nt_auth_identity->Domain && !ad->domain)) {
964 HeapFree(GetProcessHeap(), 0, ad->host);
965 HeapFree(GetProcessHeap(), 0, ad->scheme);
966 HeapFree(GetProcessHeap(), 0, ad->user);
967 HeapFree(GetProcessHeap(), 0, ad->password);
968 HeapFree(GetProcessHeap(), 0, ad->domain);
969 list_remove(&ad->entry);
970 HeapFree(GetProcessHeap(), 0, ad);
973 LeaveCriticalSection(&authcache_cs);
976 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
977 struct HttpAuthInfo **ppAuthInfo,
978 LPWSTR domain_and_username, LPWSTR password,
981 SECURITY_STATUS sec_status;
982 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
984 LPWSTR szRealm = NULL;
986 TRACE("%s\n", debugstr_w(pszAuthValue));
993 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
997 SecInvalidateHandle(&pAuthInfo->cred);
998 SecInvalidateHandle(&pAuthInfo->ctx);
999 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
1000 pAuthInfo->attr = 0;
1001 pAuthInfo->auth_data = NULL;
1002 pAuthInfo->auth_data_len = 0;
1003 pAuthInfo->finished = FALSE;
1005 if (is_basic_auth_value(pszAuthValue,NULL))
1007 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1008 pAuthInfo->scheme = heap_strdupW(szBasic);
1009 if (!pAuthInfo->scheme)
1011 HeapFree(GetProcessHeap(), 0, pAuthInfo);
1018 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1020 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1021 if (!pAuthInfo->scheme)
1023 HeapFree(GetProcessHeap(), 0, pAuthInfo);
1027 if (domain_and_username)
1029 WCHAR *user = strchrW(domain_and_username, '\\');
1030 WCHAR *domain = domain_and_username;
1032 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1034 pAuthData = &nt_auth_identity;
1039 user = domain_and_username;
1043 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1044 nt_auth_identity.User = user;
1045 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1046 nt_auth_identity.Domain = domain;
1047 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1048 nt_auth_identity.Password = password;
1049 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1051 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1053 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1054 pAuthData = &nt_auth_identity;
1056 /* use default credentials */
1059 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1060 SECPKG_CRED_OUTBOUND, NULL,
1062 NULL, &pAuthInfo->cred,
1065 if(pAuthData && !domain_and_username) {
1066 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
1067 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
1068 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
1071 if (sec_status == SEC_E_OK)
1073 PSecPkgInfoW sec_pkg_info;
1074 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1075 if (sec_status == SEC_E_OK)
1077 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1078 FreeContextBuffer(sec_pkg_info);
1081 if (sec_status != SEC_E_OK)
1083 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1084 debugstr_w(pAuthInfo->scheme), sec_status);
1085 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
1086 HeapFree(GetProcessHeap(), 0, pAuthInfo);
1090 *ppAuthInfo = pAuthInfo;
1092 else if (pAuthInfo->finished)
1095 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1096 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1098 ERR("authentication scheme changed from %s to %s\n",
1099 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1103 if (is_basic_auth_value(pszAuthValue,&szRealm))
1107 char *auth_data = NULL;
1108 UINT auth_data_len = 0;
1110 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1112 if (!domain_and_username)
1114 if (host && szRealm)
1115 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1116 if (auth_data_len == 0)
1118 HeapFree(GetProcessHeap(),0,szRealm);
1124 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1125 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1127 /* length includes a nul terminator, which will be re-used for the ':' */
1128 auth_data = heap_alloc(userlen + 1 + passlen);
1131 HeapFree(GetProcessHeap(),0,szRealm);
1135 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1136 auth_data[userlen] = ':';
1137 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1138 auth_data_len = userlen + 1 + passlen;
1139 if (host && szRealm)
1140 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1143 pAuthInfo->auth_data = auth_data;
1144 pAuthInfo->auth_data_len = auth_data_len;
1145 pAuthInfo->finished = TRUE;
1146 HeapFree(GetProcessHeap(),0,szRealm);
1152 LPCWSTR pszAuthData;
1153 SecBufferDesc out_desc, in_desc;
1155 unsigned char *buffer;
1156 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1157 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1159 in.BufferType = SECBUFFER_TOKEN;
1163 in_desc.ulVersion = 0;
1164 in_desc.cBuffers = 1;
1165 in_desc.pBuffers = ∈
1167 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1168 if (*pszAuthData == ' ')
1171 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1172 in.pvBuffer = heap_alloc(in.cbBuffer);
1173 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1176 buffer = heap_alloc(pAuthInfo->max_token);
1178 out.BufferType = SECBUFFER_TOKEN;
1179 out.cbBuffer = pAuthInfo->max_token;
1180 out.pvBuffer = buffer;
1182 out_desc.ulVersion = 0;
1183 out_desc.cBuffers = 1;
1184 out_desc.pBuffers = &out;
1186 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1187 first ? NULL : &pAuthInfo->ctx,
1188 first ? request->session->serverName : NULL,
1189 context_req, 0, SECURITY_NETWORK_DREP,
1190 in.pvBuffer ? &in_desc : NULL,
1191 0, &pAuthInfo->ctx, &out_desc,
1192 &pAuthInfo->attr, &pAuthInfo->exp);
1193 if (sec_status == SEC_E_OK)
1195 pAuthInfo->finished = TRUE;
1196 pAuthInfo->auth_data = out.pvBuffer;
1197 pAuthInfo->auth_data_len = out.cbBuffer;
1198 TRACE("sending last auth packet\n");
1200 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1202 pAuthInfo->auth_data = out.pvBuffer;
1203 pAuthInfo->auth_data_len = out.cbBuffer;
1204 TRACE("sending next auth packet\n");
1208 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1209 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
1210 destroy_authinfo(pAuthInfo);
1219 /***********************************************************************
1220 * HTTP_HttpAddRequestHeadersW (internal)
1222 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1223 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1228 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1230 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1232 if( dwHeaderLength == ~0U )
1233 len = strlenW(lpszHeader);
1235 len = dwHeaderLength;
1236 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1237 lstrcpynW( buffer, lpszHeader, len + 1);
1243 LPWSTR * pFieldAndValue;
1245 lpszEnd = lpszStart;
1247 while (*lpszEnd != '\0')
1249 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1254 if (*lpszStart == '\0')
1257 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1260 lpszEnd++; /* Jump over newline */
1262 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1263 if (*lpszStart == '\0')
1265 /* Skip 0-length headers */
1266 lpszStart = lpszEnd;
1267 res = ERROR_SUCCESS;
1270 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1273 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1274 if (res == ERROR_SUCCESS)
1275 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1276 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1277 HTTP_FreeTokens(pFieldAndValue);
1280 lpszStart = lpszEnd;
1281 } while (res == ERROR_SUCCESS);
1283 HeapFree(GetProcessHeap(), 0, buffer);
1288 /***********************************************************************
1289 * HttpAddRequestHeadersW (WININET.@)
1291 * Adds one or more HTTP header to the request handler
1294 * On Windows if dwHeaderLength includes the trailing '\0', then
1295 * HttpAddRequestHeadersW() adds it too. However this results in an
1296 * invalid Http header which is rejected by some servers so we probably
1297 * don't need to match Windows on that point.
1304 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1305 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1307 http_request_t *request;
1308 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1310 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1315 request = (http_request_t*) get_handle_object( hHttpRequest );
1316 if (request && request->hdr.htype == WH_HHTTPREQ)
1317 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1319 WININET_Release( &request->hdr );
1321 if(res != ERROR_SUCCESS)
1323 return res == ERROR_SUCCESS;
1326 /***********************************************************************
1327 * HttpAddRequestHeadersA (WININET.@)
1329 * Adds one or more HTTP header to the request handler
1336 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1337 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1343 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1345 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1346 hdr = heap_alloc(len*sizeof(WCHAR));
1347 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1348 if( dwHeaderLength != ~0U )
1349 dwHeaderLength = len;
1351 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1353 HeapFree( GetProcessHeap(), 0, hdr );
1358 /***********************************************************************
1359 * HttpOpenRequestA (WININET.@)
1361 * Open a HTTP request handle
1364 * HINTERNET a HTTP request handle on success
1368 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1369 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1370 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1371 DWORD dwFlags, DWORD_PTR dwContext)
1373 LPWSTR szVerb = NULL, szObjectName = NULL;
1374 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1375 INT acceptTypesCount;
1376 HINTERNET rc = FALSE;
1379 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1380 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1381 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1382 dwFlags, dwContext);
1386 szVerb = heap_strdupAtoW(lpszVerb);
1393 szObjectName = heap_strdupAtoW(lpszObjectName);
1394 if ( !szObjectName )
1400 szVersion = heap_strdupAtoW(lpszVersion);
1407 szReferrer = heap_strdupAtoW(lpszReferrer);
1412 if (lpszAcceptTypes)
1414 acceptTypesCount = 0;
1415 types = lpszAcceptTypes;
1420 /* find out how many there are */
1421 if (*types && **types)
1423 TRACE("accept type: %s\n", debugstr_a(*types));
1429 WARN("invalid accept type pointer\n");
1434 szAcceptTypes = heap_alloc(sizeof(WCHAR *) * (acceptTypesCount+1));
1435 if (!szAcceptTypes) goto end;
1437 acceptTypesCount = 0;
1438 types = lpszAcceptTypes;
1443 if (*types && **types)
1444 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1448 /* ignore invalid pointer */
1453 szAcceptTypes[acceptTypesCount] = NULL;
1456 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1457 szVersion, szReferrer,
1458 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1463 acceptTypesCount = 0;
1464 while (szAcceptTypes[acceptTypesCount])
1466 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1469 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1471 HeapFree(GetProcessHeap(), 0, szReferrer);
1472 HeapFree(GetProcessHeap(), 0, szVersion);
1473 HeapFree(GetProcessHeap(), 0, szObjectName);
1474 HeapFree(GetProcessHeap(), 0, szVerb);
1479 /***********************************************************************
1482 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1485 static const CHAR HTTP_Base64Enc[] =
1486 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1490 /* first 6 bits, all from bin[0] */
1491 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1492 x = (bin[0] & 3) << 4;
1494 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1497 base64[n++] = HTTP_Base64Enc[x];
1502 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1503 x = ( bin[1] & 0x0f ) << 2;
1505 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1508 base64[n++] = HTTP_Base64Enc[x];
1512 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1514 /* last 6 bits, all from bin [2] */
1515 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1523 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1524 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1525 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1526 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1527 static const signed char HTTP_Base64Dec[256] =
1529 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1530 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1531 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1532 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1533 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1534 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1535 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1536 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1537 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1538 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1539 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1540 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1541 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1542 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1543 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1544 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1545 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1546 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1547 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1548 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1549 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1550 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1551 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1552 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1553 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1554 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1558 /***********************************************************************
1561 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1569 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1570 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1571 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1572 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1574 WARN("invalid base64: %s\n", debugstr_w(base64));
1578 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1581 if ((base64[2] == '=') && (base64[3] == '='))
1583 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1584 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1586 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1590 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1593 if (base64[3] == '=')
1595 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1596 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1598 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1602 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1611 /***********************************************************************
1612 * HTTP_InsertAuthorization
1614 * Insert or delete the authorization field in the request header.
1616 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1620 static const WCHAR wszSpace[] = {' ',0};
1621 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1623 WCHAR *authorization = NULL;
1625 if (pAuthInfo->auth_data_len)
1627 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1628 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1629 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1633 strcpyW(authorization, pAuthInfo->scheme);
1634 strcatW(authorization, wszSpace);
1635 HTTP_EncodeBase64(pAuthInfo->auth_data,
1636 pAuthInfo->auth_data_len,
1637 authorization+strlenW(authorization));
1639 /* clear the data as it isn't valid now that it has been sent to the
1640 * server, unless it's Basic authentication which doesn't do
1641 * connection tracking */
1642 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1644 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1645 pAuthInfo->auth_data = NULL;
1646 pAuthInfo->auth_data_len = 0;
1650 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1652 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1654 HeapFree(GetProcessHeap(), 0, authorization);
1659 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1661 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1664 size = sizeof(new_location);
1665 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1667 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1668 strcpyW( url, new_location );
1672 static const WCHAR slash[] = { '/',0 };
1673 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1674 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1675 http_session_t *session = req->session;
1677 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1678 size += strlenW( session->hostName ) + strlenW( req->path );
1680 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1682 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1683 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1685 sprintfW( url, format, session->hostName, session->hostPort );
1686 if (req->path[0] != '/') strcatW( url, slash );
1687 strcatW( url, req->path );
1689 TRACE("url=%s\n", debugstr_w(url));
1693 /***********************************************************************
1694 * HTTP_DealWithProxy
1696 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1698 WCHAR buf[MAXHOSTNAME];
1699 WCHAR protoProxy[MAXHOSTNAME + 15];
1700 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1701 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1702 static WCHAR szNul[] = { 0 };
1703 URL_COMPONENTSW UrlComponents;
1704 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1705 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1706 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1708 memset( &UrlComponents, 0, sizeof UrlComponents );
1709 UrlComponents.dwStructSize = sizeof UrlComponents;
1710 UrlComponents.lpszHostName = buf;
1711 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1713 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1715 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1716 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1717 sprintfW(proxy, szFormat, protoProxy);
1719 strcpyW(proxy, protoProxy);
1720 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1722 if( UrlComponents.dwHostNameLength == 0 )
1725 if( !request->path )
1726 request->path = szNul;
1728 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1729 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1731 HeapFree(GetProcessHeap(), 0, session->serverName);
1732 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1733 session->serverPort = UrlComponents.nPort;
1735 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1739 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
1744 if(server->addr_len)
1745 return ERROR_SUCCESS;
1747 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1748 INTERNET_STATUS_RESOLVING_NAME,
1750 (strlenW(server->name)+1) * sizeof(WCHAR));
1752 addr_len = sizeof(server->addr);
1753 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1754 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1756 switch(server->addr.ss_family) {
1758 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1761 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1764 WARN("unsupported family %d\n", server->addr.ss_family);
1765 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1768 server->addr_len = addr_len;
1769 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1770 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1771 INTERNET_STATUS_NAME_RESOLVED,
1772 server->addr_str, strlen(server->addr_str)+1);
1774 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1775 return ERROR_SUCCESS;
1778 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1780 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1781 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1782 static const WCHAR slash[] = { '/',0 };
1783 LPHTTPHEADERW host_header;
1786 host_header = HTTP_GetHeader(req, hostW);
1790 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1794 strcpyW(buf, scheme);
1795 strcatW(buf, host_header->lpszValue);
1796 if (req->path[0] != '/')
1797 strcatW(buf, slash);
1798 strcatW(buf, req->path);
1803 /***********************************************************************
1804 * HTTPREQ_Destroy (internal)
1806 * Deallocate request handle
1809 static void HTTPREQ_Destroy(object_header_t *hdr)
1811 http_request_t *request = (http_request_t*) hdr;
1816 if(request->hCacheFile) {
1817 WCHAR url[INTERNET_MAX_URL_LENGTH];
1819 CloseHandle(request->hCacheFile);
1821 if(HTTP_GetRequestURL(request, url)) {
1824 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1825 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1826 request->last_modified, NORMAL_CACHE_ENTRY,
1827 request->rawHeaders, headersLen, NULL, 0);
1831 HeapFree(GetProcessHeap(), 0, request->cacheFile);
1833 DeleteCriticalSection( &request->read_section );
1834 WININET_Release(&request->session->hdr);
1836 destroy_authinfo(request->authInfo);
1837 destroy_authinfo(request->proxyAuthInfo);
1839 HeapFree(GetProcessHeap(), 0, request->path);
1840 HeapFree(GetProcessHeap(), 0, request->verb);
1841 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
1842 HeapFree(GetProcessHeap(), 0, request->version);
1843 HeapFree(GetProcessHeap(), 0, request->statusText);
1845 for (i = 0; i < request->nCustHeaders; i++)
1847 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszField);
1848 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszValue);
1851 destroy_data_stream(request->data_stream);
1852 HeapFree(GetProcessHeap(), 0, request->custHeaders);
1855 static void http_release_netconn(http_request_t *req, BOOL reuse)
1857 TRACE("%p %p\n",req, req->netconn);
1862 if(reuse && req->netconn->keep_alive) {
1865 EnterCriticalSection(&connection_pool_cs);
1867 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1868 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1869 req->netconn = NULL;
1871 run_collector = !collector_running;
1872 collector_running = TRUE;
1874 LeaveCriticalSection(&connection_pool_cs);
1877 HANDLE thread = NULL;
1880 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1882 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1884 EnterCriticalSection(&connection_pool_cs);
1885 collector_running = FALSE;
1886 LeaveCriticalSection(&connection_pool_cs);
1889 FreeLibrary(module);
1895 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1896 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1898 free_netconn(req->netconn);
1899 req->netconn = NULL;
1901 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1902 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1905 static void drain_content(http_request_t *req)
1909 if (!req->netconn) return;
1911 if (req->contentLength == -1)
1913 else if(!strcmpW(req->verb, szHEAD))
1916 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1918 http_release_netconn(req, try_reuse);
1921 static BOOL HTTP_KeepAlive(http_request_t *request)
1923 WCHAR szVersion[10];
1924 WCHAR szConnectionResponse[20];
1925 DWORD dwBufferSize = sizeof(szVersion);
1926 BOOL keepalive = FALSE;
1928 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1929 * the connection is keep-alive by default */
1930 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1931 && !strcmpiW(szVersion, g_szHttp1_1))
1936 dwBufferSize = sizeof(szConnectionResponse);
1937 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1938 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1940 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1946 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1948 http_request_t *req = (http_request_t*)hdr;
1953 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1955 http_request_t *req = (http_request_t*)hdr;
1958 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1960 http_session_t *session = req->session;
1961 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1963 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1965 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1966 return ERROR_INSUFFICIENT_BUFFER;
1967 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1968 /* FIXME: can't get a SOCKET from our connection since we don't use
1972 /* FIXME: get source port from req->netConnection */
1973 info->SourcePort = 0;
1974 info->DestPort = session->hostPort;
1976 if (HTTP_KeepAlive(req))
1977 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1978 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1979 info->Flags |= IDSI_FLAG_PROXY;
1980 if (req->netconn->useSSL)
1981 info->Flags |= IDSI_FLAG_SECURE;
1983 return ERROR_SUCCESS;
1986 case INTERNET_OPTION_SECURITY_FLAGS:
1990 if (*size < sizeof(ULONG))
1991 return ERROR_INSUFFICIENT_BUFFER;
1993 *size = sizeof(DWORD);
1995 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1996 flags |= SECURITY_FLAG_SECURE;
1997 flags |= req->security_flags;
1999 int bits = NETCON_GetCipherStrength(req->netconn);
2001 flags |= SECURITY_FLAG_STRENGTH_STRONG;
2002 else if (bits >= 56)
2003 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
2005 flags |= SECURITY_FLAG_STRENGTH_WEAK;
2007 *(DWORD *)buffer = flags;
2008 return ERROR_SUCCESS;
2011 case INTERNET_OPTION_HANDLE_TYPE:
2012 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2014 if (*size < sizeof(ULONG))
2015 return ERROR_INSUFFICIENT_BUFFER;
2017 *size = sizeof(DWORD);
2018 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2019 return ERROR_SUCCESS;
2021 case INTERNET_OPTION_URL: {
2022 WCHAR url[INTERNET_MAX_URL_LENGTH];
2027 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2029 TRACE("INTERNET_OPTION_URL\n");
2031 host = HTTP_GetHeader(req, hostW);
2032 strcpyW(url, httpW);
2033 strcatW(url, host->lpszValue);
2034 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2036 strcatW(url, req->path);
2038 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2041 len = (strlenW(url)+1) * sizeof(WCHAR);
2043 return ERROR_INSUFFICIENT_BUFFER;
2046 strcpyW(buffer, url);
2047 return ERROR_SUCCESS;
2049 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2051 return ERROR_INSUFFICIENT_BUFFER;
2054 return ERROR_SUCCESS;
2058 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2059 INTERNET_CACHE_ENTRY_INFOW *info;
2060 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2061 WCHAR url[INTERNET_MAX_URL_LENGTH];
2062 DWORD nbytes, error;
2065 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2067 if (*size < sizeof(*ts))
2069 *size = sizeof(*ts);
2070 return ERROR_INSUFFICIENT_BUFFER;
2073 HTTP_GetRequestURL(req, url);
2074 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2075 error = GetLastError();
2076 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2078 if (!(info = heap_alloc(nbytes)))
2079 return ERROR_OUTOFMEMORY;
2081 GetUrlCacheEntryInfoW(url, info, &nbytes);
2083 ts->ftExpires = info->ExpireTime;
2084 ts->ftLastModified = info->LastModifiedTime;
2086 HeapFree(GetProcessHeap(), 0, info);
2087 *size = sizeof(*ts);
2088 return ERROR_SUCCESS;
2093 case INTERNET_OPTION_DATAFILE_NAME: {
2096 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2098 if(!req->cacheFile) {
2100 return ERROR_INTERNET_ITEM_NOT_FOUND;
2104 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2105 if(*size < req_size)
2106 return ERROR_INSUFFICIENT_BUFFER;
2109 memcpy(buffer, req->cacheFile, *size);
2110 return ERROR_SUCCESS;
2112 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2113 if (req_size > *size)
2114 return ERROR_INSUFFICIENT_BUFFER;
2116 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2117 -1, buffer, *size, NULL, NULL);
2118 return ERROR_SUCCESS;
2122 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2123 PCCERT_CONTEXT context;
2125 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2126 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2127 return ERROR_INSUFFICIENT_BUFFER;
2130 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2132 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2135 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2136 info->ftExpiry = context->pCertInfo->NotAfter;
2137 info->ftStart = context->pCertInfo->NotBefore;
2138 len = CertNameToStrA(context->dwCertEncodingType,
2139 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2140 info->lpszSubjectInfo = LocalAlloc(0, len);
2141 if(info->lpszSubjectInfo)
2142 CertNameToStrA(context->dwCertEncodingType,
2143 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2144 info->lpszSubjectInfo, len);
2145 len = CertNameToStrA(context->dwCertEncodingType,
2146 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2147 info->lpszIssuerInfo = LocalAlloc(0, len);
2148 if(info->lpszIssuerInfo)
2149 CertNameToStrA(context->dwCertEncodingType,
2150 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2151 info->lpszIssuerInfo, len);
2152 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2153 CertFreeCertificateContext(context);
2154 return ERROR_SUCCESS;
2159 return INET_QueryOption(hdr, option, buffer, size, unicode);
2162 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2164 http_request_t *req = (http_request_t*)hdr;
2167 case INTERNET_OPTION_SECURITY_FLAGS:
2171 if (!buffer || size != sizeof(DWORD))
2172 return ERROR_INVALID_PARAMETER;
2173 flags = *(DWORD *)buffer;
2174 TRACE("%08x\n", flags);
2175 req->security_flags = flags;
2177 req->netconn->security_flags = flags;
2178 return ERROR_SUCCESS;
2180 case INTERNET_OPTION_SEND_TIMEOUT:
2181 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2182 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2184 if (size != sizeof(DWORD))
2185 return ERROR_INVALID_PARAMETER;
2188 FIXME("unsupported without active connection\n");
2189 return ERROR_SUCCESS;
2192 return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
2195 case INTERNET_OPTION_USERNAME:
2196 HeapFree(GetProcessHeap(), 0, req->session->userName);
2197 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2198 return ERROR_SUCCESS;
2200 case INTERNET_OPTION_PASSWORD:
2201 HeapFree(GetProcessHeap(), 0, req->session->password);
2202 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2203 return ERROR_SUCCESS;
2204 case INTERNET_OPTION_HTTP_DECODING:
2205 if(size != sizeof(BOOL))
2206 return ERROR_INVALID_PARAMETER;
2207 req->decoding = *(BOOL*)buffer;
2208 return ERROR_SUCCESS;
2211 return ERROR_INTERNET_INVALID_OPTION;
2214 /* read some more data into the read buffer (the read section must be held) */
2215 static DWORD read_more_data( http_request_t *req, int maxlen )
2222 /* move existing data to the start of the buffer */
2224 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2228 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2230 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2231 maxlen - req->read_size, 0, &len );
2232 if(res == ERROR_SUCCESS)
2233 req->read_size += len;
2238 /* remove some amount of data from the read buffer (the read section must be held) */
2239 static void remove_data( http_request_t *req, int count )
2241 if (!(req->read_size -= count)) req->read_pos = 0;
2242 else req->read_pos += count;
2245 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2247 int count, bytes_read, pos = 0;
2250 EnterCriticalSection( &req->read_section );
2253 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2257 count = eol - (req->read_buf + req->read_pos);
2258 bytes_read = count + 1;
2260 else count = bytes_read = req->read_size;
2262 count = min( count, *len - pos );
2263 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2265 remove_data( req, bytes_read );
2268 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2271 TRACE( "returning empty string %u\n", res);
2272 LeaveCriticalSection( &req->read_section );
2273 INTERNET_SetLastError(res);
2277 LeaveCriticalSection( &req->read_section );
2281 if (pos && buffer[pos - 1] == '\r') pos--;
2284 buffer[*len - 1] = 0;
2285 TRACE( "returning %s\n", debugstr_a(buffer));
2289 /* check if we have reached the end of the data to read (the read section must be held) */
2290 static BOOL end_of_read_data( http_request_t *req )
2292 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2295 /* fetch some more data into the read buffer (the read section must be held) */
2296 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2300 if(req->read_size == sizeof(req->read_buf))
2301 return ERROR_SUCCESS;
2305 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2309 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2310 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2311 req->read_size += read;
2313 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2319 /* return the size of data available to be read immediately (the read section must be held) */
2320 static DWORD get_avail_data( http_request_t *req )
2322 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2325 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2327 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2331 NETCON_query_data_available(req->netconn, &avail);
2332 return netconn_stream->content_length == ~0u
2334 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2337 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2339 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2340 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2343 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2344 DWORD *read, read_mode_t read_mode)
2346 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2349 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2351 if(read_mode == READMODE_NOBLOCK)
2352 size = min(size, netconn_get_avail_data(stream, req));
2354 if(size && req->netconn) {
2355 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2359 netconn_stream->content_read += *read = len;
2360 TRACE("read %u bytes\n", len);
2361 return ERROR_SUCCESS;
2364 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2366 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2371 if(netconn_end_of_data(stream, req))
2375 avail = netconn_get_avail_data(stream, req);
2379 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2382 netconn_stream->content_read += len;
2383 }while(netconn_stream->content_read < netconn_stream->content_length);
2388 static void netconn_destroy(data_stream_t *stream)
2392 static const data_stream_vtbl_t netconn_stream_vtbl = {
2393 netconn_get_avail_data,
2394 netconn_end_of_data,
2396 netconn_drain_content,
2400 /* read some more data into the read buffer (the read section must be held) */
2401 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2406 if (stream->buf_pos)
2408 /* move existing data to the start of the buffer */
2409 if(stream->buf_size)
2410 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2411 stream->buf_pos = 0;
2414 if (maxlen == -1) maxlen = sizeof(stream->buf);
2416 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2417 maxlen - stream->buf_size, 0, &len );
2418 if(res == ERROR_SUCCESS)
2419 stream->buf_size += len;
2424 /* remove some amount of data from the read buffer (the read section must be held) */
2425 static void remove_chunked_data(chunked_stream_t *stream, int count)
2427 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2428 else stream->buf_pos += count;
2431 /* discard data contents until we reach end of line (the read section must be held) */
2432 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2438 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2441 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2444 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2445 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2446 } while (stream->buf_size);
2447 return ERROR_SUCCESS;
2450 /* read the size of the next chunk (the read section must be held) */
2451 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2454 DWORD chunk_size = 0, res;
2456 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2461 while (stream->buf_size)
2463 char ch = stream->buf[stream->buf_pos];
2464 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2465 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2466 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2467 else if (ch == ';' || ch == '\r' || ch == '\n')
2469 TRACE( "reading %u byte chunk\n", chunk_size );
2470 stream->chunk_size = chunk_size;
2471 req->contentLength += chunk_size;
2472 return discard_chunked_eol(stream, req);
2474 remove_chunked_data(stream, 1);
2476 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2477 if (!stream->buf_size)
2479 stream->chunk_size = 0;
2480 return ERROR_SUCCESS;
2485 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2487 /* Allow reading only from read buffer */
2491 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2493 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2494 return !chunked_stream->chunk_size;
2497 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2498 DWORD *read, read_mode_t read_mode)
2500 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2501 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2503 if(chunked_stream->chunk_size == ~0u) {
2504 res = start_next_chunk(chunked_stream, req);
2505 if(res != ERROR_SUCCESS)
2509 while(size && chunked_stream->chunk_size) {
2510 if(chunked_stream->buf_size) {
2511 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2513 /* this could block */
2514 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2517 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2518 remove_chunked_data(chunked_stream, read_bytes);
2520 read_bytes = min(size, chunked_stream->chunk_size);
2522 if(read_mode == READMODE_NOBLOCK) {
2525 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2527 if(read_bytes > avail)
2530 /* this could block */
2531 if(read_bytes == chunked_stream->chunk_size)
2535 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2536 if(res != ERROR_SUCCESS)
2540 chunked_stream->chunk_size -= read_bytes;
2542 ret_read += read_bytes;
2543 if(!chunked_stream->chunk_size) {
2544 assert(read_mode != READMODE_NOBLOCK);
2545 res = start_next_chunk(chunked_stream, req);
2546 if(res != ERROR_SUCCESS)
2550 if(read_mode == READMODE_ASYNC)
2551 read_mode = READMODE_NOBLOCK;
2554 TRACE("read %u bytes\n", ret_read);
2559 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2561 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2563 /* FIXME: we can do better */
2564 return !chunked_stream->chunk_size;
2567 static void chunked_destroy(data_stream_t *stream)
2569 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2570 heap_free(chunked_stream);
2573 static const data_stream_vtbl_t chunked_stream_vtbl = {
2574 chunked_get_avail_data,
2575 chunked_end_of_data,
2577 chunked_drain_content,
2581 /* set the request content length based on the headers */
2582 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2584 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2588 if(status_code == HTTP_STATUS_NO_CONTENT) {
2589 request->contentLength = request->netconn_stream.content_length = 0;
2590 return ERROR_SUCCESS;
2593 size = sizeof(request->contentLength);
2594 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2595 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2596 request->contentLength = ~0u;
2597 request->netconn_stream.content_length = request->contentLength;
2598 request->netconn_stream.content_read = request->read_size;
2600 size = sizeof(encoding);
2601 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2602 !strcmpiW(encoding, szChunked))
2604 chunked_stream_t *chunked_stream;
2606 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2608 return ERROR_OUTOFMEMORY;
2610 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2611 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2612 chunked_stream->chunk_size = ~0u;
2614 if(request->read_size) {
2615 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2616 chunked_stream->buf_size = request->read_size;
2617 request->read_size = request->read_pos = 0;
2620 request->data_stream = &chunked_stream->data_stream;
2621 request->contentLength = ~0u;
2622 request->read_chunked = TRUE;
2625 if(request->decoding) {
2628 static const WCHAR gzipW[] = {'g','z','i','p',0};
2630 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2631 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2632 return init_gzip_stream(request);
2635 return ERROR_SUCCESS;
2638 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2640 INTERNET_ASYNC_RESULT iar;
2641 DWORD res, read = 0;
2646 EnterCriticalSection( &req->read_section );
2648 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2649 res = refill_read_buffer(req, mode, &read);
2650 if(res == ERROR_SUCCESS) {
2651 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2652 iar.dwError = first_notif ? 0 : get_avail_data(req);
2658 LeaveCriticalSection( &req->read_section );
2660 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2661 WARN("res %u read %u, closing connection\n", res, read);
2662 http_release_netconn(req, FALSE);
2665 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2666 sizeof(INTERNET_ASYNC_RESULT));
2669 /* read data from the http connection (the read section must be held) */
2670 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2672 DWORD current_read = 0, ret_read = 0;
2673 read_mode_t read_mode;
2674 DWORD res = ERROR_SUCCESS;
2676 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2678 EnterCriticalSection( &req->read_section );
2680 if(req->read_size) {
2681 ret_read = min(size, req->read_size);
2682 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2683 req->read_size -= ret_read;
2684 req->read_pos += ret_read;
2685 if(read_mode == READMODE_ASYNC)
2686 read_mode = READMODE_NOBLOCK;
2689 if(ret_read < size) {
2690 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2691 ret_read += current_read;
2694 LeaveCriticalSection( &req->read_section );
2697 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2699 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2703 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2705 WARN("WriteFile failed: %u\n", GetLastError());
2708 if(size && !ret_read)
2709 http_release_netconn(req, res == ERROR_SUCCESS);
2715 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2717 http_request_t *req = (http_request_t*)hdr;
2720 EnterCriticalSection( &req->read_section );
2721 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2722 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2724 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2725 if(res == ERROR_SUCCESS)
2727 LeaveCriticalSection( &req->read_section );
2732 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2734 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2735 http_request_t *req = (http_request_t*)workRequest->hdr;
2736 INTERNET_ASYNC_RESULT iar;
2739 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2741 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2742 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2744 iar.dwResult = res == ERROR_SUCCESS;
2747 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2748 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2749 sizeof(INTERNET_ASYNC_RESULT));
2752 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2753 DWORD flags, DWORD_PTR context)
2755 http_request_t *req = (http_request_t*)hdr;
2756 DWORD res, size, read, error = ERROR_SUCCESS;
2758 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2759 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2761 if (buffers->dwStructSize != sizeof(*buffers))
2762 return ERROR_INVALID_PARAMETER;
2764 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2766 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2768 WORKREQUEST workRequest;
2770 if (TryEnterCriticalSection( &req->read_section ))
2772 if (get_avail_data(req))
2774 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2775 &buffers->dwBufferLength, FALSE);
2776 size = buffers->dwBufferLength;
2777 LeaveCriticalSection( &req->read_section );
2780 LeaveCriticalSection( &req->read_section );
2783 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2784 workRequest.hdr = WININET_AddRef(&req->hdr);
2785 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2787 INTERNET_AsyncCall(&workRequest);
2789 return ERROR_IO_PENDING;
2793 size = buffers->dwBufferLength;
2795 EnterCriticalSection( &req->read_section );
2796 if(hdr->dwError == ERROR_SUCCESS)
2797 hdr->dwError = INTERNET_HANDLE_IN_USE;
2798 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2799 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2802 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2803 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2804 if(res != ERROR_SUCCESS)
2807 read += buffers->dwBufferLength;
2808 if(read == size || end_of_read_data(req))
2811 LeaveCriticalSection( &req->read_section );
2813 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2814 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2815 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2816 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2818 EnterCriticalSection( &req->read_section );
2821 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2822 hdr->dwError = ERROR_SUCCESS;
2824 error = hdr->dwError;
2826 LeaveCriticalSection( &req->read_section );
2827 size = buffers->dwBufferLength;
2828 buffers->dwBufferLength = read;
2831 if (res == ERROR_SUCCESS) {
2832 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2833 &size, sizeof(size));
2836 return res==ERROR_SUCCESS ? error : res;
2839 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2841 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2842 http_request_t *req = (http_request_t*)workRequest->hdr;
2843 INTERNET_ASYNC_RESULT iar;
2846 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2848 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2849 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2851 iar.dwResult = res == ERROR_SUCCESS;
2854 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2855 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2856 sizeof(INTERNET_ASYNC_RESULT));
2859 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2860 DWORD flags, DWORD_PTR context)
2863 http_request_t *req = (http_request_t*)hdr;
2864 DWORD res, size, read, error = ERROR_SUCCESS;
2866 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2867 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2869 if (buffers->dwStructSize != sizeof(*buffers))
2870 return ERROR_INVALID_PARAMETER;
2872 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2874 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2876 WORKREQUEST workRequest;
2878 if (TryEnterCriticalSection( &req->read_section ))
2880 if (get_avail_data(req))
2882 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2883 &buffers->dwBufferLength, FALSE);
2884 size = buffers->dwBufferLength;
2885 LeaveCriticalSection( &req->read_section );
2888 LeaveCriticalSection( &req->read_section );
2891 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2892 workRequest.hdr = WININET_AddRef(&req->hdr);
2893 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2895 INTERNET_AsyncCall(&workRequest);
2897 return ERROR_IO_PENDING;
2901 size = buffers->dwBufferLength;
2903 EnterCriticalSection( &req->read_section );
2904 if(hdr->dwError == ERROR_SUCCESS)
2905 hdr->dwError = INTERNET_HANDLE_IN_USE;
2906 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2907 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2910 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2911 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2912 if(res != ERROR_SUCCESS)
2915 read += buffers->dwBufferLength;
2916 if(read == size || end_of_read_data(req))
2919 LeaveCriticalSection( &req->read_section );
2921 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2922 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2923 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2924 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2926 EnterCriticalSection( &req->read_section );
2929 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2930 hdr->dwError = ERROR_SUCCESS;
2932 error = hdr->dwError;
2934 LeaveCriticalSection( &req->read_section );
2935 size = buffers->dwBufferLength;
2936 buffers->dwBufferLength = read;
2939 if (res == ERROR_SUCCESS) {
2940 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2941 &size, sizeof(size));
2944 return res==ERROR_SUCCESS ? error : res;
2947 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2950 http_request_t *request = (http_request_t*)hdr;
2952 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2955 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2956 if (res == ERROR_SUCCESS)
2957 request->bytesWritten += *written;
2959 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2963 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2965 http_request_t *req = (http_request_t*)workRequest->hdr;
2967 HTTP_ReceiveRequestData(req, FALSE);
2970 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2972 http_request_t *req = (http_request_t*)hdr;
2974 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2976 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2978 WORKREQUEST workRequest;
2980 /* never wait, if we can't enter the section we queue an async request right away */
2981 if (TryEnterCriticalSection( &req->read_section ))
2983 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2984 if ((*available = get_avail_data( req ))) goto done;
2985 if (end_of_read_data( req )) goto done;
2986 LeaveCriticalSection( &req->read_section );
2989 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2990 workRequest.hdr = WININET_AddRef( &req->hdr );
2992 INTERNET_AsyncCall(&workRequest);
2994 return ERROR_IO_PENDING;
2997 EnterCriticalSection( &req->read_section );
2999 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3001 refill_read_buffer( req, READMODE_ASYNC, NULL );
3002 *available = get_avail_data( req );
3006 LeaveCriticalSection( &req->read_section );
3008 TRACE( "returning %u\n", *available );
3009 return ERROR_SUCCESS;
3012 static const object_vtbl_t HTTPREQVtbl = {
3014 HTTPREQ_CloseConnection,
3015 HTTPREQ_QueryOption,
3018 HTTPREQ_ReadFileExA,
3019 HTTPREQ_ReadFileExW,
3021 HTTPREQ_QueryDataAvailable,
3025 /***********************************************************************
3026 * HTTP_HttpOpenRequestW (internal)
3028 * Open a HTTP request handle
3031 * HINTERNET a HTTP request handle on success
3035 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3036 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3037 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3038 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3040 appinfo_t *hIC = session->appInfo;
3041 http_request_t *request;
3042 DWORD len, res = ERROR_SUCCESS;
3046 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3048 return ERROR_OUTOFMEMORY;
3050 request->hdr.htype = WH_HHTTPREQ;
3051 request->hdr.dwFlags = dwFlags;
3052 request->hdr.dwContext = dwContext;
3053 request->contentLength = ~0u;
3055 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3056 request->data_stream = &request->netconn_stream.data_stream;
3058 InitializeCriticalSection( &request->read_section );
3060 WININET_AddRef( &session->hdr );
3061 request->session = session;
3062 list_add_head( &session->hdr.children, &request->hdr.entry );
3064 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3065 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3066 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3067 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3069 if (lpszObjectName && *lpszObjectName) {
3073 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3074 if (rc != E_POINTER)
3075 len = strlenW(lpszObjectName)+1;
3076 request->path = heap_alloc(len*sizeof(WCHAR));
3077 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3078 URL_ESCAPE_SPACES_ONLY);
3081 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3082 strcpyW(request->path,lpszObjectName);
3085 static const WCHAR slashW[] = {'/',0};
3087 request->path = heap_strdupW(slashW);
3090 if (lpszReferrer && *lpszReferrer)
3091 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3093 if (lpszAcceptTypes)
3096 for (i = 0; lpszAcceptTypes[i]; i++)
3098 if (!*lpszAcceptTypes[i]) continue;
3099 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3100 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3101 HTTP_ADDHDR_FLAG_REQ |
3102 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3106 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3107 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3109 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3110 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3111 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3115 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3117 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3119 res = ERROR_OUTOFMEMORY;
3123 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3124 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3125 heap_free(host_name);
3128 HTTP_ProcessHeader(request, hostW, session->hostName,
3129 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3131 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3132 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3133 INTERNET_DEFAULT_HTTPS_PORT :
3134 INTERNET_DEFAULT_HTTP_PORT);
3136 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3137 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3138 INTERNET_DEFAULT_HTTPS_PORT :
3139 INTERNET_DEFAULT_HTTP_PORT);
3141 if (hIC->proxy && hIC->proxy[0])
3142 HTTP_DealWithProxy( hIC, session, request );
3144 INTERNET_SendCallback(&session->hdr, dwContext,
3145 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3149 TRACE("<-- %u (%p)\n", res, request);
3151 if(res != ERROR_SUCCESS) {
3152 WININET_Release( &request->hdr );
3157 *ret = request->hdr.hInternet;
3158 return ERROR_SUCCESS;
3161 /***********************************************************************
3162 * HttpOpenRequestW (WININET.@)
3164 * Open a HTTP request handle
3167 * HINTERNET a HTTP request handle on success
3171 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3172 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3173 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3174 DWORD dwFlags, DWORD_PTR dwContext)
3176 http_session_t *session;
3177 HINTERNET handle = NULL;
3180 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3181 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3182 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3183 dwFlags, dwContext);
3184 if(lpszAcceptTypes!=NULL)
3187 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3188 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3191 session = (http_session_t*) get_handle_object( hHttpSession );
3192 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3194 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3199 * My tests seem to show that the windows version does not
3200 * become asynchronous until after this point. And anyhow
3201 * if this call was asynchronous then how would you get the
3202 * necessary HINTERNET pointer returned by this function.
3205 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3206 lpszVersion, lpszReferrer, lpszAcceptTypes,
3207 dwFlags, dwContext, &handle);
3210 WININET_Release( &session->hdr );
3211 TRACE("returning %p\n", handle);
3212 if(res != ERROR_SUCCESS)
3217 static const LPCWSTR header_lookup[] = {
3218 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3219 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3220 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3221 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3222 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3223 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3224 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3225 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3226 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3227 szDate, /* HTTP_QUERY_DATE = 9 */
3228 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3229 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3230 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3231 szURI, /* HTTP_QUERY_URI = 13 */
3232 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3233 NULL, /* HTTP_QUERY_COST = 15 */
3234 NULL, /* HTTP_QUERY_LINK = 16 */
3235 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3236 NULL, /* HTTP_QUERY_VERSION = 18 */
3237 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3238 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3239 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3240 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3241 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3242 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3243 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3244 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3245 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3246 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3247 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3248 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3249 NULL, /* HTTP_QUERY_FROM = 31 */
3250 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3251 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3252 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3253 szReferer, /* HTTP_QUERY_REFERER = 35 */
3254 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3255 szServer, /* HTTP_QUERY_SERVER = 37 */
3256 NULL, /* HTTP_TITLE = 38 */
3257 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3258 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3259 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3260 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3261 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3262 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3263 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3264 NULL, /* HTTP_QUERY_REFRESH = 46 */
3265 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3266 szAge, /* HTTP_QUERY_AGE = 48 */
3267 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3268 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3269 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3270 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3271 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3272 szETag, /* HTTP_QUERY_ETAG = 54 */
3273 hostW, /* HTTP_QUERY_HOST = 55 */
3274 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3275 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3276 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3277 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3278 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3279 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3280 szRange, /* HTTP_QUERY_RANGE = 62 */
3281 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3282 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3283 szVary, /* HTTP_QUERY_VARY = 65 */
3284 szVia, /* HTTP_QUERY_VIA = 66 */
3285 szWarning, /* HTTP_QUERY_WARNING = 67 */
3286 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3287 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3288 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3291 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3293 /***********************************************************************
3294 * HTTP_HttpQueryInfoW (internal)
3296 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3297 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3299 LPHTTPHEADERW lphttpHdr = NULL;
3300 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3301 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3302 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3305 /* Find requested header structure */
3308 case HTTP_QUERY_CUSTOM:
3309 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3310 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3312 case HTTP_QUERY_RAW_HEADERS_CRLF:
3316 DWORD res = ERROR_INVALID_PARAMETER;
3319 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3321 headers = request->rawHeaders;
3324 len = strlenW(headers) * sizeof(WCHAR);
3326 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3328 len += sizeof(WCHAR);
3329 res = ERROR_INSUFFICIENT_BUFFER;
3334 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3337 len = strlenW(szCrLf) * sizeof(WCHAR);
3338 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3340 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3341 res = ERROR_SUCCESS;
3343 *lpdwBufferLength = len;
3346 HeapFree(GetProcessHeap(), 0, headers);
3349 case HTTP_QUERY_RAW_HEADERS:
3351 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3353 LPWSTR pszString = lpBuffer;
3355 for (i = 0; ppszRawHeaderLines[i]; i++)
3356 size += strlenW(ppszRawHeaderLines[i]) + 1;
3358 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3360 HTTP_FreeTokens(ppszRawHeaderLines);
3361 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3362 return ERROR_INSUFFICIENT_BUFFER;
3366 for (i = 0; ppszRawHeaderLines[i]; i++)
3368 DWORD len = strlenW(ppszRawHeaderLines[i]);
3369 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3373 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3375 *lpdwBufferLength = size * sizeof(WCHAR);
3376 HTTP_FreeTokens(ppszRawHeaderLines);
3378 return ERROR_SUCCESS;
3380 case HTTP_QUERY_STATUS_TEXT:
3381 if (request->statusText)
3383 DWORD len = strlenW(request->statusText);
3384 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3386 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3387 return ERROR_INSUFFICIENT_BUFFER;
3391 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3392 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3394 *lpdwBufferLength = len * sizeof(WCHAR);
3395 return ERROR_SUCCESS;
3398 case HTTP_QUERY_VERSION:
3399 if (request->version)
3401 DWORD len = strlenW(request->version);
3402 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3404 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3405 return ERROR_INSUFFICIENT_BUFFER;
3409 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3410 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3412 *lpdwBufferLength = len * sizeof(WCHAR);
3413 return ERROR_SUCCESS;
3416 case HTTP_QUERY_CONTENT_ENCODING:
3417 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3418 requested_index,request_only);
3421 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3423 if (level < LAST_TABLE_HEADER && header_lookup[level])
3424 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3425 requested_index,request_only);
3429 lphttpHdr = &request->custHeaders[index];
3431 /* Ensure header satisfies requested attributes */
3433 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3434 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3436 return ERROR_HTTP_HEADER_NOT_FOUND;
3439 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3441 /* coalesce value to requested type */
3442 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3444 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3445 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3447 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3453 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3455 tmpTM = *gmtime(&tmpTime);
3456 STHook = (SYSTEMTIME *)lpBuffer;
3457 STHook->wDay = tmpTM.tm_mday;
3458 STHook->wHour = tmpTM.tm_hour;
3459 STHook->wMilliseconds = 0;
3460 STHook->wMinute = tmpTM.tm_min;
3461 STHook->wDayOfWeek = tmpTM.tm_wday;
3462 STHook->wMonth = tmpTM.tm_mon + 1;
3463 STHook->wSecond = tmpTM.tm_sec;
3464 STHook->wYear = tmpTM.tm_year;
3466 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3467 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3468 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3470 else if (lphttpHdr->lpszValue)
3472 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3474 if (len > *lpdwBufferLength)
3476 *lpdwBufferLength = len;
3477 return ERROR_INSUFFICIENT_BUFFER;
3481 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3482 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3484 *lpdwBufferLength = len - sizeof(WCHAR);
3486 return ERROR_SUCCESS;
3489 /***********************************************************************
3490 * HttpQueryInfoW (WININET.@)
3492 * Queries for information about an HTTP request
3499 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3500 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3502 http_request_t *request;
3505 if (TRACE_ON(wininet)) {
3506 #define FE(x) { x, #x }
3507 static const wininet_flag_info query_flags[] = {
3508 FE(HTTP_QUERY_MIME_VERSION),
3509 FE(HTTP_QUERY_CONTENT_TYPE),
3510 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3511 FE(HTTP_QUERY_CONTENT_ID),
3512 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3513 FE(HTTP_QUERY_CONTENT_LENGTH),
3514 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3515 FE(HTTP_QUERY_ALLOW),
3516 FE(HTTP_QUERY_PUBLIC),
3517 FE(HTTP_QUERY_DATE),
3518 FE(HTTP_QUERY_EXPIRES),
3519 FE(HTTP_QUERY_LAST_MODIFIED),
3520 FE(HTTP_QUERY_MESSAGE_ID),
3522 FE(HTTP_QUERY_DERIVED_FROM),
3523 FE(HTTP_QUERY_COST),
3524 FE(HTTP_QUERY_LINK),
3525 FE(HTTP_QUERY_PRAGMA),
3526 FE(HTTP_QUERY_VERSION),
3527 FE(HTTP_QUERY_STATUS_CODE),
3528 FE(HTTP_QUERY_STATUS_TEXT),
3529 FE(HTTP_QUERY_RAW_HEADERS),
3530 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3531 FE(HTTP_QUERY_CONNECTION),
3532 FE(HTTP_QUERY_ACCEPT),
3533 FE(HTTP_QUERY_ACCEPT_CHARSET),
3534 FE(HTTP_QUERY_ACCEPT_ENCODING),
3535 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3536 FE(HTTP_QUERY_AUTHORIZATION),
3537 FE(HTTP_QUERY_CONTENT_ENCODING),
3538 FE(HTTP_QUERY_FORWARDED),
3539 FE(HTTP_QUERY_FROM),
3540 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3541 FE(HTTP_QUERY_LOCATION),
3542 FE(HTTP_QUERY_ORIG_URI),
3543 FE(HTTP_QUERY_REFERER),
3544 FE(HTTP_QUERY_RETRY_AFTER),
3545 FE(HTTP_QUERY_SERVER),
3546 FE(HTTP_QUERY_TITLE),
3547 FE(HTTP_QUERY_USER_AGENT),
3548 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3549 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3550 FE(HTTP_QUERY_ACCEPT_RANGES),
3551 FE(HTTP_QUERY_SET_COOKIE),
3552 FE(HTTP_QUERY_COOKIE),
3553 FE(HTTP_QUERY_REQUEST_METHOD),
3554 FE(HTTP_QUERY_REFRESH),
3555 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3557 FE(HTTP_QUERY_CACHE_CONTROL),
3558 FE(HTTP_QUERY_CONTENT_BASE),
3559 FE(HTTP_QUERY_CONTENT_LOCATION),
3560 FE(HTTP_QUERY_CONTENT_MD5),
3561 FE(HTTP_QUERY_CONTENT_RANGE),
3562 FE(HTTP_QUERY_ETAG),
3563 FE(HTTP_QUERY_HOST),
3564 FE(HTTP_QUERY_IF_MATCH),
3565 FE(HTTP_QUERY_IF_NONE_MATCH),
3566 FE(HTTP_QUERY_IF_RANGE),
3567 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3568 FE(HTTP_QUERY_MAX_FORWARDS),
3569 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3570 FE(HTTP_QUERY_RANGE),
3571 FE(HTTP_QUERY_TRANSFER_ENCODING),
3572 FE(HTTP_QUERY_UPGRADE),
3573 FE(HTTP_QUERY_VARY),
3575 FE(HTTP_QUERY_WARNING),
3576 FE(HTTP_QUERY_CUSTOM)
3578 static const wininet_flag_info modifier_flags[] = {
3579 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3580 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3581 FE(HTTP_QUERY_FLAG_NUMBER),
3582 FE(HTTP_QUERY_FLAG_COALESCE)
3585 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3586 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3589 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3590 TRACE(" Attribute:");
3591 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3592 if (query_flags[i].val == info) {
3593 TRACE(" %s", query_flags[i].name);
3597 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3598 TRACE(" Unknown (%08x)", info);
3601 TRACE(" Modifier:");
3602 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3603 if (modifier_flags[i].val & info_mod) {
3604 TRACE(" %s", modifier_flags[i].name);
3605 info_mod &= ~ modifier_flags[i].val;
3610 TRACE(" Unknown (%08x)", info_mod);
3615 request = (http_request_t*) get_handle_object( hHttpRequest );
3616 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3618 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3622 if (lpBuffer == NULL)
3623 *lpdwBufferLength = 0;
3624 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3625 lpBuffer, lpdwBufferLength, lpdwIndex);
3629 WININET_Release( &request->hdr );
3631 TRACE("%u <--\n", res);
3632 if(res != ERROR_SUCCESS)
3634 return res == ERROR_SUCCESS;
3637 /***********************************************************************
3638 * HttpQueryInfoA (WININET.@)
3640 * Queries for information about an HTTP request
3647 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3648 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3654 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3655 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3657 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3658 lpdwBufferLength, lpdwIndex );
3664 len = (*lpdwBufferLength)*sizeof(WCHAR);
3665 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3667 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3673 bufferW = heap_alloc(alloclen);
3674 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3675 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3676 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3683 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3687 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3688 lpBuffer, *lpdwBufferLength, NULL, NULL );
3689 *lpdwBufferLength = len - 1;
3691 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3694 /* since the strings being returned from HttpQueryInfoW should be
3695 * only ASCII characters, it is reasonable to assume that all of
3696 * the Unicode characters can be reduced to a single byte */
3697 *lpdwBufferLength = len / sizeof(WCHAR);
3699 HeapFree(GetProcessHeap(), 0, bufferW );
3704 /***********************************************************************
3705 * HTTP_GetRedirectURL (internal)
3707 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3709 static WCHAR szHttp[] = {'h','t','t','p',0};
3710 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3711 http_session_t *session = request->session;
3712 URL_COMPONENTSW urlComponents;
3713 DWORD url_length = 0;
3715 LPWSTR combined_url;
3717 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3718 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3719 urlComponents.dwSchemeLength = 0;
3720 urlComponents.lpszHostName = session->hostName;
3721 urlComponents.dwHostNameLength = 0;
3722 urlComponents.nPort = session->hostPort;
3723 urlComponents.lpszUserName = session->userName;
3724 urlComponents.dwUserNameLength = 0;
3725 urlComponents.lpszPassword = NULL;
3726 urlComponents.dwPasswordLength = 0;
3727 urlComponents.lpszUrlPath = request->path;
3728 urlComponents.dwUrlPathLength = 0;
3729 urlComponents.lpszExtraInfo = NULL;
3730 urlComponents.dwExtraInfoLength = 0;
3732 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3733 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3736 orig_url = heap_alloc(url_length);
3738 /* convert from bytes to characters */
3739 url_length = url_length / sizeof(WCHAR) - 1;
3740 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3742 HeapFree(GetProcessHeap(), 0, orig_url);
3747 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3748 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3750 HeapFree(GetProcessHeap(), 0, orig_url);
3753 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3755 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3757 HeapFree(GetProcessHeap(), 0, orig_url);
3758 HeapFree(GetProcessHeap(), 0, combined_url);
3761 HeapFree(GetProcessHeap(), 0, orig_url);
3762 return combined_url;
3766 /***********************************************************************
3767 * HTTP_HandleRedirect (internal)
3769 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3771 http_session_t *session = request->session;
3772 appinfo_t *hIC = session->appInfo;
3773 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3774 WCHAR path[INTERNET_MAX_URL_LENGTH];
3779 /* if it's an absolute path, keep the same session info */
3780 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3784 URL_COMPONENTSW urlComponents;
3785 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3786 static WCHAR szHttp[] = {'h','t','t','p',0};
3787 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3793 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3794 urlComponents.lpszScheme = protocol;
3795 urlComponents.dwSchemeLength = 32;
3796 urlComponents.lpszHostName = hostName;
3797 urlComponents.dwHostNameLength = MAXHOSTNAME;
3798 urlComponents.lpszUserName = userName;
3799 urlComponents.dwUserNameLength = 1024;
3800 urlComponents.lpszPassword = NULL;
3801 urlComponents.dwPasswordLength = 0;
3802 urlComponents.lpszUrlPath = path;
3803 urlComponents.dwUrlPathLength = 2048;
3804 urlComponents.lpszExtraInfo = NULL;
3805 urlComponents.dwExtraInfoLength = 0;
3806 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3807 return INTERNET_GetLastError();
3809 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3810 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3812 TRACE("redirect from secure page to non-secure page\n");
3813 /* FIXME: warn about from secure redirect to non-secure page */
3814 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3816 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3817 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3819 TRACE("redirect from non-secure page to secure page\n");
3820 /* FIXME: notify about redirect to secure page */
3821 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3824 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3826 if (lstrlenW(protocol)>4) /*https*/
3827 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3829 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3834 * This upsets redirects to binary files on sourceforge.net
3835 * and gives an html page instead of the target file
3836 * Examination of the HTTP request sent by native wininet.dll
3837 * reveals that it doesn't send a referrer in that case.
3838 * Maybe there's a flag that enables this, or maybe a referrer
3839 * shouldn't be added in case of a redirect.
3842 /* consider the current host as the referrer */
3843 if (session->lpszServerName && *session->lpszServerName)
3844 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3845 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3846 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3849 HeapFree(GetProcessHeap(), 0, session->hostName);
3850 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3851 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3854 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3855 len = lstrlenW(hostName);
3856 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3857 session->hostName = heap_alloc(len*sizeof(WCHAR));
3858 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3861 session->hostName = heap_strdupW(hostName);
3863 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3865 HeapFree(GetProcessHeap(), 0, session->userName);
3866 session->userName = NULL;
3868 session->userName = heap_strdupW(userName);
3870 reset_data_stream(request);
3873 if(strcmpiW(session->serverName, hostName)) {
3874 HeapFree(GetProcessHeap(), 0, session->serverName);
3875 session->serverName = heap_strdupW(hostName);
3877 session->serverPort = urlComponents.nPort;
3881 HeapFree(GetProcessHeap(), 0, request->path);
3888 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3889 if (rc != E_POINTER)
3890 needed = strlenW(path)+1;
3891 request->path = heap_alloc(needed*sizeof(WCHAR));
3892 rc = UrlEscapeW(path, request->path, &needed,
3893 URL_ESCAPE_SPACES_ONLY);
3896 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3897 strcpyW(request->path,path);
3901 /* Remove custom content-type/length headers on redirects. */
3902 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3904 HTTP_DeleteCustomHeader(request, index);
3905 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3907 HTTP_DeleteCustomHeader(request, index);
3909 return ERROR_SUCCESS;
3912 /***********************************************************************
3913 * HTTP_build_req (internal)
3915 * concatenate all the strings in the request together
3917 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3922 for( t = list; *t ; t++ )
3923 len += strlenW( *t );
3926 str = heap_alloc(len*sizeof(WCHAR));
3929 for( t = list; *t ; t++ )
3935 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3938 LPWSTR requestString;
3944 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3945 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3946 http_session_t *session = request->session;
3950 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3951 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3952 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3953 HeapFree( GetProcessHeap(), 0, lpszPath );
3955 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3956 NULL, 0, NULL, NULL );
3957 len--; /* the nul terminator isn't needed */
3958 ascii_req = heap_alloc(len);
3959 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3960 ascii_req, len, NULL, NULL );
3961 HeapFree( GetProcessHeap(), 0, requestString );
3963 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3965 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3966 HeapFree( GetProcessHeap(), 0, ascii_req );
3967 if (res != ERROR_SUCCESS)
3970 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3972 return ERROR_HTTP_INVALID_HEADER;
3974 return ERROR_SUCCESS;
3977 static void HTTP_InsertCookies(http_request_t *request)
3979 DWORD cookie_size, size, cnt = 0;
3983 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3985 host = HTTP_GetHeader(request, hostW);
3989 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3992 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
3993 if(!(cookies = heap_alloc(size)))
3996 cnt += sprintfW(cookies, cookieW);
3997 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
3998 strcatW(cookies, szCrLf);
4000 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4005 static WORD HTTP_ParseDay(LPCWSTR day)
4007 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4015 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4016 if (!strcmpiW(day, days[i]))
4023 static WORD HTTP_ParseMonth(LPCWSTR month)
4025 static const WCHAR jan[] = { 'j','a','n',0 };
4026 static const WCHAR feb[] = { 'f','e','b',0 };
4027 static const WCHAR mar[] = { 'm','a','r',0 };
4028 static const WCHAR apr[] = { 'a','p','r',0 };
4029 static const WCHAR may[] = { 'm','a','y',0 };
4030 static const WCHAR jun[] = { 'j','u','n',0 };
4031 static const WCHAR jul[] = { 'j','u','l',0 };
4032 static const WCHAR aug[] = { 'a','u','g',0 };
4033 static const WCHAR sep[] = { 's','e','p',0 };
4034 static const WCHAR oct[] = { 'o','c','t',0 };
4035 static const WCHAR nov[] = { 'n','o','v',0 };
4036 static const WCHAR dec[] = { 'd','e','c',0 };
4038 if (!strcmpiW(month, jan)) return 1;
4039 if (!strcmpiW(month, feb)) return 2;
4040 if (!strcmpiW(month, mar)) return 3;
4041 if (!strcmpiW(month, apr)) return 4;
4042 if (!strcmpiW(month, may)) return 5;
4043 if (!strcmpiW(month, jun)) return 6;
4044 if (!strcmpiW(month, jul)) return 7;
4045 if (!strcmpiW(month, aug)) return 8;
4046 if (!strcmpiW(month, sep)) return 9;
4047 if (!strcmpiW(month, oct)) return 10;
4048 if (!strcmpiW(month, nov)) return 11;
4049 if (!strcmpiW(month, dec)) return 12;
4054 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4055 * optionally preceded by whitespace.
4056 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4057 * st, and sets *str to the first character after the time format.
4059 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4065 while (isspaceW(*ptr))
4068 num = strtoulW(ptr, &nextPtr, 10);
4069 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4071 ERR("unexpected time format %s\n", debugstr_w(ptr));
4076 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4080 st->wHour = (WORD)num;
4081 num = strtoulW(ptr, &nextPtr, 10);
4082 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4084 ERR("unexpected time format %s\n", debugstr_w(ptr));
4089 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4093 st->wMinute = (WORD)num;
4094 num = strtoulW(ptr, &nextPtr, 10);
4095 if (!nextPtr || nextPtr <= ptr)
4097 ERR("unexpected time format %s\n", debugstr_w(ptr));
4102 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4107 st->wSecond = (WORD)num;
4111 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4113 static const WCHAR gmt[]= { 'G','M','T',0 };
4114 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4116 SYSTEMTIME st = { 0 };
4119 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4120 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4123 st.wDayOfWeek = HTTP_ParseDay(day);
4124 if (st.wDayOfWeek >= 7)
4126 ERR("unexpected weekday %s\n", debugstr_w(day));
4130 while (isspaceW(*ptr))
4133 for (monthPtr = month; !isspace(*ptr) &&
4134 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4138 st.wMonth = HTTP_ParseMonth(month);
4139 if (!st.wMonth || st.wMonth > 12)
4141 ERR("unexpected month %s\n", debugstr_w(month));
4145 while (isspaceW(*ptr))
4148 num = strtoulW(ptr, &nextPtr, 10);
4149 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4151 ERR("unexpected day %s\n", debugstr_w(ptr));
4155 st.wDay = (WORD)num;
4157 while (isspaceW(*ptr))
4160 if (!HTTP_ParseTime(&st, &ptr))
4163 while (isspaceW(*ptr))
4166 num = strtoulW(ptr, &nextPtr, 10);
4167 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4169 ERR("unexpected year %s\n", debugstr_w(ptr));
4173 st.wYear = (WORD)num;
4175 while (isspaceW(*ptr))
4178 /* asctime() doesn't report a timezone, but some web servers do, so accept
4179 * with or without GMT.
4181 if (*ptr && strcmpW(ptr, gmt))
4183 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4186 return SystemTimeToFileTime(&st, ft);
4189 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4191 static const WCHAR gmt[]= { 'G','M','T',0 };
4192 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4195 SYSTEMTIME st = { 0 };
4197 ptr = strchrW(value, ',');
4200 if (ptr - value != 3)
4202 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4205 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4207 st.wDayOfWeek = HTTP_ParseDay(day);
4208 if (st.wDayOfWeek > 6)
4210 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4215 while (isspaceW(*ptr))
4218 num = strtoulW(ptr, &nextPtr, 10);
4219 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4221 ERR("unexpected day %s\n", debugstr_w(value));
4225 st.wDay = (WORD)num;
4227 while (isspaceW(*ptr))
4230 for (monthPtr = month; !isspace(*ptr) &&
4231 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4235 st.wMonth = HTTP_ParseMonth(month);
4236 if (!st.wMonth || st.wMonth > 12)
4238 ERR("unexpected month %s\n", debugstr_w(month));
4242 while (isspaceW(*ptr))
4245 num = strtoulW(ptr, &nextPtr, 10);
4246 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4248 ERR("unexpected year %s\n", debugstr_w(value));
4252 st.wYear = (WORD)num;
4254 if (!HTTP_ParseTime(&st, &ptr))
4257 while (isspaceW(*ptr))
4260 if (strcmpW(ptr, gmt))
4262 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4265 return SystemTimeToFileTime(&st, ft);
4268 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4269 * which may not be the only formats actually seen in the wild.
4270 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4271 * should be accepted as well.
4273 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4275 static const WCHAR zero[] = { '0',0 };
4278 if (!strcmpW(value, zero))
4280 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4283 else if (strchrW(value, ','))
4284 ret = HTTP_ParseRfc1123Date(value, ft);
4287 ret = HTTP_ParseDateAsAsctime(value, ft);
4289 ERR("unexpected date format %s\n", debugstr_w(value));
4294 static void HTTP_ProcessExpires(http_request_t *request)
4296 BOOL expirationFound = FALSE;
4299 /* Look for a Cache-Control header with a max-age directive, as it takes
4300 * precedence over the Expires header.
4302 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4303 if (headerIndex != -1)
4305 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4308 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4310 LPWSTR comma = strchrW(ptr, ','), end, equal;
4315 end = ptr + strlenW(ptr);
4316 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4320 static const WCHAR max_age[] = {
4321 'm','a','x','-','a','g','e',0 };
4323 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4328 age = strtoulW(equal + 1, &nextPtr, 10);
4329 if (nextPtr > equal + 1)
4333 NtQuerySystemTime( &ft );
4334 /* Age is in seconds, FILETIME resolution is in
4335 * 100 nanosecond intervals.
4337 ft.QuadPart += age * (ULONGLONG)1000000;
4338 request->expires.dwLowDateTime = ft.u.LowPart;
4339 request->expires.dwHighDateTime = ft.u.HighPart;
4340 expirationFound = TRUE;
4347 while (isspaceW(*ptr))
4354 if (!expirationFound)
4356 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4357 if (headerIndex != -1)
4359 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4362 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4364 expirationFound = TRUE;
4365 request->expires = ft;
4369 if (!expirationFound)
4373 /* With no known age, default to 10 minutes until expiration. */
4374 NtQuerySystemTime( &t );
4375 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4376 request->expires.dwLowDateTime = t.u.LowPart;
4377 request->expires.dwHighDateTime = t.u.HighPart;
4381 static void HTTP_ProcessLastModified(http_request_t *request)
4385 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4386 if (headerIndex != -1)
4388 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4391 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4392 request->last_modified = ft;
4396 static void http_process_keep_alive(http_request_t *req)
4400 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4402 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4404 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4407 static void HTTP_CacheRequest(http_request_t *request)
4409 WCHAR url[INTERNET_MAX_URL_LENGTH];
4410 WCHAR cacheFileName[MAX_PATH+1];
4413 b = HTTP_GetRequestURL(request, url);
4415 WARN("Could not get URL\n");
4419 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4421 HeapFree(GetProcessHeap(), 0, request->cacheFile);
4422 CloseHandle(request->hCacheFile);
4424 request->cacheFile = heap_strdupW(cacheFileName);
4425 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4426 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4427 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4428 WARN("Could not create file: %u\n", GetLastError());
4429 request->hCacheFile = NULL;
4432 WARN("Could not create cache entry: %08x\n", GetLastError());
4436 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4438 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4439 http_session_t *session = request->session;
4440 netconn_t *netconn = NULL;
4444 assert(!request->netconn);
4445 reset_data_stream(request);
4447 server = get_server(session->serverName, session->serverPort);
4449 return ERROR_OUTOFMEMORY;
4451 res = HTTP_ResolveName(request, server);
4452 if(res != ERROR_SUCCESS) {
4453 server_release(server);
4457 EnterCriticalSection(&connection_pool_cs);
4459 while(!list_empty(&server->conn_pool)) {
4460 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4461 list_remove(&netconn->pool_entry);
4463 if(NETCON_is_alive(netconn))
4466 TRACE("connection %p closed during idle\n", netconn);
4467 free_netconn(netconn);
4471 LeaveCriticalSection(&connection_pool_cs);
4474 TRACE("<-- reusing %p netconn\n", netconn);
4475 request->netconn = netconn;
4477 return ERROR_SUCCESS;
4480 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4481 INTERNET_STATUS_CONNECTING_TO_SERVER,
4483 strlen(server->addr_str)+1);
4485 res = create_netconn(is_https, server, request->security_flags, &netconn);
4486 server_release(server);
4487 if(res != ERROR_SUCCESS) {
4488 ERR("create_netconn failed: %u\n", res);
4492 request->netconn = netconn;
4494 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4495 INTERNET_STATUS_CONNECTED_TO_SERVER,
4496 server->addr_str, strlen(server->addr_str)+1);
4499 /* Note: we differ from Microsoft's WinINet here. they seem to have
4500 * a bug that causes no status callbacks to be sent when starting
4501 * a tunnel to a proxy server using the CONNECT verb. i believe our
4502 * behaviour to be more correct and to not cause any incompatibilities
4503 * because using a secure connection through a proxy server is a rare
4504 * case that would be hard for anyone to depend on */
4505 if(session->appInfo->proxy)
4506 res = HTTP_SecureProxyConnect(request);
4507 if(res == ERROR_SUCCESS)
4508 res = NETCON_secure_connect(request->netconn, session->hostName);
4509 if(res != ERROR_SUCCESS)
4511 WARN("Couldn't connect securely to host\n");
4513 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4514 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4515 || res == ERROR_INTERNET_INVALID_CA
4516 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4517 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4518 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4519 || res == ERROR_INTERNET_SEC_INVALID_CERT
4520 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4521 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4525 if(res != ERROR_SUCCESS) {
4526 http_release_netconn(request, FALSE);
4531 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4532 return ERROR_SUCCESS;
4535 /***********************************************************************
4536 * HTTP_HttpSendRequestW (internal)
4538 * Sends the specified request to the HTTP server
4541 * ERROR_SUCCESS on success
4542 * win32 error code on failure
4545 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4546 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4547 DWORD dwContentLength, BOOL bEndRequest)
4550 BOOL redirected = FALSE;
4551 LPWSTR requestString = NULL;
4554 INTERNET_ASYNC_RESULT iar;
4555 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4556 static const WCHAR szContentLength[] =
4557 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4558 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4561 TRACE("--> %p\n", request);
4563 assert(request->hdr.htype == WH_HHTTPREQ);
4565 /* if the verb is NULL default to GET */
4567 request->verb = heap_strdupW(szGET);
4569 if (dwContentLength || strcmpW(request->verb, szGET))
4571 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4572 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4573 request->bytesToWrite = dwContentLength;
4575 if (request->session->appInfo->agent)
4577 WCHAR *agent_header;
4578 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4581 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4582 agent_header = heap_alloc(len * sizeof(WCHAR));
4583 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4585 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4586 HeapFree(GetProcessHeap(), 0, agent_header);
4588 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4590 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4591 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4593 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4595 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4596 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4597 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4603 BOOL reusing_connection;
4608 /* like native, just in case the caller forgot to call InternetReadFile
4609 * for all the data */
4610 drain_content(request);
4612 request->contentLength = ~0u;
4613 request->bytesToWrite = 0;
4616 if (TRACE_ON(wininet))
4618 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4619 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4622 HTTP_FixURL(request);
4623 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4625 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4627 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4628 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4630 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4631 HTTP_InsertCookies(request);
4633 /* add the headers the caller supplied */
4634 if( lpszHeaders && dwHeaderLength )
4636 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4637 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4640 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4642 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4643 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4644 HeapFree(GetProcessHeap(), 0, url);
4647 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4650 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4652 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4655 /* send the request as ASCII, tack on the optional data */
4656 if (!lpOptional || redirected)
4657 dwOptionalLength = 0;
4658 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4659 NULL, 0, NULL, NULL );
4660 ascii_req = heap_alloc(len + dwOptionalLength);
4661 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4662 ascii_req, len, NULL, NULL );
4664 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4665 len = (len + dwOptionalLength - 1);
4667 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4669 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4670 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4672 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4673 HeapFree( GetProcessHeap(), 0, ascii_req );
4674 if(res != ERROR_SUCCESS) {
4675 TRACE("send failed: %u\n", res);
4676 if(!reusing_connection)
4678 http_release_netconn(request, FALSE);
4683 request->bytesWritten = dwOptionalLength;
4685 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4686 INTERNET_STATUS_REQUEST_SENT,
4687 &len, sizeof(DWORD));
4694 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4695 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4697 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4698 /* FIXME: We should know that connection is closed before sending
4699 * headers. Otherwise wrong callbacks are executed */
4700 if(!responseLen && reusing_connection) {
4701 TRACE("Connection closed by server, reconnecting\n");
4702 http_release_netconn(request, FALSE);
4707 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4708 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4711 http_process_keep_alive(request);
4712 HTTP_ProcessCookies(request);
4713 HTTP_ProcessExpires(request);
4714 HTTP_ProcessLastModified(request);
4716 dwBufferSize = sizeof(dwStatusCode);
4717 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4718 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4721 res = set_content_length(request, dwStatusCode);
4722 if(res != ERROR_SUCCESS)
4724 if(!request->contentLength)
4725 http_release_netconn(request, TRUE);
4727 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4729 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4730 dwBufferSize=sizeof(szNewLocation);
4731 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4732 dwStatusCode == HTTP_STATUS_MOVED ||
4733 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4734 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4735 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4737 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4739 HeapFree(GetProcessHeap(), 0, request->verb);
4740 request->verb = heap_strdupW(szGET);
4742 drain_content(request);
4743 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4745 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4746 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4747 res = HTTP_HandleRedirect(request, new_url);
4748 if (res == ERROR_SUCCESS)
4750 HeapFree(GetProcessHeap(), 0, requestString);
4753 HeapFree( GetProcessHeap(), 0, new_url );
4758 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4760 WCHAR szAuthValue[2048];
4762 if (dwStatusCode == HTTP_STATUS_DENIED)
4764 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4766 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4768 if (HTTP_DoAuthorization(request, szAuthValue,
4770 request->session->userName,
4771 request->session->password,
4774 HeapFree(GetProcessHeap(), 0, requestString);
4781 TRACE("Cleaning wrong authorization data\n");
4782 destroy_authinfo(request->authInfo);
4783 request->authInfo = NULL;
4786 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4789 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4791 if (HTTP_DoAuthorization(request, szAuthValue,
4792 &request->proxyAuthInfo,
4793 request->session->appInfo->proxyUsername,
4794 request->session->appInfo->proxyPassword,
4803 TRACE("Cleaning wrong proxy authorization data\n");
4804 destroy_authinfo(request->proxyAuthInfo);
4805 request->proxyAuthInfo = NULL;
4811 res = ERROR_SUCCESS;
4815 if(res == ERROR_SUCCESS)
4816 HTTP_CacheRequest(request);
4820 HeapFree(GetProcessHeap(), 0, requestString);
4822 /* TODO: send notification for P3P header */
4824 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4826 if (res == ERROR_SUCCESS && request->contentLength && request->bytesWritten == request->bytesToWrite)
4827 HTTP_ReceiveRequestData(request, TRUE);
4830 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4833 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4834 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4835 sizeof(INTERNET_ASYNC_RESULT));
4843 /***********************************************************************
4845 * Helper functions for the HttpSendRequest(Ex) functions
4848 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4850 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4851 http_request_t *request = (http_request_t*) workRequest->hdr;
4853 TRACE("%p\n", request);
4855 HTTP_HttpSendRequestW(request, req->lpszHeader,
4856 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4857 req->dwContentLength, req->bEndRequest);
4859 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
4863 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4866 DWORD dwCode, dwCodeLength;
4868 DWORD res = ERROR_SUCCESS;
4870 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4871 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4873 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4875 res = ERROR_HTTP_HEADER_NOT_FOUND;
4877 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4878 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4880 /* process cookies here. Is this right? */
4881 http_process_keep_alive(request);
4882 HTTP_ProcessCookies(request);
4883 HTTP_ProcessExpires(request);
4884 HTTP_ProcessLastModified(request);
4886 dwCodeLength = sizeof(dwCode);
4887 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4888 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
4891 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
4892 if(!request->contentLength)
4893 http_release_netconn(request, TRUE);
4896 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4898 if (dwCode == HTTP_STATUS_REDIRECT ||
4899 dwCode == HTTP_STATUS_MOVED ||
4900 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
4901 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
4903 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4904 dwBufferSize=sizeof(szNewLocation);
4905 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4907 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4909 HeapFree(GetProcessHeap(), 0, request->verb);
4910 request->verb = heap_strdupW(szGET);
4912 drain_content(request);
4913 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4915 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4916 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4917 res = HTTP_HandleRedirect(request, new_url);
4918 if (res == ERROR_SUCCESS)
4919 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4920 HeapFree( GetProcessHeap(), 0, new_url );
4926 if (res == ERROR_SUCCESS && request->contentLength) {
4927 HTTP_ReceiveRequestData(request, TRUE);
4929 INTERNET_ASYNC_RESULT iar = {0, res};
4931 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4932 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4933 sizeof(INTERNET_ASYNC_RESULT));
4939 /***********************************************************************
4940 * HttpEndRequestA (WININET.@)
4942 * Ends an HTTP request that was started by HttpSendRequestEx
4945 * TRUE if successful
4949 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4950 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4952 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4956 SetLastError(ERROR_INVALID_PARAMETER);
4960 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4963 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4965 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4966 http_request_t *request = (http_request_t*)work->hdr;
4968 TRACE("%p\n", request);
4970 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4973 /***********************************************************************
4974 * HttpEndRequestW (WININET.@)
4976 * Ends an HTTP request that was started by HttpSendRequestEx
4979 * TRUE if successful
4983 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4984 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4986 http_request_t *request;
4993 SetLastError(ERROR_INVALID_PARAMETER);
4997 request = (http_request_t*) get_handle_object( hRequest );
4999 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5001 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5003 WININET_Release( &request->hdr );
5006 request->hdr.dwFlags |= dwFlags;
5008 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5011 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5013 work.asyncproc = AsyncHttpEndRequestProc;
5014 work.hdr = WININET_AddRef( &request->hdr );
5016 work_endrequest = &work.u.HttpEndRequestW;
5017 work_endrequest->dwFlags = dwFlags;
5018 work_endrequest->dwContext = dwContext;
5020 INTERNET_AsyncCall(&work);
5021 res = ERROR_IO_PENDING;
5024 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5026 WININET_Release( &request->hdr );
5027 TRACE("%u <--\n", res);
5028 if(res != ERROR_SUCCESS)
5030 return res == ERROR_SUCCESS;
5033 /***********************************************************************
5034 * HttpSendRequestExA (WININET.@)
5036 * Sends the specified request to the HTTP server and allows chunked
5041 * Failure: FALSE, call GetLastError() for more information.
5043 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5044 LPINTERNET_BUFFERSA lpBuffersIn,
5045 LPINTERNET_BUFFERSA lpBuffersOut,
5046 DWORD dwFlags, DWORD_PTR dwContext)
5048 INTERNET_BUFFERSW BuffersInW;
5051 LPWSTR header = NULL;
5053 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5054 lpBuffersOut, dwFlags, dwContext);
5058 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5059 if (lpBuffersIn->lpcszHeader)
5061 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5062 lpBuffersIn->dwHeadersLength,0,0);
5063 header = heap_alloc(headerlen*sizeof(WCHAR));
5064 if (!(BuffersInW.lpcszHeader = header))
5066 SetLastError(ERROR_OUTOFMEMORY);
5069 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5070 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5074 BuffersInW.lpcszHeader = NULL;
5075 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5076 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5077 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5078 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5079 BuffersInW.Next = NULL;
5082 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5084 HeapFree(GetProcessHeap(),0,header);
5089 /***********************************************************************
5090 * HttpSendRequestExW (WININET.@)
5092 * Sends the specified request to the HTTP server and allows chunked
5097 * Failure: FALSE, call GetLastError() for more information.
5099 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5100 LPINTERNET_BUFFERSW lpBuffersIn,
5101 LPINTERNET_BUFFERSW lpBuffersOut,
5102 DWORD dwFlags, DWORD_PTR dwContext)
5104 http_request_t *request;
5105 http_session_t *session;
5109 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5110 lpBuffersOut, dwFlags, dwContext);
5112 request = (http_request_t*) get_handle_object( hRequest );
5114 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5116 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5120 session = request->session;
5121 assert(session->hdr.htype == WH_HHTTPSESSION);
5122 hIC = session->appInfo;
5123 assert(hIC->hdr.htype == WH_HINIT);
5125 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5127 WORKREQUEST workRequest;
5128 struct WORKREQ_HTTPSENDREQUESTW *req;
5130 workRequest.asyncproc = AsyncHttpSendRequestProc;
5131 workRequest.hdr = WININET_AddRef( &request->hdr );
5132 req = &workRequest.u.HttpSendRequestW;
5137 if (lpBuffersIn->lpcszHeader)
5139 if (lpBuffersIn->dwHeadersLength == ~0u)
5140 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5142 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5144 req->lpszHeader = heap_alloc(size);
5145 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5147 else req->lpszHeader = NULL;
5149 req->dwHeaderLength = size / sizeof(WCHAR);
5150 req->lpOptional = lpBuffersIn->lpvBuffer;
5151 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5152 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5156 req->lpszHeader = NULL;
5157 req->dwHeaderLength = 0;
5158 req->lpOptional = NULL;
5159 req->dwOptionalLength = 0;
5160 req->dwContentLength = 0;
5163 req->bEndRequest = FALSE;
5165 INTERNET_AsyncCall(&workRequest);
5167 * This is from windows.
5169 res = ERROR_IO_PENDING;
5174 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5175 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5176 lpBuffersIn->dwBufferTotal, FALSE);
5178 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5183 WININET_Release( &request->hdr );
5187 return res == ERROR_SUCCESS;
5190 /***********************************************************************
5191 * HttpSendRequestW (WININET.@)
5193 * Sends the specified request to the HTTP server
5200 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5201 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5203 http_request_t *request;
5204 http_session_t *session = NULL;
5205 appinfo_t *hIC = NULL;
5206 DWORD res = ERROR_SUCCESS;
5208 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5209 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5211 request = (http_request_t*) get_handle_object( hHttpRequest );
5212 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5214 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5218 session = request->session;
5219 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5221 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5225 hIC = session->appInfo;
5226 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5228 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5232 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5234 WORKREQUEST workRequest;
5235 struct WORKREQ_HTTPSENDREQUESTW *req;
5237 workRequest.asyncproc = AsyncHttpSendRequestProc;
5238 workRequest.hdr = WININET_AddRef( &request->hdr );
5239 req = &workRequest.u.HttpSendRequestW;
5244 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5245 else size = dwHeaderLength * sizeof(WCHAR);
5247 req->lpszHeader = heap_alloc(size);
5248 memcpy(req->lpszHeader, lpszHeaders, size);
5251 req->lpszHeader = 0;
5252 req->dwHeaderLength = dwHeaderLength;
5253 req->lpOptional = lpOptional;
5254 req->dwOptionalLength = dwOptionalLength;
5255 req->dwContentLength = dwOptionalLength;
5256 req->bEndRequest = TRUE;
5258 INTERNET_AsyncCall(&workRequest);
5260 * This is from windows.
5262 res = ERROR_IO_PENDING;
5266 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5267 dwHeaderLength, lpOptional, dwOptionalLength,
5268 dwOptionalLength, TRUE);
5272 WININET_Release( &request->hdr );
5275 return res == ERROR_SUCCESS;
5278 /***********************************************************************
5279 * HttpSendRequestA (WININET.@)
5281 * Sends the specified request to the HTTP server
5288 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5289 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5292 LPWSTR szHeaders=NULL;
5293 DWORD nLen=dwHeaderLength;
5294 if(lpszHeaders!=NULL)
5296 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5297 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5298 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5300 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5301 HeapFree(GetProcessHeap(),0,szHeaders);
5305 /***********************************************************************
5306 * HTTPSESSION_Destroy (internal)
5308 * Deallocate session handle
5311 static void HTTPSESSION_Destroy(object_header_t *hdr)
5313 http_session_t *session = (http_session_t*) hdr;
5315 TRACE("%p\n", session);
5317 WININET_Release(&session->appInfo->hdr);
5319 HeapFree(GetProcessHeap(), 0, session->hostName);
5320 HeapFree(GetProcessHeap(), 0, session->serverName);
5321 HeapFree(GetProcessHeap(), 0, session->password);
5322 HeapFree(GetProcessHeap(), 0, session->userName);
5325 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5328 case INTERNET_OPTION_HANDLE_TYPE:
5329 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5331 if (*size < sizeof(ULONG))
5332 return ERROR_INSUFFICIENT_BUFFER;
5334 *size = sizeof(DWORD);
5335 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5336 return ERROR_SUCCESS;
5339 return INET_QueryOption(hdr, option, buffer, size, unicode);
5342 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5344 http_session_t *ses = (http_session_t*)hdr;
5347 case INTERNET_OPTION_USERNAME:
5349 HeapFree(GetProcessHeap(), 0, ses->userName);
5350 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5351 return ERROR_SUCCESS;
5353 case INTERNET_OPTION_PASSWORD:
5355 HeapFree(GetProcessHeap(), 0, ses->password);
5356 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5357 return ERROR_SUCCESS;
5362 return ERROR_INTERNET_INVALID_OPTION;
5365 static const object_vtbl_t HTTPSESSIONVtbl = {
5366 HTTPSESSION_Destroy,
5368 HTTPSESSION_QueryOption,
5369 HTTPSESSION_SetOption,
5378 /***********************************************************************
5379 * HTTP_Connect (internal)
5381 * Create http session handle
5384 * HINTERNET a session handle on success
5388 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5389 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5390 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5391 DWORD dwInternalFlags, HINTERNET *ret)
5393 http_session_t *session = NULL;
5397 if (!lpszServerName || !lpszServerName[0])
5398 return ERROR_INVALID_PARAMETER;
5400 assert( hIC->hdr.htype == WH_HINIT );
5402 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5404 return ERROR_OUTOFMEMORY;
5407 * According to my tests. The name is not resolved until a request is sent
5410 session->hdr.htype = WH_HHTTPSESSION;
5411 session->hdr.dwFlags = dwFlags;
5412 session->hdr.dwContext = dwContext;
5413 session->hdr.dwInternalFlags |= dwInternalFlags;
5415 WININET_AddRef( &hIC->hdr );
5416 session->appInfo = hIC;
5417 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5419 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5420 if(hIC->proxyBypass)
5421 FIXME("Proxy bypass is ignored.\n");
5423 session->serverName = heap_strdupW(lpszServerName);
5424 session->hostName = heap_strdupW(lpszServerName);
5425 if (lpszUserName && lpszUserName[0])
5426 session->userName = heap_strdupW(lpszUserName);
5427 if (lpszPassword && lpszPassword[0])
5428 session->password = heap_strdupW(lpszPassword);
5429 session->serverPort = serverPort;
5430 session->hostPort = serverPort;
5432 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5433 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5435 INTERNET_SendCallback(&hIC->hdr, dwContext,
5436 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5441 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5445 TRACE("%p --> %p\n", hIC, session);
5447 *ret = session->hdr.hInternet;
5448 return ERROR_SUCCESS;
5451 /***********************************************************************
5452 * HTTP_clear_response_headers (internal)
5454 * clear out any old response headers
5456 static void HTTP_clear_response_headers( http_request_t *request )
5460 for( i=0; i<request->nCustHeaders; i++)
5462 if( !request->custHeaders[i].lpszField )
5464 if( !request->custHeaders[i].lpszValue )
5466 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5468 HTTP_DeleteCustomHeader( request, i );
5473 /***********************************************************************
5474 * HTTP_GetResponseHeaders (internal)
5476 * Read server response
5483 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5486 WCHAR buffer[MAX_REPLY_LEN];
5487 DWORD buflen = MAX_REPLY_LEN;
5488 BOOL bSuccess = FALSE;
5490 char bufferA[MAX_REPLY_LEN];
5491 LPWSTR status_code = NULL, status_text = NULL;
5492 DWORD cchMaxRawHeaders = 1024;
5493 LPWSTR lpszRawHeaders = NULL;
5495 DWORD cchRawHeaders = 0;
5496 BOOL codeHundred = FALSE;
5500 if(!request->netconn)
5504 static const WCHAR szHundred[] = {'1','0','0',0};
5506 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5508 buflen = MAX_REPLY_LEN;
5509 if (!read_line(request, bufferA, &buflen))
5512 /* clear old response headers (eg. from a redirect response) */
5514 HTTP_clear_response_headers( request );
5519 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5520 /* check is this a status code line? */
5521 if (!strncmpW(buffer, g_szHttp1_0, 4))
5523 /* split the version from the status code */
5524 status_code = strchrW( buffer, ' ' );
5529 /* split the status code from the status text */
5530 status_text = strchrW( status_code, ' ' );
5535 TRACE("version [%s] status code [%s] status text [%s]\n",
5536 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5538 codeHundred = (!strcmpW(status_code, szHundred));
5540 else if (!codeHundred)
5542 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5544 HeapFree(GetProcessHeap(), 0, request->version);
5545 HeapFree(GetProcessHeap(), 0, request->statusText);
5547 request->version = heap_strdupW(g_szHttp1_0);
5548 request->statusText = heap_strdupW(szOK);
5550 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5551 request->rawHeaders = heap_strdupW(szDefaultHeader);
5556 } while (codeHundred);
5558 /* Add status code */
5559 HTTP_ProcessHeader(request, szStatus, status_code,
5560 HTTP_ADDHDR_FLAG_REPLACE);
5562 HeapFree(GetProcessHeap(),0,request->version);
5563 HeapFree(GetProcessHeap(),0,request->statusText);
5565 request->version = heap_strdupW(buffer);
5566 request->statusText = heap_strdupW(status_text);
5568 /* Restore the spaces */
5569 *(status_code-1) = ' ';
5570 *(status_text-1) = ' ';
5572 /* regenerate raw headers */
5573 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5574 if (!lpszRawHeaders) goto lend;
5576 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5577 cchMaxRawHeaders *= 2;
5578 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5579 if (temp == NULL) goto lend;
5580 lpszRawHeaders = temp;
5581 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5582 cchRawHeaders += (buflen-1);
5583 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5584 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5585 lpszRawHeaders[cchRawHeaders] = '\0';
5587 /* Parse each response line */
5590 buflen = MAX_REPLY_LEN;
5591 if (read_line(request, bufferA, &buflen))
5593 LPWSTR * pFieldAndValue;
5595 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5597 if (!bufferA[0]) break;
5598 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5600 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5603 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5604 cchMaxRawHeaders *= 2;
5605 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5606 if (temp == NULL) goto lend;
5607 lpszRawHeaders = temp;
5608 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5609 cchRawHeaders += (buflen-1);
5610 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5611 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5612 lpszRawHeaders[cchRawHeaders] = '\0';
5614 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5615 HTTP_ADDREQ_FLAG_ADD );
5617 HTTP_FreeTokens(pFieldAndValue);
5628 /* make sure the response header is terminated with an empty line. Some apps really
5629 truly care about that empty line being there for some reason. Just add it to the
5631 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5633 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5634 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5635 if (temp == NULL) goto lend;
5636 lpszRawHeaders = temp;
5639 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5641 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5642 request->rawHeaders = lpszRawHeaders;
5643 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5653 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
5658 /***********************************************************************
5659 * HTTP_InterpretHttpHeader (internal)
5661 * Parse server response
5665 * Pointer to array of field, value, NULL on success.
5668 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5670 LPWSTR * pTokenPair;
5674 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5676 pszColon = strchrW(buffer, ':');
5677 /* must have two tokens */
5680 HTTP_FreeTokens(pTokenPair);
5682 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5686 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5689 HTTP_FreeTokens(pTokenPair);
5692 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5693 pTokenPair[0][pszColon - buffer] = '\0';
5697 len = strlenW(pszColon);
5698 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5701 HTTP_FreeTokens(pTokenPair);
5704 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5706 strip_spaces(pTokenPair[0]);
5707 strip_spaces(pTokenPair[1]);
5709 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5713 /***********************************************************************
5714 * HTTP_ProcessHeader (internal)
5716 * Stuff header into header tables according to <dwModifier>
5720 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5722 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5724 LPHTTPHEADERW lphttpHdr = NULL;
5726 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5727 DWORD res = ERROR_HTTP_INVALID_HEADER;
5729 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5731 /* REPLACE wins out over ADD */
5732 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5733 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5735 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5738 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5742 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5743 return ERROR_HTTP_INVALID_HEADER;
5744 lphttpHdr = &request->custHeaders[index];
5750 hdr.lpszField = (LPWSTR)field;
5751 hdr.lpszValue = (LPWSTR)value;
5752 hdr.wFlags = hdr.wCount = 0;
5754 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5755 hdr.wFlags |= HDR_ISREQUEST;
5757 return HTTP_InsertCustomHeader(request, &hdr);
5759 /* no value to delete */
5760 else return ERROR_SUCCESS;
5762 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5763 lphttpHdr->wFlags |= HDR_ISREQUEST;
5765 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5767 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5769 HTTP_DeleteCustomHeader( request, index );
5775 hdr.lpszField = (LPWSTR)field;
5776 hdr.lpszValue = (LPWSTR)value;
5777 hdr.wFlags = hdr.wCount = 0;
5779 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5780 hdr.wFlags |= HDR_ISREQUEST;
5782 return HTTP_InsertCustomHeader(request, &hdr);
5785 return ERROR_SUCCESS;
5787 else if (dwModifier & COALESCEFLAGS)
5792 INT origlen = strlenW(lphttpHdr->lpszValue);
5793 INT valuelen = strlenW(value);
5795 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5798 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5800 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5803 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5806 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5808 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5811 lphttpHdr->lpszValue = lpsztmp;
5812 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5815 lphttpHdr->lpszValue[origlen] = ch;
5817 lphttpHdr->lpszValue[origlen] = ' ';
5821 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5822 lphttpHdr->lpszValue[len] = '\0';
5823 res = ERROR_SUCCESS;
5827 WARN("heap_realloc (%d bytes) failed\n",len+1);
5828 res = ERROR_OUTOFMEMORY;
5831 TRACE("<-- %d\n", res);
5835 /***********************************************************************
5836 * HTTP_GetCustomHeaderIndex (internal)
5838 * Return index of custom header from header array
5841 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5842 int requested_index, BOOL request_only)
5846 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5848 for (index = 0; index < request->nCustHeaders; index++)
5850 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5853 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5856 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5859 if (requested_index == 0)
5864 if (index >= request->nCustHeaders)
5867 TRACE("Return: %d\n", index);
5872 /***********************************************************************
5873 * HTTP_InsertCustomHeader (internal)
5875 * Insert header into array
5878 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5881 LPHTTPHEADERW lph = NULL;
5883 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5884 count = request->nCustHeaders + 1;
5886 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5888 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5891 return ERROR_OUTOFMEMORY;
5893 request->custHeaders = lph;
5894 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5895 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5896 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5897 request->custHeaders[count-1].wCount= lpHdr->wCount;
5898 request->nCustHeaders++;
5900 return ERROR_SUCCESS;
5904 /***********************************************************************
5905 * HTTP_DeleteCustomHeader (internal)
5907 * Delete header from array
5908 * If this function is called, the indexs may change.
5910 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5912 if( request->nCustHeaders <= 0 )
5914 if( index >= request->nCustHeaders )
5916 request->nCustHeaders--;
5918 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszField);
5919 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszValue);
5921 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5922 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5923 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5929 /***********************************************************************
5930 * HTTP_VerifyValidHeader (internal)
5932 * Verify the given header is not invalid for the given http request
5935 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5937 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5938 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5939 return ERROR_HTTP_INVALID_HEADER;
5941 return ERROR_SUCCESS;
5944 /***********************************************************************
5945 * IsHostInProxyBypassList (@)
5950 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5952 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5956 /***********************************************************************
5957 * InternetShowSecurityInfoByURLA (@)
5959 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5961 FIXME("stub: %s %p\n", url, window);
5965 /***********************************************************************
5966 * InternetShowSecurityInfoByURLW (@)
5968 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5970 FIXME("stub: %s %p\n", debugstr_w(url), window);