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
69 #include "cryptuiapi.h"
72 #include "wine/debug.h"
73 #include "wine/exception.h"
74 #include "wine/unicode.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
78 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
79 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
80 static const WCHAR szOK[] = {'O','K',0};
81 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
82 static const WCHAR hostW[] = { 'H','o','s','t',0 };
83 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
85 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
86 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
87 static const WCHAR szGET[] = { 'G','E','T', 0 };
88 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
89 static const WCHAR szCrLf[] = {'\r','\n', 0};
91 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
92 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
93 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
94 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
95 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
96 static const WCHAR szAge[] = { 'A','g','e',0 };
97 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
98 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
99 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
100 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
101 static const WCHAR szContent_Disposition[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
102 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
104 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
105 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
106 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
107 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
108 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
109 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 };
110 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
111 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
112 static const WCHAR szDate[] = { 'D','a','t','e',0 };
113 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
114 static const WCHAR szETag[] = { 'E','T','a','g',0 };
115 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
116 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
117 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
118 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
120 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
121 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
122 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
123 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
124 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
125 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
126 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
127 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
128 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
129 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
130 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
131 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
132 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
133 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
134 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
135 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
136 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 };
137 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
138 static const WCHAR szURI[] = { 'U','R','I',0 };
139 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
140 static const WCHAR szVary[] = { 'V','a','r','y',0 };
141 static const WCHAR szVia[] = { 'V','i','a',0 };
142 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
143 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
145 #define HTTP_REFERER szReferer
146 #define HTTP_ACCEPT szAccept
147 #define HTTP_USERAGENT szUser_Agent
149 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
150 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
151 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
154 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
155 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157 #define COLLECT_TIME 60000
159 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
170 unsigned int auth_data_len;
171 BOOL finished; /* finished authenticating */
175 typedef struct _basicAuthorizationData
182 UINT authorizationLen;
183 } basicAuthorizationData;
185 typedef struct _authorizationData
199 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
200 static struct list authorizationCache = LIST_INIT(authorizationCache);
202 static CRITICAL_SECTION authcache_cs;
203 static CRITICAL_SECTION_DEBUG critsect_debug =
206 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
207 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
209 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
211 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
212 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
213 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
214 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
215 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
216 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
217 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
218 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
219 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
220 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
221 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
223 static CRITICAL_SECTION connection_pool_cs;
224 static CRITICAL_SECTION_DEBUG connection_pool_debug =
226 0, 0, &connection_pool_cs,
227 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
228 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
230 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
232 static struct list connection_pool = LIST_INIT(connection_pool);
233 static BOOL collector_running;
235 void server_addref(server_t *server)
237 InterlockedIncrement(&server->ref);
240 void server_release(server_t *server)
242 if(InterlockedDecrement(&server->ref))
246 server->keep_until = GetTickCount64() + COLLECT_TIME;
249 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
251 server_t *iter, *server = NULL;
253 EnterCriticalSection(&connection_pool_cs);
255 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
256 if(iter->port == port && !strcmpW(iter->name, name)) {
258 server_addref(server);
264 server = heap_alloc(sizeof(*server));
266 server->addr_len = 0;
269 list_init(&server->conn_pool);
270 server->name = heap_strdupW(name);
272 list_add_head(&connection_pool, &server->entry);
280 LeaveCriticalSection(&connection_pool_cs);
285 BOOL collect_connections(BOOL collect_all)
287 netconn_t *netconn, *netconn_safe;
288 server_t *server, *server_safe;
289 BOOL remaining = FALSE;
292 now = GetTickCount64();
294 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
295 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
296 if(collect_all || netconn->keep_until < now) {
297 TRACE("freeing %p\n", netconn);
298 list_remove(&netconn->pool_entry);
299 free_netconn(netconn);
306 if(collect_all || server->keep_until < now) {
307 list_remove(&server->entry);
309 heap_free(server->name);
320 static DWORD WINAPI collect_connections_proc(void *arg)
322 BOOL remaining_conns;
325 /* FIXME: Use more sophisticated method */
328 EnterCriticalSection(&connection_pool_cs);
330 remaining_conns = collect_connections(FALSE);
332 collector_running = FALSE;
334 LeaveCriticalSection(&connection_pool_cs);
335 }while(remaining_conns);
337 FreeLibraryAndExitThread(WININET_hModule, 0);
340 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
343 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
344 if (HeaderIndex == -1)
347 return &req->custHeaders[HeaderIndex];
356 struct data_stream_vtbl_t {
357 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
358 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
359 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
360 BOOL (*drain_content)(data_stream_t*,http_request_t*);
361 void (*destroy)(data_stream_t*);
365 data_stream_t data_stream;
367 BYTE buf[READ_BUFFER_SIZE];
373 static inline void destroy_data_stream(data_stream_t *stream)
375 stream->vtbl->destroy(stream);
378 static void reset_data_stream(http_request_t *req)
380 destroy_data_stream(req->data_stream);
381 req->data_stream = &req->netconn_stream.data_stream;
382 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
383 req->read_chunked = req->read_gzip = FALSE;
389 data_stream_t stream;
390 data_stream_t *parent_stream;
392 BYTE buf[READ_BUFFER_SIZE];
398 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
400 /* Allow reading only from read buffer */
404 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
406 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
407 return gzip_stream->end_of_data;
410 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
411 DWORD *read, read_mode_t read_mode)
413 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
414 z_stream *zstream = &gzip_stream->zstream;
415 DWORD current_read, ret_read = 0;
418 DWORD res = ERROR_SUCCESS;
420 while(size && !gzip_stream->end_of_data) {
421 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
423 if(gzip_stream->buf_size <= 64 && !end) {
424 if(gzip_stream->buf_pos) {
425 if(gzip_stream->buf_size)
426 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
427 gzip_stream->buf_pos = 0;
429 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
430 sizeof(gzip_stream->buf)-gzip_stream->buf_size, ¤t_read, read_mode);
431 gzip_stream->buf_size += current_read;
432 if(res != ERROR_SUCCESS)
434 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
435 if(!current_read && !end) {
436 if(read_mode != READMODE_NOBLOCK) {
437 WARN("unexpected end of data\n");
438 gzip_stream->end_of_data = TRUE;
442 if(gzip_stream->buf_size <= 64 && !end)
446 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
447 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
448 zstream->next_out = buf+ret_read;
449 zstream->avail_out = size;
450 zres = inflate(&gzip_stream->zstream, 0);
451 current_read = size - zstream->avail_out;
452 size -= current_read;
453 ret_read += current_read;
454 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
455 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
456 if(zres == Z_STREAM_END) {
457 TRACE("end of data\n");
458 gzip_stream->end_of_data = TRUE;
460 }else if(zres != Z_OK) {
461 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
463 res = ERROR_INTERNET_DECODING_FAILED;
467 if(ret_read && read_mode == READMODE_ASYNC)
468 read_mode = READMODE_NOBLOCK;
471 TRACE("read %u bytes\n", ret_read);
476 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
478 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
479 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
482 static void gzip_destroy(data_stream_t *stream)
484 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
486 destroy_data_stream(gzip_stream->parent_stream);
488 if(!gzip_stream->end_of_data)
489 inflateEnd(&gzip_stream->zstream);
490 heap_free(gzip_stream);
493 static const data_stream_vtbl_t gzip_stream_vtbl = {
501 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
503 return heap_alloc(items*size);
506 static void wininet_zfree(voidpf opaque, voidpf address)
511 static DWORD init_gzip_stream(http_request_t *req)
513 gzip_stream_t *gzip_stream;
516 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
518 return ERROR_OUTOFMEMORY;
520 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
521 gzip_stream->zstream.zalloc = wininet_zalloc;
522 gzip_stream->zstream.zfree = wininet_zfree;
524 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
526 ERR("inflateInit failed: %d\n", zres);
527 heap_free(gzip_stream);
528 return ERROR_OUTOFMEMORY;
531 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
533 HTTP_DeleteCustomHeader(req, index);
536 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
537 gzip_stream->buf_size = req->read_size;
538 req->read_pos = req->read_size = 0;
541 req->read_gzip = TRUE;
542 gzip_stream->parent_stream = req->data_stream;
543 req->data_stream = &gzip_stream->stream;
544 return ERROR_SUCCESS;
549 static DWORD init_gzip_stream(http_request_t *req)
551 ERR("gzip stream not supported, missing zlib.\n");
552 return ERROR_SUCCESS;
557 /***********************************************************************
558 * HTTP_Tokenize (internal)
560 * Tokenize a string, allocating memory for the tokens.
562 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
564 LPWSTR * token_array;
571 /* empty string has no tokens */
575 for (i = 0; string[i]; i++)
577 if (!strncmpW(string+i, token_string, strlenW(token_string)))
581 /* we want to skip over separators, but not the null terminator */
582 for (j = 0; j < strlenW(token_string) - 1; j++)
590 /* add 1 for terminating NULL */
591 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
592 token_array[tokens] = NULL;
595 for (i = 0; i < tokens; i++)
598 next_token = strstrW(string, token_string);
599 if (!next_token) next_token = string+strlenW(string);
600 len = next_token - string;
601 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
602 memcpy(token_array[i], string, len*sizeof(WCHAR));
603 token_array[i][len] = '\0';
604 string = next_token+strlenW(token_string);
609 /***********************************************************************
610 * HTTP_FreeTokens (internal)
612 * Frees memory returned from HTTP_Tokenize.
614 static void HTTP_FreeTokens(LPWSTR * token_array)
617 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
618 heap_free(token_array);
621 static void HTTP_FixURL(http_request_t *request)
623 static const WCHAR szSlash[] = { '/',0 };
624 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
626 /* If we don't have a path we set it to root */
627 if (NULL == request->path)
628 request->path = heap_strdupW(szSlash);
629 else /* remove \r and \n*/
631 int nLen = strlenW(request->path);
632 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
635 request->path[nLen]='\0';
637 /* Replace '\' with '/' */
640 if (request->path[nLen] == '\\') request->path[nLen]='/';
644 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
645 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
646 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
648 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
650 strcpyW(fixurl + 1, request->path);
651 heap_free( request->path );
652 request->path = fixurl;
656 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
658 LPWSTR requestString;
664 static const WCHAR szSpace[] = { ' ',0 };
665 static const WCHAR szColon[] = { ':',' ',0 };
666 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
668 /* allocate space for an array of all the string pointers to be added */
669 len = (request->nCustHeaders)*4 + 10;
670 req = heap_alloc(len*sizeof(LPCWSTR));
672 /* add the verb, path and HTTP version string */
680 /* Append custom request headers */
681 for (i = 0; i < request->nCustHeaders; i++)
683 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
686 req[n++] = request->custHeaders[i].lpszField;
688 req[n++] = request->custHeaders[i].lpszValue;
690 TRACE("Adding custom header %s (%s)\n",
691 debugstr_w(request->custHeaders[i].lpszField),
692 debugstr_w(request->custHeaders[i].lpszValue));
697 ERR("oops. buffer overrun\n");
700 requestString = HTTP_build_req( req, 4 );
704 * Set (header) termination string for request
705 * Make sure there's exactly two new lines at the end of the request
707 p = &requestString[strlenW(requestString)-1];
708 while ( (*p == '\n') || (*p == '\r') )
710 strcpyW( p+1, sztwocrlf );
712 return requestString;
715 static void HTTP_ProcessCookies( http_request_t *request )
719 LPHTTPHEADERW setCookieHeader;
721 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
724 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
730 setCookieHeader = &request->custHeaders[HeaderIndex];
732 if (!setCookieHeader->lpszValue)
735 host = HTTP_GetHeader(request, hostW);
739 data = strchrW(setCookieHeader->lpszValue, '=');
743 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
748 set_cookie(host->lpszValue, request->path, name, data);
753 static void strip_spaces(LPWSTR start)
758 while (*str == ' ' && *str != '\0')
762 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
764 end = start + strlenW(start) - 1;
765 while (end >= start && *end == ' ')
772 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
774 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
775 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
777 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
778 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
779 if (is_basic && pszRealm)
782 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
786 token = strchrW(ptr,'=');
790 while (*realm == ' ' && *realm != '\0')
792 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
793 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
796 while (*token == ' ' && *token != '\0')
800 *pszRealm = heap_strdupW(token);
801 strip_spaces(*pszRealm);
808 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
810 if (!authinfo) return;
812 if (SecIsValidHandle(&authinfo->ctx))
813 DeleteSecurityContext(&authinfo->ctx);
814 if (SecIsValidHandle(&authinfo->cred))
815 FreeCredentialsHandle(&authinfo->cred);
817 heap_free(authinfo->auth_data);
818 heap_free(authinfo->scheme);
822 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
824 basicAuthorizationData *ad;
827 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
829 EnterCriticalSection(&authcache_cs);
830 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
832 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
834 TRACE("Authorization found in cache\n");
835 *auth_data = heap_alloc(ad->authorizationLen);
836 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
837 rc = ad->authorizationLen;
841 LeaveCriticalSection(&authcache_cs);
845 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
848 basicAuthorizationData* ad = NULL;
850 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
852 EnterCriticalSection(&authcache_cs);
853 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
855 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
856 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
865 TRACE("Found match in cache, replacing\n");
866 heap_free(ad->authorization);
867 ad->authorization = heap_alloc(auth_data_len);
868 memcpy(ad->authorization, auth_data, auth_data_len);
869 ad->authorizationLen = auth_data_len;
873 ad = heap_alloc(sizeof(basicAuthorizationData));
874 ad->host = heap_strdupW(host);
875 ad->realm = heap_strdupW(realm);
876 ad->authorization = heap_alloc(auth_data_len);
877 memcpy(ad->authorization, auth_data, auth_data_len);
878 ad->authorizationLen = auth_data_len;
879 list_add_head(&basicAuthorizationCache,&ad->entry);
880 TRACE("authorization cached\n");
882 LeaveCriticalSection(&authcache_cs);
885 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
886 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
888 authorizationData *ad;
890 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
892 EnterCriticalSection(&authcache_cs);
893 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
894 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
895 TRACE("Authorization found in cache\n");
897 nt_auth_identity->User = heap_strdupW(ad->user);
898 nt_auth_identity->Password = heap_strdupW(ad->password);
899 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
900 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
901 (!nt_auth_identity->Domain && ad->domain_len)) {
902 heap_free(nt_auth_identity->User);
903 heap_free(nt_auth_identity->Password);
904 heap_free(nt_auth_identity->Domain);
908 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
909 nt_auth_identity->UserLength = ad->user_len;
910 nt_auth_identity->PasswordLength = ad->password_len;
911 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
912 nt_auth_identity->DomainLength = ad->domain_len;
913 LeaveCriticalSection(&authcache_cs);
917 LeaveCriticalSection(&authcache_cs);
922 static void cache_authorization(LPWSTR host, LPWSTR scheme,
923 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
925 authorizationData *ad;
928 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
930 EnterCriticalSection(&authcache_cs);
931 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
932 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
939 heap_free(ad->password);
940 heap_free(ad->domain);
942 ad = heap_alloc(sizeof(authorizationData));
944 LeaveCriticalSection(&authcache_cs);
948 ad->host = heap_strdupW(host);
949 ad->scheme = heap_strdupW(scheme);
950 list_add_head(&authorizationCache, &ad->entry);
953 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
954 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
955 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
956 ad->user_len = nt_auth_identity->UserLength;
957 ad->password_len = nt_auth_identity->PasswordLength;
958 ad->domain_len = nt_auth_identity->DomainLength;
960 if(!ad->host || !ad->scheme || !ad->user || !ad->password
961 || (nt_auth_identity->Domain && !ad->domain)) {
963 heap_free(ad->scheme);
965 heap_free(ad->password);
966 heap_free(ad->domain);
967 list_remove(&ad->entry);
971 LeaveCriticalSection(&authcache_cs);
974 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
975 struct HttpAuthInfo **ppAuthInfo,
976 LPWSTR domain_and_username, LPWSTR password,
979 SECURITY_STATUS sec_status;
980 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
982 LPWSTR szRealm = NULL;
984 TRACE("%s\n", debugstr_w(pszAuthValue));
991 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
995 SecInvalidateHandle(&pAuthInfo->cred);
996 SecInvalidateHandle(&pAuthInfo->ctx);
997 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
999 pAuthInfo->auth_data = NULL;
1000 pAuthInfo->auth_data_len = 0;
1001 pAuthInfo->finished = FALSE;
1003 if (is_basic_auth_value(pszAuthValue,NULL))
1005 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1006 pAuthInfo->scheme = heap_strdupW(szBasic);
1007 if (!pAuthInfo->scheme)
1009 heap_free(pAuthInfo);
1016 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1018 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1019 if (!pAuthInfo->scheme)
1021 heap_free(pAuthInfo);
1025 if (domain_and_username)
1027 WCHAR *user = strchrW(domain_and_username, '\\');
1028 WCHAR *domain = domain_and_username;
1030 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1032 pAuthData = &nt_auth_identity;
1037 user = domain_and_username;
1041 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1042 nt_auth_identity.User = user;
1043 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1044 nt_auth_identity.Domain = domain;
1045 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1046 nt_auth_identity.Password = password;
1047 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1049 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1051 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1052 pAuthData = &nt_auth_identity;
1054 /* use default credentials */
1057 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1058 SECPKG_CRED_OUTBOUND, NULL,
1060 NULL, &pAuthInfo->cred,
1063 if(pAuthData && !domain_and_username) {
1064 heap_free(nt_auth_identity.User);
1065 heap_free(nt_auth_identity.Domain);
1066 heap_free(nt_auth_identity.Password);
1069 if (sec_status == SEC_E_OK)
1071 PSecPkgInfoW sec_pkg_info;
1072 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1073 if (sec_status == SEC_E_OK)
1075 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1076 FreeContextBuffer(sec_pkg_info);
1079 if (sec_status != SEC_E_OK)
1081 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1082 debugstr_w(pAuthInfo->scheme), sec_status);
1083 heap_free(pAuthInfo->scheme);
1084 heap_free(pAuthInfo);
1088 *ppAuthInfo = pAuthInfo;
1090 else if (pAuthInfo->finished)
1093 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1094 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1096 ERR("authentication scheme changed from %s to %s\n",
1097 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1101 if (is_basic_auth_value(pszAuthValue,&szRealm))
1105 char *auth_data = NULL;
1106 UINT auth_data_len = 0;
1108 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1110 if (!domain_and_username)
1112 if (host && szRealm)
1113 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1114 if (auth_data_len == 0)
1122 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1123 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1125 /* length includes a nul terminator, which will be re-used for the ':' */
1126 auth_data = heap_alloc(userlen + 1 + passlen);
1133 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1134 auth_data[userlen] = ':';
1135 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1136 auth_data_len = userlen + 1 + passlen;
1137 if (host && szRealm)
1138 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1141 pAuthInfo->auth_data = auth_data;
1142 pAuthInfo->auth_data_len = auth_data_len;
1143 pAuthInfo->finished = TRUE;
1149 LPCWSTR pszAuthData;
1150 SecBufferDesc out_desc, in_desc;
1152 unsigned char *buffer;
1153 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1154 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1156 in.BufferType = SECBUFFER_TOKEN;
1160 in_desc.ulVersion = 0;
1161 in_desc.cBuffers = 1;
1162 in_desc.pBuffers = ∈
1164 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1165 if (*pszAuthData == ' ')
1168 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1169 in.pvBuffer = heap_alloc(in.cbBuffer);
1170 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1173 buffer = heap_alloc(pAuthInfo->max_token);
1175 out.BufferType = SECBUFFER_TOKEN;
1176 out.cbBuffer = pAuthInfo->max_token;
1177 out.pvBuffer = buffer;
1179 out_desc.ulVersion = 0;
1180 out_desc.cBuffers = 1;
1181 out_desc.pBuffers = &out;
1183 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1184 first ? NULL : &pAuthInfo->ctx,
1185 first ? request->session->serverName : NULL,
1186 context_req, 0, SECURITY_NETWORK_DREP,
1187 in.pvBuffer ? &in_desc : NULL,
1188 0, &pAuthInfo->ctx, &out_desc,
1189 &pAuthInfo->attr, &pAuthInfo->exp);
1190 if (sec_status == SEC_E_OK)
1192 pAuthInfo->finished = TRUE;
1193 pAuthInfo->auth_data = out.pvBuffer;
1194 pAuthInfo->auth_data_len = out.cbBuffer;
1195 TRACE("sending last auth packet\n");
1197 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1199 pAuthInfo->auth_data = out.pvBuffer;
1200 pAuthInfo->auth_data_len = out.cbBuffer;
1201 TRACE("sending next auth packet\n");
1205 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1206 heap_free(out.pvBuffer);
1207 destroy_authinfo(pAuthInfo);
1216 /***********************************************************************
1217 * HTTP_HttpAddRequestHeadersW (internal)
1219 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1220 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1225 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1227 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1229 if( dwHeaderLength == ~0U )
1230 len = strlenW(lpszHeader);
1232 len = dwHeaderLength;
1233 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1234 lstrcpynW( buffer, lpszHeader, len + 1);
1240 LPWSTR * pFieldAndValue;
1242 lpszEnd = lpszStart;
1244 while (*lpszEnd != '\0')
1246 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1251 if (*lpszStart == '\0')
1254 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1257 lpszEnd++; /* Jump over newline */
1259 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1260 if (*lpszStart == '\0')
1262 /* Skip 0-length headers */
1263 lpszStart = lpszEnd;
1264 res = ERROR_SUCCESS;
1267 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1270 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1271 if (res == ERROR_SUCCESS)
1272 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1273 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1274 HTTP_FreeTokens(pFieldAndValue);
1277 lpszStart = lpszEnd;
1278 } while (res == ERROR_SUCCESS);
1284 /***********************************************************************
1285 * HttpAddRequestHeadersW (WININET.@)
1287 * Adds one or more HTTP header to the request handler
1290 * On Windows if dwHeaderLength includes the trailing '\0', then
1291 * HttpAddRequestHeadersW() adds it too. However this results in an
1292 * invalid HTTP header which is rejected by some servers so we probably
1293 * don't need to match Windows on that point.
1300 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1301 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1303 http_request_t *request;
1304 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1306 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1311 request = (http_request_t*) get_handle_object( hHttpRequest );
1312 if (request && request->hdr.htype == WH_HHTTPREQ)
1313 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1315 WININET_Release( &request->hdr );
1317 if(res != ERROR_SUCCESS)
1319 return res == ERROR_SUCCESS;
1322 /***********************************************************************
1323 * HttpAddRequestHeadersA (WININET.@)
1325 * Adds one or more HTTP header to the request handler
1332 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1333 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1339 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1341 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1342 hdr = heap_alloc(len*sizeof(WCHAR));
1343 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1344 if( dwHeaderLength != ~0U )
1345 dwHeaderLength = len;
1347 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1353 static void free_accept_types( WCHAR **accept_types )
1355 WCHAR *ptr, **types = accept_types;
1358 while ((ptr = *types))
1363 heap_free( accept_types );
1366 static WCHAR **convert_accept_types( const char **accept_types )
1369 const char **types = accept_types;
1371 BOOL invalid_pointer = FALSE;
1373 if (!types) return NULL;
1379 /* find out how many there are */
1380 if (*types && **types)
1382 TRACE("accept type: %s\n", debugstr_a(*types));
1388 WARN("invalid accept type pointer\n");
1389 invalid_pointer = TRUE;
1394 if (invalid_pointer) return NULL;
1395 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1397 types = accept_types;
1400 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1403 typesW[count] = NULL;
1407 /***********************************************************************
1408 * HttpOpenRequestA (WININET.@)
1410 * Open a HTTP request handle
1413 * HINTERNET a HTTP request handle on success
1417 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1418 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1419 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1420 DWORD dwFlags, DWORD_PTR dwContext)
1422 LPWSTR szVerb = NULL, szObjectName = NULL;
1423 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1424 HINTERNET rc = FALSE;
1426 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1427 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1428 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1429 dwFlags, dwContext);
1433 szVerb = heap_strdupAtoW(lpszVerb);
1440 szObjectName = heap_strdupAtoW(lpszObjectName);
1441 if ( !szObjectName )
1447 szVersion = heap_strdupAtoW(lpszVersion);
1454 szReferrer = heap_strdupAtoW(lpszReferrer);
1459 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1460 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1461 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1464 free_accept_types(szAcceptTypes);
1465 heap_free(szReferrer);
1466 heap_free(szVersion);
1467 heap_free(szObjectName);
1472 /***********************************************************************
1475 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1478 static const CHAR HTTP_Base64Enc[] =
1479 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1483 /* first 6 bits, all from bin[0] */
1484 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1485 x = (bin[0] & 3) << 4;
1487 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1490 base64[n++] = HTTP_Base64Enc[x];
1495 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1496 x = ( bin[1] & 0x0f ) << 2;
1498 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1501 base64[n++] = HTTP_Base64Enc[x];
1505 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1507 /* last 6 bits, all from bin [2] */
1508 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1516 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1517 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1518 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1519 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1520 static const signed char HTTP_Base64Dec[256] =
1522 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1523 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1524 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1525 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1526 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1527 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1528 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1529 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1530 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1531 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1532 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1533 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1534 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1535 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1536 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1537 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1538 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1539 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1540 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1541 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1542 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1543 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1544 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1545 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1546 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1547 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1551 /***********************************************************************
1554 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1562 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1563 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1564 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1565 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1567 WARN("invalid base64: %s\n", debugstr_w(base64));
1571 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1574 if ((base64[2] == '=') && (base64[3] == '='))
1576 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1577 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1579 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1583 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1586 if (base64[3] == '=')
1588 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1589 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1591 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1595 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1604 /***********************************************************************
1605 * HTTP_InsertAuthorization
1607 * Insert or delete the authorization field in the request header.
1609 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1613 static const WCHAR wszSpace[] = {' ',0};
1614 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1616 WCHAR *authorization = NULL;
1618 if (pAuthInfo->auth_data_len)
1620 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1621 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1622 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1626 strcpyW(authorization, pAuthInfo->scheme);
1627 strcatW(authorization, wszSpace);
1628 HTTP_EncodeBase64(pAuthInfo->auth_data,
1629 pAuthInfo->auth_data_len,
1630 authorization+strlenW(authorization));
1632 /* clear the data as it isn't valid now that it has been sent to the
1633 * server, unless it's Basic authentication which doesn't do
1634 * connection tracking */
1635 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1637 heap_free(pAuthInfo->auth_data);
1638 pAuthInfo->auth_data = NULL;
1639 pAuthInfo->auth_data_len = 0;
1643 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1645 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1646 heap_free(authorization);
1651 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1653 static const WCHAR slash[] = { '/',0 };
1654 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1655 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1656 http_session_t *session = req->session;
1657 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1660 size = sizeof(new_location);
1661 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1663 URL_COMPONENTSW UrlComponents;
1665 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1666 strcpyW( url, new_location );
1668 ZeroMemory(&UrlComponents,sizeof(URL_COMPONENTSW));
1669 if(InternetCrackUrlW(url, 0, 0, &UrlComponents)) goto done;
1673 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1674 size += strlenW( session->hostName ) + strlenW( req->path );
1676 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1678 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1679 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1681 sprintfW( url, format, session->hostName, session->hostPort );
1682 if (req->path[0] != '/') strcatW( url, slash );
1683 strcatW( url, req->path );
1686 TRACE("url=%s\n", debugstr_w(url));
1690 /***********************************************************************
1691 * HTTP_DealWithProxy
1693 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1695 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1696 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1697 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1698 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1699 static WCHAR szNul[] = { 0 };
1700 URL_COMPONENTSW UrlComponents;
1701 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1702 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1703 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1705 memset( &UrlComponents, 0, sizeof UrlComponents );
1706 UrlComponents.dwStructSize = sizeof UrlComponents;
1707 UrlComponents.lpszHostName = buf;
1708 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1710 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1712 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1713 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1714 sprintfW(proxy, szFormat, protoProxy);
1716 strcpyW(proxy, protoProxy);
1717 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1719 if( UrlComponents.dwHostNameLength == 0 )
1722 if( !request->path )
1723 request->path = szNul;
1725 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1726 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1728 heap_free(session->serverName);
1729 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1730 session->serverPort = UrlComponents.nPort;
1732 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1736 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
1741 if(server->addr_len)
1742 return ERROR_SUCCESS;
1744 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1745 INTERNET_STATUS_RESOLVING_NAME,
1747 (strlenW(server->name)+1) * sizeof(WCHAR));
1749 addr_len = sizeof(server->addr);
1750 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1751 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1753 switch(server->addr.ss_family) {
1755 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1758 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1761 WARN("unsupported family %d\n", server->addr.ss_family);
1762 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1765 server->addr_len = addr_len;
1766 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1767 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1768 INTERNET_STATUS_NAME_RESOLVED,
1769 server->addr_str, strlen(server->addr_str)+1);
1771 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1772 return ERROR_SUCCESS;
1775 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1777 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1778 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1779 static const WCHAR slash[] = { '/',0 };
1780 LPHTTPHEADERW host_header;
1783 host_header = HTTP_GetHeader(req, hostW);
1787 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1791 strcpyW(buf, scheme);
1792 strcatW(buf, host_header->lpszValue);
1793 if (req->path[0] != '/')
1794 strcatW(buf, slash);
1795 strcatW(buf, req->path);
1800 /***********************************************************************
1801 * HTTPREQ_Destroy (internal)
1803 * Deallocate request handle
1806 static void HTTPREQ_Destroy(object_header_t *hdr)
1808 http_request_t *request = (http_request_t*) hdr;
1813 if(request->hCacheFile) {
1814 WCHAR url[INTERNET_MAX_URL_LENGTH];
1816 CloseHandle(request->hCacheFile);
1818 if(HTTP_GetRequestURL(request, url)) {
1821 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1822 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1823 request->last_modified, NORMAL_CACHE_ENTRY,
1824 request->rawHeaders, headersLen, NULL, 0);
1827 heap_free(request->cacheFile);
1829 request->read_section.DebugInfo->Spare[0] = 0;
1830 DeleteCriticalSection( &request->read_section );
1831 WININET_Release(&request->session->hdr);
1833 destroy_authinfo(request->authInfo);
1834 destroy_authinfo(request->proxyAuthInfo);
1836 heap_free(request->path);
1837 heap_free(request->verb);
1838 heap_free(request->rawHeaders);
1839 heap_free(request->version);
1840 heap_free(request->statusText);
1842 for (i = 0; i < request->nCustHeaders; i++)
1844 heap_free(request->custHeaders[i].lpszField);
1845 heap_free(request->custHeaders[i].lpszValue);
1847 destroy_data_stream(request->data_stream);
1848 heap_free(request->custHeaders);
1851 static void http_release_netconn(http_request_t *req, BOOL reuse)
1853 TRACE("%p %p\n",req, req->netconn);
1858 if(reuse && req->netconn->keep_alive) {
1861 EnterCriticalSection(&connection_pool_cs);
1863 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1864 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1865 req->netconn = NULL;
1867 run_collector = !collector_running;
1868 collector_running = TRUE;
1870 LeaveCriticalSection(&connection_pool_cs);
1873 HANDLE thread = NULL;
1876 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1878 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1880 EnterCriticalSection(&connection_pool_cs);
1881 collector_running = FALSE;
1882 LeaveCriticalSection(&connection_pool_cs);
1885 FreeLibrary(module);
1888 CloseHandle(thread);
1893 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1894 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1896 free_netconn(req->netconn);
1897 req->netconn = NULL;
1899 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1900 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1903 static void drain_content(http_request_t *req)
1907 if (!req->netconn) return;
1909 if (req->contentLength == -1)
1911 else if(!strcmpW(req->verb, szHEAD))
1914 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1916 http_release_netconn(req, try_reuse);
1919 static BOOL HTTP_KeepAlive(http_request_t *request)
1921 WCHAR szVersion[10];
1922 WCHAR szConnectionResponse[20];
1923 DWORD dwBufferSize = sizeof(szVersion);
1924 BOOL keepalive = FALSE;
1926 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1927 * the connection is keep-alive by default */
1928 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1929 && !strcmpiW(szVersion, g_szHttp1_1))
1934 dwBufferSize = sizeof(szConnectionResponse);
1935 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1936 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1938 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1944 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1946 http_request_t *req = (http_request_t*)hdr;
1951 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1953 http_request_t *req = (http_request_t*)hdr;
1956 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1958 http_session_t *session = req->session;
1959 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1961 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1963 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1964 return ERROR_INSUFFICIENT_BUFFER;
1965 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1966 /* FIXME: can't get a SOCKET from our connection since we don't use
1970 /* FIXME: get source port from req->netConnection */
1971 info->SourcePort = 0;
1972 info->DestPort = session->hostPort;
1974 if (HTTP_KeepAlive(req))
1975 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1976 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1977 info->Flags |= IDSI_FLAG_PROXY;
1978 if (req->netconn->useSSL)
1979 info->Flags |= IDSI_FLAG_SECURE;
1981 return ERROR_SUCCESS;
1984 case INTERNET_OPTION_SECURITY_FLAGS:
1988 if (*size < sizeof(ULONG))
1989 return ERROR_INSUFFICIENT_BUFFER;
1991 *size = sizeof(DWORD);
1993 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1994 flags |= SECURITY_FLAG_SECURE;
1995 flags |= req->security_flags;
1997 int bits = NETCON_GetCipherStrength(req->netconn);
1999 flags |= SECURITY_FLAG_STRENGTH_STRONG;
2000 else if (bits >= 56)
2001 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
2003 flags |= SECURITY_FLAG_STRENGTH_WEAK;
2005 *(DWORD *)buffer = flags;
2006 return ERROR_SUCCESS;
2009 case INTERNET_OPTION_HANDLE_TYPE:
2010 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2012 if (*size < sizeof(ULONG))
2013 return ERROR_INSUFFICIENT_BUFFER;
2015 *size = sizeof(DWORD);
2016 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2017 return ERROR_SUCCESS;
2019 case INTERNET_OPTION_URL: {
2020 WCHAR url[INTERNET_MAX_URL_LENGTH];
2025 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2027 TRACE("INTERNET_OPTION_URL\n");
2029 host = HTTP_GetHeader(req, hostW);
2030 strcpyW(url, httpW);
2031 strcatW(url, host->lpszValue);
2032 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2034 strcatW(url, req->path);
2036 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2039 len = (strlenW(url)+1) * sizeof(WCHAR);
2041 return ERROR_INSUFFICIENT_BUFFER;
2044 strcpyW(buffer, url);
2045 return ERROR_SUCCESS;
2047 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2049 return ERROR_INSUFFICIENT_BUFFER;
2052 return ERROR_SUCCESS;
2056 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2057 INTERNET_CACHE_ENTRY_INFOW *info;
2058 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2059 WCHAR url[INTERNET_MAX_URL_LENGTH];
2060 DWORD nbytes, error;
2063 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2065 if (*size < sizeof(*ts))
2067 *size = sizeof(*ts);
2068 return ERROR_INSUFFICIENT_BUFFER;
2071 HTTP_GetRequestURL(req, url);
2072 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2073 error = GetLastError();
2074 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2076 if (!(info = heap_alloc(nbytes)))
2077 return ERROR_OUTOFMEMORY;
2079 GetUrlCacheEntryInfoW(url, info, &nbytes);
2081 ts->ftExpires = info->ExpireTime;
2082 ts->ftLastModified = info->LastModifiedTime;
2085 *size = sizeof(*ts);
2086 return ERROR_SUCCESS;
2091 case INTERNET_OPTION_DATAFILE_NAME: {
2094 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2096 if(!req->cacheFile) {
2098 return ERROR_INTERNET_ITEM_NOT_FOUND;
2102 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2103 if(*size < req_size)
2104 return ERROR_INSUFFICIENT_BUFFER;
2107 memcpy(buffer, req->cacheFile, *size);
2108 return ERROR_SUCCESS;
2110 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2111 if (req_size > *size)
2112 return ERROR_INSUFFICIENT_BUFFER;
2114 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2115 -1, buffer, *size, NULL, NULL);
2116 return ERROR_SUCCESS;
2120 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2121 PCCERT_CONTEXT context;
2123 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2124 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2125 return ERROR_INSUFFICIENT_BUFFER;
2128 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2130 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2133 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2134 info->ftExpiry = context->pCertInfo->NotAfter;
2135 info->ftStart = context->pCertInfo->NotBefore;
2136 len = CertNameToStrA(context->dwCertEncodingType,
2137 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2138 info->lpszSubjectInfo = LocalAlloc(0, len);
2139 if(info->lpszSubjectInfo)
2140 CertNameToStrA(context->dwCertEncodingType,
2141 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2142 info->lpszSubjectInfo, len);
2143 len = CertNameToStrA(context->dwCertEncodingType,
2144 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2145 info->lpszIssuerInfo = LocalAlloc(0, len);
2146 if(info->lpszIssuerInfo)
2147 CertNameToStrA(context->dwCertEncodingType,
2148 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2149 info->lpszIssuerInfo, len);
2150 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2151 CertFreeCertificateContext(context);
2152 return ERROR_SUCCESS;
2155 case INTERNET_OPTION_CONNECT_TIMEOUT:
2156 if (*size < sizeof(DWORD))
2157 return ERROR_INSUFFICIENT_BUFFER;
2159 *size = sizeof(DWORD);
2160 *(DWORD *)buffer = req->connect_timeout;
2161 return ERROR_SUCCESS;
2162 case INTERNET_OPTION_REQUEST_FLAGS:
2163 TRACE("INTERNET_OPTION_REQUEST_FLAGS\n");
2165 if (*size < sizeof(DWORD))
2166 return ERROR_INSUFFICIENT_BUFFER;
2168 *(DWORD*)buffer = 4;
2169 *size = sizeof(DWORD);
2171 return ERROR_SUCCESS;
2174 return INET_QueryOption(hdr, option, buffer, size, unicode);
2177 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2179 http_request_t *req = (http_request_t*)hdr;
2182 case INTERNET_OPTION_SECURITY_FLAGS:
2186 if (!buffer || size != sizeof(DWORD))
2187 return ERROR_INVALID_PARAMETER;
2188 flags = *(DWORD *)buffer;
2189 TRACE("%08x\n", flags);
2190 req->security_flags = flags;
2192 req->netconn->security_flags = flags;
2193 return ERROR_SUCCESS;
2195 case INTERNET_OPTION_CONNECT_TIMEOUT:
2196 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2197 req->connect_timeout = *(DWORD *)buffer;
2198 return ERROR_SUCCESS;
2200 case INTERNET_OPTION_SEND_TIMEOUT:
2201 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2202 req->send_timeout = *(DWORD *)buffer;
2203 return ERROR_SUCCESS;
2205 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2206 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2207 req->receive_timeout = *(DWORD *)buffer;
2208 return ERROR_SUCCESS;
2210 case INTERNET_OPTION_USERNAME:
2211 heap_free(req->session->userName);
2212 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2213 return ERROR_SUCCESS;
2215 case INTERNET_OPTION_PASSWORD:
2216 heap_free(req->session->password);
2217 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2218 return ERROR_SUCCESS;
2219 case INTERNET_OPTION_HTTP_DECODING:
2220 if(size != sizeof(BOOL))
2221 return ERROR_INVALID_PARAMETER;
2222 req->decoding = *(BOOL*)buffer;
2223 return ERROR_SUCCESS;
2226 return INET_SetOption(hdr, option, buffer, size);
2229 /* read some more data into the read buffer (the read section must be held) */
2230 static DWORD read_more_data( http_request_t *req, int maxlen )
2237 /* move existing data to the start of the buffer */
2239 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2243 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2245 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2246 maxlen - req->read_size, 0, &len );
2247 if(res == ERROR_SUCCESS)
2248 req->read_size += len;
2253 /* remove some amount of data from the read buffer (the read section must be held) */
2254 static void remove_data( http_request_t *req, int count )
2256 if (!(req->read_size -= count)) req->read_pos = 0;
2257 else req->read_pos += count;
2260 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2262 int count, bytes_read, pos = 0;
2265 EnterCriticalSection( &req->read_section );
2268 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2272 count = eol - (req->read_buf + req->read_pos);
2273 bytes_read = count + 1;
2275 else count = bytes_read = req->read_size;
2277 count = min( count, *len - pos );
2278 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2280 remove_data( req, bytes_read );
2283 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2286 TRACE( "returning empty string %u\n", res);
2287 LeaveCriticalSection( &req->read_section );
2288 INTERNET_SetLastError(res);
2292 LeaveCriticalSection( &req->read_section );
2296 if (pos && buffer[pos - 1] == '\r') pos--;
2299 buffer[*len - 1] = 0;
2300 TRACE( "returning %s\n", debugstr_a(buffer));
2304 /* check if we have reached the end of the data to read (the read section must be held) */
2305 static BOOL end_of_read_data( http_request_t *req )
2307 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2310 /* fetch some more data into the read buffer (the read section must be held) */
2311 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2315 if(req->read_size == sizeof(req->read_buf))
2316 return ERROR_SUCCESS;
2320 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2324 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2325 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2326 req->read_size += read;
2328 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2334 /* return the size of data available to be read immediately (the read section must be held) */
2335 static DWORD get_avail_data( http_request_t *req )
2337 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2340 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2342 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2346 NETCON_query_data_available(req->netconn, &avail);
2347 return netconn_stream->content_length == ~0u
2349 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2352 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2354 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2355 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2358 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2359 DWORD *read, read_mode_t read_mode)
2361 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2364 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2366 if(read_mode == READMODE_NOBLOCK)
2367 size = min(size, netconn_get_avail_data(stream, req));
2369 if(size && req->netconn) {
2370 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2373 netconn_stream->content_length = netconn_stream->content_read;
2376 netconn_stream->content_read += *read = len;
2377 TRACE("read %u bytes\n", len);
2378 return ERROR_SUCCESS;
2381 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2383 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2388 if(netconn_end_of_data(stream, req))
2392 avail = netconn_get_avail_data(stream, req);
2396 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2399 netconn_stream->content_read += len;
2400 }while(netconn_stream->content_read < netconn_stream->content_length);
2405 static void netconn_destroy(data_stream_t *stream)
2409 static const data_stream_vtbl_t netconn_stream_vtbl = {
2410 netconn_get_avail_data,
2411 netconn_end_of_data,
2413 netconn_drain_content,
2417 /* read some more data into the read buffer (the read section must be held) */
2418 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2423 if (stream->buf_pos)
2425 /* move existing data to the start of the buffer */
2426 if(stream->buf_size)
2427 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2428 stream->buf_pos = 0;
2431 if (maxlen == -1) maxlen = sizeof(stream->buf);
2433 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2434 maxlen - stream->buf_size, 0, &len );
2435 if(res == ERROR_SUCCESS)
2436 stream->buf_size += len;
2441 /* remove some amount of data from the read buffer (the read section must be held) */
2442 static void remove_chunked_data(chunked_stream_t *stream, int count)
2444 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2445 else stream->buf_pos += count;
2448 /* discard data contents until we reach end of line (the read section must be held) */
2449 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2455 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2458 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2461 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2462 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2463 } while (stream->buf_size);
2464 return ERROR_SUCCESS;
2467 /* read the size of the next chunk (the read section must be held) */
2468 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2471 DWORD chunk_size = 0, res;
2473 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2478 while (stream->buf_size)
2480 char ch = stream->buf[stream->buf_pos];
2481 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2482 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2483 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2484 else if (ch == ';' || ch == '\r' || ch == '\n')
2486 TRACE( "reading %u byte chunk\n", chunk_size );
2487 stream->chunk_size = chunk_size;
2488 req->contentLength += chunk_size;
2489 return discard_chunked_eol(stream, req);
2491 remove_chunked_data(stream, 1);
2493 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2494 if (!stream->buf_size)
2496 stream->chunk_size = 0;
2497 return ERROR_SUCCESS;
2502 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2504 /* Allow reading only from read buffer */
2508 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2510 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2511 return !chunked_stream->chunk_size;
2514 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2515 DWORD *read, read_mode_t read_mode)
2517 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2518 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2520 if(chunked_stream->chunk_size == ~0u) {
2521 res = start_next_chunk(chunked_stream, req);
2522 if(res != ERROR_SUCCESS)
2526 while(size && chunked_stream->chunk_size) {
2527 if(chunked_stream->buf_size) {
2528 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2530 /* this could block */
2531 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2534 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2535 remove_chunked_data(chunked_stream, read_bytes);
2537 read_bytes = min(size, chunked_stream->chunk_size);
2539 if(read_mode == READMODE_NOBLOCK) {
2542 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2544 if(read_bytes > avail)
2547 /* this could block */
2548 if(read_bytes == chunked_stream->chunk_size)
2552 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2553 if(res != ERROR_SUCCESS)
2557 chunked_stream->chunk_size -= read_bytes;
2559 ret_read += read_bytes;
2560 if(!chunked_stream->chunk_size) {
2561 assert(read_mode != READMODE_NOBLOCK);
2562 res = start_next_chunk(chunked_stream, req);
2563 if(res != ERROR_SUCCESS)
2567 if(read_mode == READMODE_ASYNC)
2568 read_mode = READMODE_NOBLOCK;
2571 TRACE("read %u bytes\n", ret_read);
2576 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2578 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2580 /* FIXME: we can do better */
2581 return !chunked_stream->chunk_size;
2584 static void chunked_destroy(data_stream_t *stream)
2586 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2587 heap_free(chunked_stream);
2590 static const data_stream_vtbl_t chunked_stream_vtbl = {
2591 chunked_get_avail_data,
2592 chunked_end_of_data,
2594 chunked_drain_content,
2598 /* set the request content length based on the headers */
2599 static DWORD set_content_length(http_request_t *request)
2601 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2605 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2606 request->contentLength = request->netconn_stream.content_length = 0;
2607 return ERROR_SUCCESS;
2610 size = sizeof(request->contentLength);
2611 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2612 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2613 request->contentLength = ~0u;
2614 request->netconn_stream.content_length = request->contentLength;
2615 request->netconn_stream.content_read = request->read_size;
2617 size = sizeof(encoding);
2618 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2619 !strcmpiW(encoding, szChunked))
2621 chunked_stream_t *chunked_stream;
2623 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2625 return ERROR_OUTOFMEMORY;
2627 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2628 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2629 chunked_stream->chunk_size = ~0u;
2631 if(request->read_size) {
2632 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2633 chunked_stream->buf_size = request->read_size;
2634 request->read_size = request->read_pos = 0;
2637 request->data_stream = &chunked_stream->data_stream;
2638 request->contentLength = ~0u;
2639 request->read_chunked = TRUE;
2642 if(request->decoding) {
2645 static const WCHAR gzipW[] = {'g','z','i','p',0};
2647 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2648 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2649 return init_gzip_stream(request);
2652 return ERROR_SUCCESS;
2655 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2657 INTERNET_ASYNC_RESULT iar;
2659 iar.dwResult = result;
2660 iar.dwError = error;
2662 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2663 sizeof(INTERNET_ASYNC_RESULT));
2666 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2668 DWORD res, read = 0, avail = 0;
2673 EnterCriticalSection( &req->read_section );
2675 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2676 res = refill_read_buffer(req, mode, &read);
2677 if(res == ERROR_SUCCESS && !first_notif)
2678 avail = get_avail_data(req);
2680 LeaveCriticalSection( &req->read_section );
2682 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2683 WARN("res %u read %u, closing connection\n", res, read);
2684 http_release_netconn(req, FALSE);
2687 if(res == ERROR_SUCCESS)
2688 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2690 send_request_complete(req, 0, res);
2693 /* read data from the http connection (the read section must be held) */
2694 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2696 DWORD current_read = 0, ret_read = 0;
2697 read_mode_t read_mode;
2698 DWORD res = ERROR_SUCCESS;
2700 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2702 EnterCriticalSection( &req->read_section );
2704 if(req->read_size) {
2705 ret_read = min(size, req->read_size);
2706 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2707 req->read_size -= ret_read;
2708 req->read_pos += ret_read;
2709 if(read_mode == READMODE_ASYNC)
2710 read_mode = READMODE_NOBLOCK;
2713 if(ret_read < size) {
2714 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2715 ret_read += current_read;
2718 LeaveCriticalSection( &req->read_section );
2721 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2723 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2727 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2729 WARN("WriteFile failed: %u\n", GetLastError());
2732 if(size && !ret_read)
2733 http_release_netconn(req, res == ERROR_SUCCESS);
2739 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2741 http_request_t *req = (http_request_t*)hdr;
2744 EnterCriticalSection( &req->read_section );
2745 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2746 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2748 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2749 if(res == ERROR_SUCCESS)
2751 LeaveCriticalSection( &req->read_section );
2756 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2758 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2759 http_request_t *req = (http_request_t*)workRequest->hdr;
2762 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2764 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2765 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2767 send_request_complete(req, res == ERROR_SUCCESS, res);
2770 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2771 DWORD flags, DWORD_PTR context)
2773 http_request_t *req = (http_request_t*)hdr;
2774 DWORD res, size, read, error = ERROR_SUCCESS;
2776 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2777 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2779 if (buffers->dwStructSize != sizeof(*buffers))
2780 return ERROR_INVALID_PARAMETER;
2782 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2784 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2786 WORKREQUEST workRequest;
2788 if (TryEnterCriticalSection( &req->read_section ))
2790 if (get_avail_data(req))
2792 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2793 &buffers->dwBufferLength, FALSE);
2794 size = buffers->dwBufferLength;
2795 LeaveCriticalSection( &req->read_section );
2798 LeaveCriticalSection( &req->read_section );
2801 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2802 workRequest.hdr = WININET_AddRef(&req->hdr);
2803 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2805 INTERNET_AsyncCall(&workRequest);
2807 return ERROR_IO_PENDING;
2811 size = buffers->dwBufferLength;
2813 EnterCriticalSection( &req->read_section );
2814 if(hdr->dwError == ERROR_SUCCESS)
2815 hdr->dwError = INTERNET_HANDLE_IN_USE;
2816 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2817 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2820 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2821 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2822 if(res != ERROR_SUCCESS)
2825 read += buffers->dwBufferLength;
2826 if(read == size || end_of_read_data(req))
2829 LeaveCriticalSection( &req->read_section );
2831 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2832 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2833 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2834 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2836 EnterCriticalSection( &req->read_section );
2839 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2840 hdr->dwError = ERROR_SUCCESS;
2842 error = hdr->dwError;
2844 LeaveCriticalSection( &req->read_section );
2845 size = buffers->dwBufferLength;
2846 buffers->dwBufferLength = read;
2849 if (res == ERROR_SUCCESS) {
2850 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2851 &size, sizeof(size));
2854 return res==ERROR_SUCCESS ? error : res;
2857 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2859 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2860 http_request_t *req = (http_request_t*)workRequest->hdr;
2863 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2865 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2866 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2868 send_request_complete(req, res == ERROR_SUCCESS, res);
2871 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2872 DWORD flags, DWORD_PTR context)
2875 http_request_t *req = (http_request_t*)hdr;
2876 DWORD res, size, read, error = ERROR_SUCCESS;
2878 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2879 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2881 if (buffers->dwStructSize != sizeof(*buffers))
2882 return ERROR_INVALID_PARAMETER;
2884 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2886 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2888 WORKREQUEST workRequest;
2890 if (TryEnterCriticalSection( &req->read_section ))
2892 if (get_avail_data(req))
2894 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2895 &buffers->dwBufferLength, FALSE);
2896 size = buffers->dwBufferLength;
2897 LeaveCriticalSection( &req->read_section );
2900 LeaveCriticalSection( &req->read_section );
2903 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2904 workRequest.hdr = WININET_AddRef(&req->hdr);
2905 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2907 INTERNET_AsyncCall(&workRequest);
2909 return ERROR_IO_PENDING;
2913 size = buffers->dwBufferLength;
2915 EnterCriticalSection( &req->read_section );
2916 if(hdr->dwError == ERROR_SUCCESS)
2917 hdr->dwError = INTERNET_HANDLE_IN_USE;
2918 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2919 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2922 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2923 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2924 if(res != ERROR_SUCCESS)
2927 read += buffers->dwBufferLength;
2928 if(read == size || end_of_read_data(req))
2931 LeaveCriticalSection( &req->read_section );
2933 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2934 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2935 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2936 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2938 EnterCriticalSection( &req->read_section );
2941 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2942 hdr->dwError = ERROR_SUCCESS;
2944 error = hdr->dwError;
2946 LeaveCriticalSection( &req->read_section );
2947 size = buffers->dwBufferLength;
2948 buffers->dwBufferLength = read;
2951 if (res == ERROR_SUCCESS) {
2952 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2953 &size, sizeof(size));
2956 return res==ERROR_SUCCESS ? error : res;
2959 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2962 http_request_t *request = (http_request_t*)hdr;
2964 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2967 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2968 if (res == ERROR_SUCCESS)
2969 request->bytesWritten += *written;
2971 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2975 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2977 http_request_t *req = (http_request_t*)workRequest->hdr;
2979 HTTP_ReceiveRequestData(req, FALSE);
2982 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2984 http_request_t *req = (http_request_t*)hdr;
2986 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2988 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2990 WORKREQUEST workRequest;
2992 /* never wait, if we can't enter the section we queue an async request right away */
2993 if (TryEnterCriticalSection( &req->read_section ))
2995 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2996 if ((*available = get_avail_data( req ))) goto done;
2997 if (end_of_read_data( req )) goto done;
2998 LeaveCriticalSection( &req->read_section );
3001 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3002 workRequest.hdr = WININET_AddRef( &req->hdr );
3004 INTERNET_AsyncCall(&workRequest);
3006 return ERROR_IO_PENDING;
3009 EnterCriticalSection( &req->read_section );
3011 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3013 refill_read_buffer( req, READMODE_ASYNC, NULL );
3014 *available = get_avail_data( req );
3018 LeaveCriticalSection( &req->read_section );
3020 TRACE( "returning %u\n", *available );
3021 return ERROR_SUCCESS;
3024 static const object_vtbl_t HTTPREQVtbl = {
3026 HTTPREQ_CloseConnection,
3027 HTTPREQ_QueryOption,
3030 HTTPREQ_ReadFileExA,
3031 HTTPREQ_ReadFileExW,
3033 HTTPREQ_QueryDataAvailable,
3037 /***********************************************************************
3038 * HTTP_HttpOpenRequestW (internal)
3040 * Open a HTTP request handle
3043 * HINTERNET a HTTP request handle on success
3047 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3048 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3049 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3050 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3052 appinfo_t *hIC = session->appInfo;
3053 http_request_t *request;
3054 DWORD len, res = ERROR_SUCCESS;
3058 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3060 return ERROR_OUTOFMEMORY;
3062 request->hdr.htype = WH_HHTTPREQ;
3063 request->hdr.dwFlags = dwFlags;
3064 request->hdr.dwContext = dwContext;
3065 request->contentLength = ~0u;
3067 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3068 request->data_stream = &request->netconn_stream.data_stream;
3069 request->connect_timeout = session->connect_timeout;
3070 request->send_timeout = session->send_timeout;
3071 request->receive_timeout = session->receive_timeout;
3073 InitializeCriticalSection( &request->read_section );
3074 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3076 WININET_AddRef( &session->hdr );
3077 request->session = session;
3078 list_add_head( &session->hdr.children, &request->hdr.entry );
3080 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3081 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3082 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3083 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3085 if (lpszObjectName && *lpszObjectName) {
3089 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3090 if (rc != E_POINTER)
3091 len = strlenW(lpszObjectName)+1;
3092 request->path = heap_alloc(len*sizeof(WCHAR));
3093 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3094 URL_ESCAPE_SPACES_ONLY);
3097 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3098 strcpyW(request->path,lpszObjectName);
3101 static const WCHAR slashW[] = {'/',0};
3103 request->path = heap_strdupW(slashW);
3106 if (lpszReferrer && *lpszReferrer)
3107 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3109 if (lpszAcceptTypes)
3112 for (i = 0; lpszAcceptTypes[i]; i++)
3114 if (!*lpszAcceptTypes[i]) continue;
3115 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3116 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3117 HTTP_ADDHDR_FLAG_REQ |
3118 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3122 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3123 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3125 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3126 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3127 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3131 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3133 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3135 res = ERROR_OUTOFMEMORY;
3139 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3140 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3141 heap_free(host_name);
3144 HTTP_ProcessHeader(request, hostW, session->hostName,
3145 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3147 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3148 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3149 INTERNET_DEFAULT_HTTPS_PORT :
3150 INTERNET_DEFAULT_HTTP_PORT);
3152 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3153 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3154 INTERNET_DEFAULT_HTTPS_PORT :
3155 INTERNET_DEFAULT_HTTP_PORT);
3157 if (hIC->proxy && hIC->proxy[0])
3158 HTTP_DealWithProxy( hIC, session, request );
3160 INTERNET_SendCallback(&session->hdr, dwContext,
3161 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3165 TRACE("<-- %u (%p)\n", res, request);
3167 if(res != ERROR_SUCCESS) {
3168 WININET_Release( &request->hdr );
3173 *ret = request->hdr.hInternet;
3174 return ERROR_SUCCESS;
3177 /***********************************************************************
3178 * HttpOpenRequestW (WININET.@)
3180 * Open a HTTP request handle
3183 * HINTERNET a HTTP request handle on success
3187 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3188 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3189 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3190 DWORD dwFlags, DWORD_PTR dwContext)
3192 http_session_t *session;
3193 HINTERNET handle = NULL;
3196 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3197 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3198 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3199 dwFlags, dwContext);
3200 if(lpszAcceptTypes!=NULL)
3203 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3204 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3207 session = (http_session_t*) get_handle_object( hHttpSession );
3208 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3210 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3215 * My tests seem to show that the windows version does not
3216 * become asynchronous until after this point. And anyhow
3217 * if this call was asynchronous then how would you get the
3218 * necessary HINTERNET pointer returned by this function.
3221 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3222 lpszVersion, lpszReferrer, lpszAcceptTypes,
3223 dwFlags, dwContext, &handle);
3226 WININET_Release( &session->hdr );
3227 TRACE("returning %p\n", handle);
3228 if(res != ERROR_SUCCESS)
3233 static const LPCWSTR header_lookup[] = {
3234 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3235 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3236 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3237 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3238 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3239 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3240 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3241 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3242 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3243 szDate, /* HTTP_QUERY_DATE = 9 */
3244 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3245 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3246 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3247 szURI, /* HTTP_QUERY_URI = 13 */
3248 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3249 NULL, /* HTTP_QUERY_COST = 15 */
3250 NULL, /* HTTP_QUERY_LINK = 16 */
3251 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3252 NULL, /* HTTP_QUERY_VERSION = 18 */
3253 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3254 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3255 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3256 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3257 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3258 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3259 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3260 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3261 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3262 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3263 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3264 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3265 NULL, /* HTTP_QUERY_FROM = 31 */
3266 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3267 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3268 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3269 szReferer, /* HTTP_QUERY_REFERER = 35 */
3270 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3271 szServer, /* HTTP_QUERY_SERVER = 37 */
3272 NULL, /* HTTP_TITLE = 38 */
3273 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3274 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3275 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3276 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3277 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3278 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3279 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3280 NULL, /* HTTP_QUERY_REFRESH = 46 */
3281 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3282 szAge, /* HTTP_QUERY_AGE = 48 */
3283 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3284 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3285 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3286 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3287 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3288 szETag, /* HTTP_QUERY_ETAG = 54 */
3289 hostW, /* HTTP_QUERY_HOST = 55 */
3290 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3291 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3292 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3293 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3294 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3295 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3296 szRange, /* HTTP_QUERY_RANGE = 62 */
3297 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3298 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3299 szVary, /* HTTP_QUERY_VARY = 65 */
3300 szVia, /* HTTP_QUERY_VIA = 66 */
3301 szWarning, /* HTTP_QUERY_WARNING = 67 */
3302 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3303 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3304 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3307 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3309 /***********************************************************************
3310 * HTTP_HttpQueryInfoW (internal)
3312 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3313 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3315 LPHTTPHEADERW lphttpHdr = NULL;
3316 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3317 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3318 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3321 /* Find requested header structure */
3324 case HTTP_QUERY_CUSTOM:
3325 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3326 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3328 case HTTP_QUERY_RAW_HEADERS_CRLF:
3332 DWORD res = ERROR_INVALID_PARAMETER;
3335 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3337 headers = request->rawHeaders;
3340 len = strlenW(headers) * sizeof(WCHAR);
3342 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3344 len += sizeof(WCHAR);
3345 res = ERROR_INSUFFICIENT_BUFFER;
3350 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3353 len = strlenW(szCrLf) * sizeof(WCHAR);
3354 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3356 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3357 res = ERROR_SUCCESS;
3359 *lpdwBufferLength = len;
3361 if (request_only) heap_free(headers);
3364 case HTTP_QUERY_RAW_HEADERS:
3366 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3368 LPWSTR pszString = lpBuffer;
3370 for (i = 0; ppszRawHeaderLines[i]; i++)
3371 size += strlenW(ppszRawHeaderLines[i]) + 1;
3373 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3375 HTTP_FreeTokens(ppszRawHeaderLines);
3376 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3377 return ERROR_INSUFFICIENT_BUFFER;
3381 for (i = 0; ppszRawHeaderLines[i]; i++)
3383 DWORD len = strlenW(ppszRawHeaderLines[i]);
3384 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3388 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3390 *lpdwBufferLength = size * sizeof(WCHAR);
3391 HTTP_FreeTokens(ppszRawHeaderLines);
3393 return ERROR_SUCCESS;
3395 case HTTP_QUERY_STATUS_TEXT:
3396 if (request->statusText)
3398 DWORD len = strlenW(request->statusText);
3399 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3401 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3402 return ERROR_INSUFFICIENT_BUFFER;
3406 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3407 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3409 *lpdwBufferLength = len * sizeof(WCHAR);
3410 return ERROR_SUCCESS;
3413 case HTTP_QUERY_VERSION:
3414 if (request->version)
3416 DWORD len = strlenW(request->version);
3417 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3419 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3420 return ERROR_INSUFFICIENT_BUFFER;
3424 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3425 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3427 *lpdwBufferLength = len * sizeof(WCHAR);
3428 return ERROR_SUCCESS;
3431 case HTTP_QUERY_CONTENT_ENCODING:
3432 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3433 requested_index,request_only);
3435 case HTTP_QUERY_STATUS_CODE: {
3436 DWORD res = ERROR_SUCCESS;
3439 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3444 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3445 if(*lpdwBufferLength >= sizeof(DWORD))
3446 *(DWORD*)lpBuffer = request->status_code;
3448 res = ERROR_INSUFFICIENT_BUFFER;
3449 *lpdwBufferLength = sizeof(DWORD);
3453 static const WCHAR formatW[] = {'%','u',0};
3455 size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3457 if(size <= *lpdwBufferLength)
3458 memcpy(lpBuffer, buf, size);
3460 res = ERROR_INSUFFICIENT_BUFFER;
3462 *lpdwBufferLength = size;
3467 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3469 if (level < LAST_TABLE_HEADER && header_lookup[level])
3470 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3471 requested_index,request_only);
3475 lphttpHdr = &request->custHeaders[index];
3477 /* Ensure header satisfies requested attributes */
3479 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3480 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3482 return ERROR_HTTP_HEADER_NOT_FOUND;
3485 if (lpdwIndex) (*lpdwIndex)++;
3487 /* coalesce value to requested type */
3488 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3490 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3491 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3493 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3499 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3501 tmpTM = *gmtime(&tmpTime);
3502 STHook = (SYSTEMTIME *)lpBuffer;
3503 STHook->wDay = tmpTM.tm_mday;
3504 STHook->wHour = tmpTM.tm_hour;
3505 STHook->wMilliseconds = 0;
3506 STHook->wMinute = tmpTM.tm_min;
3507 STHook->wDayOfWeek = tmpTM.tm_wday;
3508 STHook->wMonth = tmpTM.tm_mon + 1;
3509 STHook->wSecond = tmpTM.tm_sec;
3510 STHook->wYear = tmpTM.tm_year;
3512 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3513 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3514 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3516 else if (lphttpHdr->lpszValue)
3518 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3520 if (len > *lpdwBufferLength)
3522 *lpdwBufferLength = len;
3523 return ERROR_INSUFFICIENT_BUFFER;
3527 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3528 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3530 *lpdwBufferLength = len - sizeof(WCHAR);
3532 return ERROR_SUCCESS;
3535 /***********************************************************************
3536 * HttpQueryInfoW (WININET.@)
3538 * Queries for information about an HTTP request
3545 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3546 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3548 http_request_t *request;
3551 if (TRACE_ON(wininet)) {
3552 #define FE(x) { x, #x }
3553 static const wininet_flag_info query_flags[] = {
3554 FE(HTTP_QUERY_MIME_VERSION),
3555 FE(HTTP_QUERY_CONTENT_TYPE),
3556 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3557 FE(HTTP_QUERY_CONTENT_ID),
3558 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3559 FE(HTTP_QUERY_CONTENT_LENGTH),
3560 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3561 FE(HTTP_QUERY_ALLOW),
3562 FE(HTTP_QUERY_PUBLIC),
3563 FE(HTTP_QUERY_DATE),
3564 FE(HTTP_QUERY_EXPIRES),
3565 FE(HTTP_QUERY_LAST_MODIFIED),
3566 FE(HTTP_QUERY_MESSAGE_ID),
3568 FE(HTTP_QUERY_DERIVED_FROM),
3569 FE(HTTP_QUERY_COST),
3570 FE(HTTP_QUERY_LINK),
3571 FE(HTTP_QUERY_PRAGMA),
3572 FE(HTTP_QUERY_VERSION),
3573 FE(HTTP_QUERY_STATUS_CODE),
3574 FE(HTTP_QUERY_STATUS_TEXT),
3575 FE(HTTP_QUERY_RAW_HEADERS),
3576 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3577 FE(HTTP_QUERY_CONNECTION),
3578 FE(HTTP_QUERY_ACCEPT),
3579 FE(HTTP_QUERY_ACCEPT_CHARSET),
3580 FE(HTTP_QUERY_ACCEPT_ENCODING),
3581 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3582 FE(HTTP_QUERY_AUTHORIZATION),
3583 FE(HTTP_QUERY_CONTENT_ENCODING),
3584 FE(HTTP_QUERY_FORWARDED),
3585 FE(HTTP_QUERY_FROM),
3586 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3587 FE(HTTP_QUERY_LOCATION),
3588 FE(HTTP_QUERY_ORIG_URI),
3589 FE(HTTP_QUERY_REFERER),
3590 FE(HTTP_QUERY_RETRY_AFTER),
3591 FE(HTTP_QUERY_SERVER),
3592 FE(HTTP_QUERY_TITLE),
3593 FE(HTTP_QUERY_USER_AGENT),
3594 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3595 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3596 FE(HTTP_QUERY_ACCEPT_RANGES),
3597 FE(HTTP_QUERY_SET_COOKIE),
3598 FE(HTTP_QUERY_COOKIE),
3599 FE(HTTP_QUERY_REQUEST_METHOD),
3600 FE(HTTP_QUERY_REFRESH),
3601 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3603 FE(HTTP_QUERY_CACHE_CONTROL),
3604 FE(HTTP_QUERY_CONTENT_BASE),
3605 FE(HTTP_QUERY_CONTENT_LOCATION),
3606 FE(HTTP_QUERY_CONTENT_MD5),
3607 FE(HTTP_QUERY_CONTENT_RANGE),
3608 FE(HTTP_QUERY_ETAG),
3609 FE(HTTP_QUERY_HOST),
3610 FE(HTTP_QUERY_IF_MATCH),
3611 FE(HTTP_QUERY_IF_NONE_MATCH),
3612 FE(HTTP_QUERY_IF_RANGE),
3613 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3614 FE(HTTP_QUERY_MAX_FORWARDS),
3615 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3616 FE(HTTP_QUERY_RANGE),
3617 FE(HTTP_QUERY_TRANSFER_ENCODING),
3618 FE(HTTP_QUERY_UPGRADE),
3619 FE(HTTP_QUERY_VARY),
3621 FE(HTTP_QUERY_WARNING),
3622 FE(HTTP_QUERY_CUSTOM)
3624 static const wininet_flag_info modifier_flags[] = {
3625 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3626 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3627 FE(HTTP_QUERY_FLAG_NUMBER),
3628 FE(HTTP_QUERY_FLAG_COALESCE)
3631 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3632 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3635 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3636 TRACE(" Attribute:");
3637 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3638 if (query_flags[i].val == info) {
3639 TRACE(" %s", query_flags[i].name);
3643 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3644 TRACE(" Unknown (%08x)", info);
3647 TRACE(" Modifier:");
3648 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3649 if (modifier_flags[i].val & info_mod) {
3650 TRACE(" %s", modifier_flags[i].name);
3651 info_mod &= ~ modifier_flags[i].val;
3656 TRACE(" Unknown (%08x)", info_mod);
3661 request = (http_request_t*) get_handle_object( hHttpRequest );
3662 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3664 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3668 if (lpBuffer == NULL)
3669 *lpdwBufferLength = 0;
3670 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3671 lpBuffer, lpdwBufferLength, lpdwIndex);
3675 WININET_Release( &request->hdr );
3677 TRACE("%u <--\n", res);
3678 if(res != ERROR_SUCCESS)
3680 return res == ERROR_SUCCESS;
3683 /***********************************************************************
3684 * HttpQueryInfoA (WININET.@)
3686 * Queries for information about an HTTP request
3693 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3694 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3700 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3701 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3703 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3704 lpdwBufferLength, lpdwIndex );
3710 len = (*lpdwBufferLength)*sizeof(WCHAR);
3711 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3713 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3719 bufferW = heap_alloc(alloclen);
3720 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3721 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3722 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3729 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3733 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3734 lpBuffer, *lpdwBufferLength, NULL, NULL );
3735 *lpdwBufferLength = len - 1;
3737 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3740 /* since the strings being returned from HttpQueryInfoW should be
3741 * only ASCII characters, it is reasonable to assume that all of
3742 * the Unicode characters can be reduced to a single byte */
3743 *lpdwBufferLength = len / sizeof(WCHAR);
3745 heap_free( bufferW );
3749 /***********************************************************************
3750 * HTTP_GetRedirectURL (internal)
3752 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3754 static WCHAR szHttp[] = {'h','t','t','p',0};
3755 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3756 http_session_t *session = request->session;
3757 URL_COMPONENTSW urlComponents;
3758 DWORD url_length = 0;
3760 LPWSTR combined_url;
3762 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3763 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3764 urlComponents.dwSchemeLength = 0;
3765 urlComponents.lpszHostName = session->hostName;
3766 urlComponents.dwHostNameLength = 0;
3767 urlComponents.nPort = session->hostPort;
3768 urlComponents.lpszUserName = session->userName;
3769 urlComponents.dwUserNameLength = 0;
3770 urlComponents.lpszPassword = NULL;
3771 urlComponents.dwPasswordLength = 0;
3772 urlComponents.lpszUrlPath = request->path;
3773 urlComponents.dwUrlPathLength = 0;
3774 urlComponents.lpszExtraInfo = NULL;
3775 urlComponents.dwExtraInfoLength = 0;
3777 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3778 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3781 orig_url = heap_alloc(url_length);
3783 /* convert from bytes to characters */
3784 url_length = url_length / sizeof(WCHAR) - 1;
3785 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3787 heap_free(orig_url);
3792 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3793 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3795 heap_free(orig_url);
3798 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3800 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3802 heap_free(orig_url);
3803 heap_free(combined_url);
3806 heap_free(orig_url);
3807 return combined_url;
3811 /***********************************************************************
3812 * HTTP_HandleRedirect (internal)
3814 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3816 http_session_t *session = request->session;
3817 appinfo_t *hIC = session->appInfo;
3818 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3819 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3824 /* if it's an absolute path, keep the same session info */
3825 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3829 URL_COMPONENTSW urlComponents;
3830 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3831 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3832 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3833 BOOL custom_port = FALSE;
3835 static WCHAR httpW[] = {'h','t','t','p',0};
3836 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3842 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3843 urlComponents.lpszScheme = protocol;
3844 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3845 urlComponents.lpszHostName = hostName;
3846 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3847 urlComponents.lpszUserName = userName;
3848 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3849 urlComponents.lpszPassword = NULL;
3850 urlComponents.dwPasswordLength = 0;
3851 urlComponents.lpszUrlPath = path;
3852 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3853 urlComponents.lpszExtraInfo = NULL;
3854 urlComponents.dwExtraInfoLength = 0;
3855 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3856 return INTERNET_GetLastError();
3858 if(!strcmpiW(protocol, httpW)) {
3859 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3860 TRACE("redirect from secure page to non-secure page\n");
3861 /* FIXME: warn about from secure redirect to non-secure page */
3862 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3865 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3866 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3867 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3869 }else if(!strcmpiW(protocol, httpsW)) {
3870 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3871 TRACE("redirect from non-secure page to secure page\n");
3872 /* FIXME: notify about redirect to secure page */
3873 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3876 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3877 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3878 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3882 heap_free(session->hostName);
3886 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3887 len = lstrlenW(hostName);
3888 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3889 session->hostName = heap_alloc(len*sizeof(WCHAR));
3890 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3893 session->hostName = heap_strdupW(hostName);
3895 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3897 heap_free(session->userName);
3898 session->userName = NULL;
3900 session->userName = heap_strdupW(userName);
3902 reset_data_stream(request);
3905 if(strcmpiW(session->serverName, hostName)) {
3906 heap_free(session->serverName);
3907 session->serverName = heap_strdupW(hostName);
3909 session->serverPort = urlComponents.nPort;
3912 heap_free(request->path);
3919 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3920 if (rc != E_POINTER)
3921 needed = strlenW(path)+1;
3922 request->path = heap_alloc(needed*sizeof(WCHAR));
3923 rc = UrlEscapeW(path, request->path, &needed,
3924 URL_ESCAPE_SPACES_ONLY);
3927 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3928 strcpyW(request->path,path);
3932 /* Remove custom content-type/length headers on redirects. */
3933 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3935 HTTP_DeleteCustomHeader(request, index);
3936 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3938 HTTP_DeleteCustomHeader(request, index);
3940 return ERROR_SUCCESS;
3943 /***********************************************************************
3944 * HTTP_build_req (internal)
3946 * concatenate all the strings in the request together
3948 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3953 for( t = list; *t ; t++ )
3954 len += strlenW( *t );
3957 str = heap_alloc(len*sizeof(WCHAR));
3960 for( t = list; *t ; t++ )
3966 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3969 LPWSTR requestString;
3975 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3976 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3977 http_session_t *session = request->session;
3981 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3982 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3983 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3984 heap_free( lpszPath );
3986 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3987 NULL, 0, NULL, NULL );
3988 len--; /* the nul terminator isn't needed */
3989 ascii_req = heap_alloc(len);
3990 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3991 heap_free( requestString );
3993 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3995 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
3996 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3997 heap_free( ascii_req );
3998 if (res != ERROR_SUCCESS)
4001 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4003 return ERROR_HTTP_INVALID_HEADER;
4005 return ERROR_SUCCESS;
4008 static void HTTP_InsertCookies(http_request_t *request)
4010 DWORD cookie_size, size, cnt = 0;
4014 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4016 host = HTTP_GetHeader(request, hostW);
4020 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4023 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4024 if(!(cookies = heap_alloc(size)))
4027 cnt += sprintfW(cookies, cookieW);
4028 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4029 strcatW(cookies, szCrLf);
4031 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4036 static WORD HTTP_ParseWkday(LPCWSTR day)
4038 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4046 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4047 if (!strcmpiW(day, days[i]))
4054 static WORD HTTP_ParseMonth(LPCWSTR month)
4056 static const WCHAR jan[] = { 'j','a','n',0 };
4057 static const WCHAR feb[] = { 'f','e','b',0 };
4058 static const WCHAR mar[] = { 'm','a','r',0 };
4059 static const WCHAR apr[] = { 'a','p','r',0 };
4060 static const WCHAR may[] = { 'm','a','y',0 };
4061 static const WCHAR jun[] = { 'j','u','n',0 };
4062 static const WCHAR jul[] = { 'j','u','l',0 };
4063 static const WCHAR aug[] = { 'a','u','g',0 };
4064 static const WCHAR sep[] = { 's','e','p',0 };
4065 static const WCHAR oct[] = { 'o','c','t',0 };
4066 static const WCHAR nov[] = { 'n','o','v',0 };
4067 static const WCHAR dec[] = { 'd','e','c',0 };
4069 if (!strcmpiW(month, jan)) return 1;
4070 if (!strcmpiW(month, feb)) return 2;
4071 if (!strcmpiW(month, mar)) return 3;
4072 if (!strcmpiW(month, apr)) return 4;
4073 if (!strcmpiW(month, may)) return 5;
4074 if (!strcmpiW(month, jun)) return 6;
4075 if (!strcmpiW(month, jul)) return 7;
4076 if (!strcmpiW(month, aug)) return 8;
4077 if (!strcmpiW(month, sep)) return 9;
4078 if (!strcmpiW(month, oct)) return 10;
4079 if (!strcmpiW(month, nov)) return 11;
4080 if (!strcmpiW(month, dec)) return 12;
4085 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4086 * optionally preceded by whitespace.
4087 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4088 * st, and sets *str to the first character after the time format.
4090 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4096 while (isspaceW(*ptr))
4099 num = strtoulW(ptr, &nextPtr, 10);
4100 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4102 ERR("unexpected time format %s\n", debugstr_w(ptr));
4107 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4111 st->wHour = (WORD)num;
4112 num = strtoulW(ptr, &nextPtr, 10);
4113 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4115 ERR("unexpected time format %s\n", debugstr_w(ptr));
4120 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4124 st->wMinute = (WORD)num;
4125 num = strtoulW(ptr, &nextPtr, 10);
4126 if (!nextPtr || nextPtr <= ptr)
4128 ERR("unexpected time format %s\n", debugstr_w(ptr));
4133 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4138 st->wSecond = (WORD)num;
4142 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4144 static const WCHAR gmt[]= { 'G','M','T',0 };
4145 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4147 SYSTEMTIME st = { 0 };
4150 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4151 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4154 st.wDayOfWeek = HTTP_ParseWkday(day);
4155 if (st.wDayOfWeek >= 7)
4157 ERR("unexpected weekday %s\n", debugstr_w(day));
4161 while (isspaceW(*ptr))
4164 for (monthPtr = month; !isspace(*ptr) &&
4165 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4169 st.wMonth = HTTP_ParseMonth(month);
4170 if (!st.wMonth || st.wMonth > 12)
4172 ERR("unexpected month %s\n", debugstr_w(month));
4176 while (isspaceW(*ptr))
4179 num = strtoulW(ptr, &nextPtr, 10);
4180 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4182 ERR("unexpected day %s\n", debugstr_w(ptr));
4186 st.wDay = (WORD)num;
4188 while (isspaceW(*ptr))
4191 if (!HTTP_ParseTime(&st, &ptr))
4194 while (isspaceW(*ptr))
4197 num = strtoulW(ptr, &nextPtr, 10);
4198 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4200 ERR("unexpected year %s\n", debugstr_w(ptr));
4204 st.wYear = (WORD)num;
4206 while (isspaceW(*ptr))
4209 /* asctime() doesn't report a timezone, but some web servers do, so accept
4210 * with or without GMT.
4212 if (*ptr && strcmpW(ptr, gmt))
4214 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4217 return SystemTimeToFileTime(&st, ft);
4220 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4222 static const WCHAR gmt[]= { 'G','M','T',0 };
4223 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4226 SYSTEMTIME st = { 0 };
4228 ptr = strchrW(value, ',');
4231 if (ptr - value != 3)
4233 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4236 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4238 st.wDayOfWeek = HTTP_ParseWkday(day);
4239 if (st.wDayOfWeek > 6)
4241 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4246 while (isspaceW(*ptr))
4249 num = strtoulW(ptr, &nextPtr, 10);
4250 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4252 WARN("unexpected day %s\n", debugstr_w(value));
4256 st.wDay = (WORD)num;
4258 while (isspaceW(*ptr))
4261 for (monthPtr = month; !isspace(*ptr) &&
4262 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4266 st.wMonth = HTTP_ParseMonth(month);
4267 if (!st.wMonth || st.wMonth > 12)
4269 WARN("unexpected month %s\n", debugstr_w(month));
4273 while (isspaceW(*ptr))
4276 num = strtoulW(ptr, &nextPtr, 10);
4277 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4279 ERR("unexpected year %s\n", debugstr_w(value));
4283 st.wYear = (WORD)num;
4285 if (!HTTP_ParseTime(&st, &ptr))
4288 while (isspaceW(*ptr))
4291 if (strcmpW(ptr, gmt))
4293 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4296 return SystemTimeToFileTime(&st, ft);
4299 static WORD HTTP_ParseWeekday(LPCWSTR day)
4301 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4302 { 'm','o','n','d','a','y',0 },
4303 { 't','u','e','s','d','a','y',0 },
4304 { 'w','e','d','n','e','s','d','a','y',0 },
4305 { 't','h','u','r','s','d','a','y',0 },
4306 { 'f','r','i','d','a','y',0 },
4307 { 's','a','t','u','r','d','a','y',0 }};
4309 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4310 if (!strcmpiW(day, days[i]))
4317 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4319 static const WCHAR gmt[]= { 'G','M','T',0 };
4320 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4323 SYSTEMTIME st = { 0 };
4325 ptr = strchrW(value, ',');
4328 if (ptr - value == 3)
4330 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4332 st.wDayOfWeek = HTTP_ParseWkday(day);
4333 if (st.wDayOfWeek > 6)
4335 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4339 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4341 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4342 day[ptr - value + 1] = 0;
4343 st.wDayOfWeek = HTTP_ParseWeekday(day);
4344 if (st.wDayOfWeek > 6)
4346 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4352 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4357 while (isspaceW(*ptr))
4360 num = strtoulW(ptr, &nextPtr, 10);
4361 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4363 ERR("unexpected day %s\n", debugstr_w(value));
4367 st.wDay = (WORD)num;
4371 ERR("unexpected month format %s\n", debugstr_w(ptr));
4376 for (monthPtr = month; *ptr != '-' &&
4377 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4381 st.wMonth = HTTP_ParseMonth(month);
4382 if (!st.wMonth || st.wMonth > 12)
4384 ERR("unexpected month %s\n", debugstr_w(month));
4390 ERR("unexpected year format %s\n", debugstr_w(ptr));
4395 num = strtoulW(ptr, &nextPtr, 10);
4396 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4398 ERR("unexpected year %s\n", debugstr_w(value));
4402 st.wYear = (WORD)num;
4404 if (!HTTP_ParseTime(&st, &ptr))
4407 while (isspaceW(*ptr))
4410 if (strcmpW(ptr, gmt))
4412 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4415 return SystemTimeToFileTime(&st, ft);
4418 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4420 static const WCHAR zero[] = { '0',0 };
4423 if (!strcmpW(value, zero))
4425 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4428 else if (strchrW(value, ','))
4430 ret = HTTP_ParseRfc1123Date(value, ft);
4433 ret = HTTP_ParseRfc850Date(value, ft);
4435 ERR("unexpected date format %s\n", debugstr_w(value));
4440 ret = HTTP_ParseDateAsAsctime(value, ft);
4442 ERR("unexpected date format %s\n", debugstr_w(value));
4447 static void HTTP_ProcessExpires(http_request_t *request)
4449 BOOL expirationFound = FALSE;
4452 /* Look for a Cache-Control header with a max-age directive, as it takes
4453 * precedence over the Expires header.
4455 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4456 if (headerIndex != -1)
4458 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4461 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4463 LPWSTR comma = strchrW(ptr, ','), end, equal;
4468 end = ptr + strlenW(ptr);
4469 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4473 static const WCHAR max_age[] = {
4474 'm','a','x','-','a','g','e',0 };
4476 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4481 age = strtoulW(equal + 1, &nextPtr, 10);
4482 if (nextPtr > equal + 1)
4486 NtQuerySystemTime( &ft );
4487 /* Age is in seconds, FILETIME resolution is in
4488 * 100 nanosecond intervals.
4490 ft.QuadPart += age * (ULONGLONG)1000000;
4491 request->expires.dwLowDateTime = ft.u.LowPart;
4492 request->expires.dwHighDateTime = ft.u.HighPart;
4493 expirationFound = TRUE;
4500 while (isspaceW(*ptr))
4507 if (!expirationFound)
4509 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4510 if (headerIndex != -1)
4512 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4515 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4517 expirationFound = TRUE;
4518 request->expires = ft;
4522 if (!expirationFound)
4526 /* With no known age, default to 10 minutes until expiration. */
4527 NtQuerySystemTime( &t );
4528 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4529 request->expires.dwLowDateTime = t.u.LowPart;
4530 request->expires.dwHighDateTime = t.u.HighPart;
4534 static void HTTP_ProcessLastModified(http_request_t *request)
4538 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4539 if (headerIndex != -1)
4541 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4544 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4545 request->last_modified = ft;
4549 static void http_process_keep_alive(http_request_t *req)
4553 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4555 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4557 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4560 static void HTTP_CacheRequest(http_request_t *request)
4562 WCHAR url[INTERNET_MAX_URL_LENGTH];
4563 WCHAR cacheFileName[MAX_PATH+1];
4566 b = HTTP_GetRequestURL(request, url);
4568 WARN("Could not get URL\n");
4572 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4574 heap_free(request->cacheFile);
4575 CloseHandle(request->hCacheFile);
4577 request->cacheFile = heap_strdupW(cacheFileName);
4578 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4579 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4580 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4581 WARN("Could not create file: %u\n", GetLastError());
4582 request->hCacheFile = NULL;
4585 WARN("Could not create cache entry: %08x\n", GetLastError());
4589 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4591 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4592 http_session_t *session = request->session;
4593 netconn_t *netconn = NULL;
4597 assert(!request->netconn);
4598 reset_data_stream(request);
4600 server = get_server(session->serverName, session->serverPort);
4602 return ERROR_OUTOFMEMORY;
4604 res = HTTP_ResolveName(request, server);
4605 if(res != ERROR_SUCCESS) {
4606 server_release(server);
4610 EnterCriticalSection(&connection_pool_cs);
4612 while(!list_empty(&server->conn_pool)) {
4613 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4614 list_remove(&netconn->pool_entry);
4616 if(NETCON_is_alive(netconn))
4619 TRACE("connection %p closed during idle\n", netconn);
4620 free_netconn(netconn);
4624 LeaveCriticalSection(&connection_pool_cs);
4627 TRACE("<-- reusing %p netconn\n", netconn);
4628 request->netconn = netconn;
4630 return ERROR_SUCCESS;
4633 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4634 INTERNET_STATUS_CONNECTING_TO_SERVER,
4636 strlen(server->addr_str)+1);
4638 res = create_netconn(is_https, server, request->security_flags, request->connect_timeout, &netconn);
4639 server_release(server);
4640 if(res != ERROR_SUCCESS) {
4641 ERR("create_netconn failed: %u\n", res);
4645 request->netconn = netconn;
4647 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4648 INTERNET_STATUS_CONNECTED_TO_SERVER,
4649 server->addr_str, strlen(server->addr_str)+1);
4652 /* Note: we differ from Microsoft's WinINet here. they seem to have
4653 * a bug that causes no status callbacks to be sent when starting
4654 * a tunnel to a proxy server using the CONNECT verb. i believe our
4655 * behaviour to be more correct and to not cause any incompatibilities
4656 * because using a secure connection through a proxy server is a rare
4657 * case that would be hard for anyone to depend on */
4658 if(session->appInfo->proxy)
4659 res = HTTP_SecureProxyConnect(request);
4660 if(res == ERROR_SUCCESS)
4661 res = NETCON_secure_connect(request->netconn);
4662 if(res != ERROR_SUCCESS)
4664 WARN("Couldn't connect securely to host\n");
4666 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4667 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4668 || res == ERROR_INTERNET_INVALID_CA
4669 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4670 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4671 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4672 || res == ERROR_INTERNET_SEC_INVALID_CERT
4673 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4674 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4678 if(res != ERROR_SUCCESS) {
4679 http_release_netconn(request, FALSE);
4684 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4685 return ERROR_SUCCESS;
4688 /***********************************************************************
4689 * HTTP_HttpSendRequestW (internal)
4691 * Sends the specified request to the HTTP server
4694 * ERROR_SUCCESS on success
4695 * win32 error code on failure
4698 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4699 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4700 DWORD dwContentLength, BOOL bEndRequest)
4703 BOOL redirected = FALSE;
4704 LPWSTR requestString = NULL;
4707 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4708 static const WCHAR szContentLength[] =
4709 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4710 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4713 TRACE("--> %p\n", request);
4715 assert(request->hdr.htype == WH_HHTTPREQ);
4717 /* if the verb is NULL default to GET */
4719 request->verb = heap_strdupW(szGET);
4721 if (dwContentLength || strcmpW(request->verb, szGET))
4723 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4724 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4725 request->bytesToWrite = dwContentLength;
4727 if (request->session->appInfo->agent)
4729 WCHAR *agent_header;
4730 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4733 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4734 agent_header = heap_alloc(len * sizeof(WCHAR));
4735 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4737 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4738 heap_free(agent_header);
4740 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4742 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4743 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4745 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4747 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4748 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4749 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4752 /* add the headers the caller supplied */
4753 if( lpszHeaders && dwHeaderLength )
4754 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4759 BOOL reusing_connection;
4764 /* like native, just in case the caller forgot to call InternetReadFile
4765 * for all the data */
4766 drain_content(request);
4768 request->contentLength = ~0u;
4769 request->bytesToWrite = 0;
4772 if (TRACE_ON(wininet))
4774 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4775 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4778 HTTP_FixURL(request);
4779 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4781 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4783 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4784 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4786 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4787 HTTP_InsertCookies(request);
4789 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4791 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4792 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4796 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4799 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4801 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4804 /* send the request as ASCII, tack on the optional data */
4805 if (!lpOptional || redirected)
4806 dwOptionalLength = 0;
4807 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4808 NULL, 0, NULL, NULL );
4809 ascii_req = heap_alloc(len + dwOptionalLength);
4810 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4811 ascii_req, len, NULL, NULL );
4813 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4814 len = (len + dwOptionalLength - 1);
4816 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4818 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4819 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4821 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4822 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4823 heap_free( ascii_req );
4824 if(res != ERROR_SUCCESS) {
4825 TRACE("send failed: %u\n", res);
4826 if(!reusing_connection)
4828 http_release_netconn(request, FALSE);
4833 request->bytesWritten = dwOptionalLength;
4835 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4836 INTERNET_STATUS_REQUEST_SENT,
4837 &len, sizeof(DWORD));
4843 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4844 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4846 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4847 /* FIXME: We should know that connection is closed before sending
4848 * headers. Otherwise wrong callbacks are executed */
4849 if(!responseLen && reusing_connection) {
4850 TRACE("Connection closed by server, reconnecting\n");
4851 http_release_netconn(request, FALSE);
4856 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4857 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4860 http_process_keep_alive(request);
4861 HTTP_ProcessCookies(request);
4862 HTTP_ProcessExpires(request);
4863 HTTP_ProcessLastModified(request);
4865 res = set_content_length(request);
4866 if(res != ERROR_SUCCESS)
4868 if(!request->contentLength)
4869 http_release_netconn(request, TRUE);
4871 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4873 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4874 dwBufferSize=sizeof(szNewLocation);
4875 switch(request->status_code) {
4876 case HTTP_STATUS_REDIRECT:
4877 case HTTP_STATUS_MOVED:
4878 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4879 case HTTP_STATUS_REDIRECT_METHOD:
4880 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4883 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4884 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4886 heap_free(request->verb);
4887 request->verb = heap_strdupW(szGET);
4889 drain_content(request);
4890 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4892 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4893 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4894 res = HTTP_HandleRedirect(request, new_url);
4895 if (res == ERROR_SUCCESS)
4897 heap_free(requestString);
4900 heap_free( new_url );
4905 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4907 WCHAR szAuthValue[2048];
4909 if (request->status_code == HTTP_STATUS_DENIED)
4911 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4913 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4915 if (HTTP_DoAuthorization(request, szAuthValue,
4917 request->session->userName,
4918 request->session->password,
4921 heap_free(requestString);
4928 TRACE("Cleaning wrong authorization data\n");
4929 destroy_authinfo(request->authInfo);
4930 request->authInfo = NULL;
4933 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4936 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4938 if (HTTP_DoAuthorization(request, szAuthValue,
4939 &request->proxyAuthInfo,
4940 request->session->appInfo->proxyUsername,
4941 request->session->appInfo->proxyPassword,
4950 TRACE("Cleaning wrong proxy authorization data\n");
4951 destroy_authinfo(request->proxyAuthInfo);
4952 request->proxyAuthInfo = NULL;
4958 res = ERROR_SUCCESS;
4962 if(res == ERROR_SUCCESS)
4963 HTTP_CacheRequest(request);
4966 heap_free(requestString);
4968 /* TODO: send notification for P3P header */
4970 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4972 if (res == ERROR_SUCCESS) {
4973 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4974 HTTP_ReceiveRequestData(request, TRUE);
4976 send_request_complete(request,
4977 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4979 send_request_complete(request, 0, res);
4987 /***********************************************************************
4989 * Helper functions for the HttpSendRequest(Ex) functions
4992 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4994 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4995 http_request_t *request = (http_request_t*) workRequest->hdr;
4997 TRACE("%p\n", request);
4999 HTTP_HttpSendRequestW(request, req->lpszHeader,
5000 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5001 req->dwContentLength, req->bEndRequest);
5003 heap_free(req->lpszHeader);
5007 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5011 DWORD res = ERROR_SUCCESS;
5013 if(!request->netconn) {
5014 WARN("Not connected\n");
5015 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5016 return ERROR_INTERNET_OPERATION_CANCELLED;
5019 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5020 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5022 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5024 res = ERROR_HTTP_HEADER_NOT_FOUND;
5026 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5027 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5029 /* process cookies here. Is this right? */
5030 http_process_keep_alive(request);
5031 HTTP_ProcessCookies(request);
5032 HTTP_ProcessExpires(request);
5033 HTTP_ProcessLastModified(request);
5035 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5036 if(!request->contentLength)
5037 http_release_netconn(request, TRUE);
5040 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5042 switch(request->status_code) {
5043 case HTTP_STATUS_REDIRECT:
5044 case HTTP_STATUS_MOVED:
5045 case HTTP_STATUS_REDIRECT_METHOD:
5046 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5047 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5048 dwBufferSize=sizeof(szNewLocation);
5049 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5052 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5053 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5055 heap_free(request->verb);
5056 request->verb = heap_strdupW(szGET);
5058 drain_content(request);
5059 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5061 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5062 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5063 res = HTTP_HandleRedirect(request, new_url);
5064 if (res == ERROR_SUCCESS)
5065 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5066 heap_free( new_url );
5072 if (res == ERROR_SUCCESS && request->contentLength)
5073 HTTP_ReceiveRequestData(request, TRUE);
5075 send_request_complete(request, res == ERROR_SUCCESS, res);
5080 /***********************************************************************
5081 * HttpEndRequestA (WININET.@)
5083 * Ends an HTTP request that was started by HttpSendRequestEx
5086 * TRUE if successful
5090 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5091 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5093 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5097 SetLastError(ERROR_INVALID_PARAMETER);
5101 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5104 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5106 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5107 http_request_t *request = (http_request_t*)work->hdr;
5109 TRACE("%p\n", request);
5111 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5114 /***********************************************************************
5115 * HttpEndRequestW (WININET.@)
5117 * Ends an HTTP request that was started by HttpSendRequestEx
5120 * TRUE if successful
5124 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5125 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5127 http_request_t *request;
5130 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5134 SetLastError(ERROR_INVALID_PARAMETER);
5138 request = (http_request_t*) get_handle_object( hRequest );
5140 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5142 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5144 WININET_Release( &request->hdr );
5147 request->hdr.dwFlags |= dwFlags;
5149 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5152 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5154 work.asyncproc = AsyncHttpEndRequestProc;
5155 work.hdr = WININET_AddRef( &request->hdr );
5157 work_endrequest = &work.u.HttpEndRequestW;
5158 work_endrequest->dwFlags = dwFlags;
5159 work_endrequest->dwContext = dwContext;
5161 INTERNET_AsyncCall(&work);
5162 res = ERROR_IO_PENDING;
5165 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5167 WININET_Release( &request->hdr );
5168 TRACE("%u <--\n", res);
5169 if(res != ERROR_SUCCESS)
5171 return res == ERROR_SUCCESS;
5174 /***********************************************************************
5175 * HttpSendRequestExA (WININET.@)
5177 * Sends the specified request to the HTTP server and allows chunked
5182 * Failure: FALSE, call GetLastError() for more information.
5184 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5185 LPINTERNET_BUFFERSA lpBuffersIn,
5186 LPINTERNET_BUFFERSA lpBuffersOut,
5187 DWORD dwFlags, DWORD_PTR dwContext)
5189 INTERNET_BUFFERSW BuffersInW;
5192 LPWSTR header = NULL;
5194 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5195 lpBuffersOut, dwFlags, dwContext);
5199 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5200 if (lpBuffersIn->lpcszHeader)
5202 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5203 lpBuffersIn->dwHeadersLength,0,0);
5204 header = heap_alloc(headerlen*sizeof(WCHAR));
5205 if (!(BuffersInW.lpcszHeader = header))
5207 SetLastError(ERROR_OUTOFMEMORY);
5210 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5211 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5215 BuffersInW.lpcszHeader = NULL;
5216 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5217 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5218 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5219 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5220 BuffersInW.Next = NULL;
5223 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5229 /***********************************************************************
5230 * HttpSendRequestExW (WININET.@)
5232 * Sends the specified request to the HTTP server and allows chunked
5237 * Failure: FALSE, call GetLastError() for more information.
5239 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5240 LPINTERNET_BUFFERSW lpBuffersIn,
5241 LPINTERNET_BUFFERSW lpBuffersOut,
5242 DWORD dwFlags, DWORD_PTR dwContext)
5244 http_request_t *request;
5245 http_session_t *session;
5249 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5250 lpBuffersOut, dwFlags, dwContext);
5252 request = (http_request_t*) get_handle_object( hRequest );
5254 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5256 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5260 session = request->session;
5261 assert(session->hdr.htype == WH_HHTTPSESSION);
5262 hIC = session->appInfo;
5263 assert(hIC->hdr.htype == WH_HINIT);
5265 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5267 WORKREQUEST workRequest;
5268 struct WORKREQ_HTTPSENDREQUESTW *req;
5270 workRequest.asyncproc = AsyncHttpSendRequestProc;
5271 workRequest.hdr = WININET_AddRef( &request->hdr );
5272 req = &workRequest.u.HttpSendRequestW;
5277 if (lpBuffersIn->lpcszHeader)
5279 if (lpBuffersIn->dwHeadersLength == ~0u)
5280 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5282 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5284 req->lpszHeader = heap_alloc(size);
5285 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5287 else req->lpszHeader = NULL;
5289 req->dwHeaderLength = size / sizeof(WCHAR);
5290 req->lpOptional = lpBuffersIn->lpvBuffer;
5291 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5292 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5296 req->lpszHeader = NULL;
5297 req->dwHeaderLength = 0;
5298 req->lpOptional = NULL;
5299 req->dwOptionalLength = 0;
5300 req->dwContentLength = 0;
5303 req->bEndRequest = FALSE;
5305 INTERNET_AsyncCall(&workRequest);
5307 * This is from windows.
5309 res = ERROR_IO_PENDING;
5314 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5315 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5316 lpBuffersIn->dwBufferTotal, FALSE);
5318 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5323 WININET_Release( &request->hdr );
5327 return res == ERROR_SUCCESS;
5330 /***********************************************************************
5331 * HttpSendRequestW (WININET.@)
5333 * Sends the specified request to the HTTP server
5340 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5341 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5343 http_request_t *request;
5344 http_session_t *session = NULL;
5345 appinfo_t *hIC = NULL;
5346 DWORD res = ERROR_SUCCESS;
5348 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5349 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5351 request = (http_request_t*) get_handle_object( hHttpRequest );
5352 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5354 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5358 session = request->session;
5359 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5361 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5365 hIC = session->appInfo;
5366 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5368 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5372 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5374 WORKREQUEST workRequest;
5375 struct WORKREQ_HTTPSENDREQUESTW *req;
5377 workRequest.asyncproc = AsyncHttpSendRequestProc;
5378 workRequest.hdr = WININET_AddRef( &request->hdr );
5379 req = &workRequest.u.HttpSendRequestW;
5384 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5385 else size = dwHeaderLength * sizeof(WCHAR);
5387 req->lpszHeader = heap_alloc(size);
5388 memcpy(req->lpszHeader, lpszHeaders, size);
5391 req->lpszHeader = 0;
5392 req->dwHeaderLength = dwHeaderLength;
5393 req->lpOptional = lpOptional;
5394 req->dwOptionalLength = dwOptionalLength;
5395 req->dwContentLength = dwOptionalLength;
5396 req->bEndRequest = TRUE;
5398 INTERNET_AsyncCall(&workRequest);
5400 * This is from windows.
5402 res = ERROR_IO_PENDING;
5406 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5407 dwHeaderLength, lpOptional, dwOptionalLength,
5408 dwOptionalLength, TRUE);
5412 WININET_Release( &request->hdr );
5415 return res == ERROR_SUCCESS;
5418 /***********************************************************************
5419 * HttpSendRequestA (WININET.@)
5421 * Sends the specified request to the HTTP server
5428 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5429 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5432 LPWSTR szHeaders=NULL;
5433 DWORD nLen=dwHeaderLength;
5434 if(lpszHeaders!=NULL)
5436 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5437 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5438 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5440 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5441 heap_free(szHeaders);
5445 /***********************************************************************
5446 * HTTPSESSION_Destroy (internal)
5448 * Deallocate session handle
5451 static void HTTPSESSION_Destroy(object_header_t *hdr)
5453 http_session_t *session = (http_session_t*) hdr;
5455 TRACE("%p\n", session);
5457 WININET_Release(&session->appInfo->hdr);
5459 heap_free(session->hostName);
5460 heap_free(session->serverName);
5461 heap_free(session->password);
5462 heap_free(session->userName);
5465 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5467 http_session_t *ses = (http_session_t *)hdr;
5470 case INTERNET_OPTION_HANDLE_TYPE:
5471 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5473 if (*size < sizeof(ULONG))
5474 return ERROR_INSUFFICIENT_BUFFER;
5476 *size = sizeof(DWORD);
5477 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5478 return ERROR_SUCCESS;
5479 case INTERNET_OPTION_CONNECT_TIMEOUT:
5480 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5482 if (*size < sizeof(DWORD))
5483 return ERROR_INSUFFICIENT_BUFFER;
5485 *size = sizeof(DWORD);
5486 *(DWORD *)buffer = ses->connect_timeout;
5487 return ERROR_SUCCESS;
5489 case INTERNET_OPTION_SEND_TIMEOUT:
5490 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5492 if (*size < sizeof(DWORD))
5493 return ERROR_INSUFFICIENT_BUFFER;
5495 *size = sizeof(DWORD);
5496 *(DWORD *)buffer = ses->send_timeout;
5497 return ERROR_SUCCESS;
5499 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5500 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5502 if (*size < sizeof(DWORD))
5503 return ERROR_INSUFFICIENT_BUFFER;
5505 *size = sizeof(DWORD);
5506 *(DWORD *)buffer = ses->receive_timeout;
5507 return ERROR_SUCCESS;
5510 return INET_QueryOption(hdr, option, buffer, size, unicode);
5513 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5515 http_session_t *ses = (http_session_t*)hdr;
5518 case INTERNET_OPTION_USERNAME:
5520 heap_free(ses->userName);
5521 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5522 return ERROR_SUCCESS;
5524 case INTERNET_OPTION_PASSWORD:
5526 heap_free(ses->password);
5527 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5528 return ERROR_SUCCESS;
5530 case INTERNET_OPTION_CONNECT_TIMEOUT:
5532 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5533 ses->connect_timeout = *(DWORD *)buffer;
5534 return ERROR_SUCCESS;
5536 case INTERNET_OPTION_SEND_TIMEOUT:
5538 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5539 ses->send_timeout = *(DWORD *)buffer;
5540 return ERROR_SUCCESS;
5542 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5544 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5545 ses->receive_timeout = *(DWORD *)buffer;
5546 return ERROR_SUCCESS;
5551 return INET_SetOption(hdr, option, buffer, size);
5554 static const object_vtbl_t HTTPSESSIONVtbl = {
5555 HTTPSESSION_Destroy,
5557 HTTPSESSION_QueryOption,
5558 HTTPSESSION_SetOption,
5567 /***********************************************************************
5568 * HTTP_Connect (internal)
5570 * Create http session handle
5573 * HINTERNET a session handle on success
5577 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5578 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5579 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5580 DWORD dwInternalFlags, HINTERNET *ret)
5582 http_session_t *session = NULL;
5586 if (!lpszServerName || !lpszServerName[0])
5587 return ERROR_INVALID_PARAMETER;
5589 assert( hIC->hdr.htype == WH_HINIT );
5591 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5593 return ERROR_OUTOFMEMORY;
5596 * According to my tests. The name is not resolved until a request is sent
5599 session->hdr.htype = WH_HHTTPSESSION;
5600 session->hdr.dwFlags = dwFlags;
5601 session->hdr.dwContext = dwContext;
5602 session->hdr.dwInternalFlags |= dwInternalFlags;
5604 WININET_AddRef( &hIC->hdr );
5605 session->appInfo = hIC;
5606 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5608 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5609 if(hIC->proxyBypass)
5610 FIXME("Proxy bypass is ignored.\n");
5612 session->serverName = heap_strdupW(lpszServerName);
5613 session->hostName = heap_strdupW(lpszServerName);
5614 if (lpszUserName && lpszUserName[0])
5615 session->userName = heap_strdupW(lpszUserName);
5616 if (lpszPassword && lpszPassword[0])
5617 session->password = heap_strdupW(lpszPassword);
5618 session->serverPort = serverPort;
5619 session->hostPort = serverPort;
5620 session->connect_timeout = hIC->connect_timeout;
5621 session->send_timeout = INFINITE;
5622 session->receive_timeout = INFINITE;
5624 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5625 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5627 INTERNET_SendCallback(&hIC->hdr, dwContext,
5628 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5633 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5637 TRACE("%p --> %p\n", hIC, session);
5639 *ret = session->hdr.hInternet;
5640 return ERROR_SUCCESS;
5643 /***********************************************************************
5644 * HTTP_clear_response_headers (internal)
5646 * clear out any old response headers
5648 static void HTTP_clear_response_headers( http_request_t *request )
5652 for( i=0; i<request->nCustHeaders; i++)
5654 if( !request->custHeaders[i].lpszField )
5656 if( !request->custHeaders[i].lpszValue )
5658 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5660 HTTP_DeleteCustomHeader( request, i );
5665 /***********************************************************************
5666 * HTTP_GetResponseHeaders (internal)
5668 * Read server response
5675 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5678 WCHAR buffer[MAX_REPLY_LEN];
5679 DWORD buflen = MAX_REPLY_LEN;
5680 BOOL bSuccess = FALSE;
5682 char bufferA[MAX_REPLY_LEN];
5683 LPWSTR status_code = NULL, status_text = NULL;
5684 DWORD cchMaxRawHeaders = 1024;
5685 LPWSTR lpszRawHeaders = NULL;
5687 DWORD cchRawHeaders = 0;
5688 BOOL codeHundred = FALSE;
5692 if(!request->netconn)
5695 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5697 static const WCHAR szHundred[] = {'1','0','0',0};
5699 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5701 buflen = MAX_REPLY_LEN;
5702 if (!read_line(request, bufferA, &buflen))
5705 /* clear old response headers (eg. from a redirect response) */
5707 HTTP_clear_response_headers( request );
5712 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5713 /* check is this a status code line? */
5714 if (!strncmpW(buffer, g_szHttp1_0, 4))
5716 /* split the version from the status code */
5717 status_code = strchrW( buffer, ' ' );
5722 /* split the status code from the status text */
5723 status_text = strchrW( status_code, ' ' );
5728 request->status_code = atoiW(status_code);
5730 TRACE("version [%s] status code [%s] status text [%s]\n",
5731 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5733 codeHundred = (!strcmpW(status_code, szHundred));
5735 else if (!codeHundred)
5737 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5739 heap_free(request->version);
5740 heap_free(request->statusText);
5742 request->status_code = HTTP_STATUS_OK;
5743 request->version = heap_strdupW(g_szHttp1_0);
5744 request->statusText = heap_strdupW(szOK);
5746 heap_free(request->rawHeaders);
5747 request->rawHeaders = heap_strdupW(szDefaultHeader);
5752 } while (codeHundred);
5754 /* Add status code */
5755 HTTP_ProcessHeader(request, szStatus, status_code,
5756 HTTP_ADDHDR_FLAG_REPLACE);
5758 heap_free(request->version);
5759 heap_free(request->statusText);
5761 request->version = heap_strdupW(buffer);
5762 request->statusText = heap_strdupW(status_text);
5764 /* Restore the spaces */
5765 *(status_code-1) = ' ';
5766 *(status_text-1) = ' ';
5768 /* regenerate raw headers */
5769 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5770 if (!lpszRawHeaders) goto lend;
5772 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5773 cchMaxRawHeaders *= 2;
5774 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5775 if (temp == NULL) goto lend;
5776 lpszRawHeaders = temp;
5777 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5778 cchRawHeaders += (buflen-1);
5779 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5780 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5781 lpszRawHeaders[cchRawHeaders] = '\0';
5783 /* Parse each response line */
5786 buflen = MAX_REPLY_LEN;
5787 if (read_line(request, bufferA, &buflen))
5789 LPWSTR * pFieldAndValue;
5791 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5793 if (!bufferA[0]) break;
5794 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5796 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5799 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5800 cchMaxRawHeaders *= 2;
5801 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5802 if (temp == NULL) goto lend;
5803 lpszRawHeaders = temp;
5804 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5805 cchRawHeaders += (buflen-1);
5806 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5807 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5808 lpszRawHeaders[cchRawHeaders] = '\0';
5810 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5811 HTTP_ADDREQ_FLAG_ADD );
5813 HTTP_FreeTokens(pFieldAndValue);
5824 /* make sure the response header is terminated with an empty line. Some apps really
5825 truly care about that empty line being there for some reason. Just add it to the
5827 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5829 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5830 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5831 if (temp == NULL) goto lend;
5832 lpszRawHeaders = temp;
5835 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5837 heap_free(request->rawHeaders);
5838 request->rawHeaders = lpszRawHeaders;
5839 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5849 heap_free(lpszRawHeaders);
5854 /***********************************************************************
5855 * HTTP_InterpretHttpHeader (internal)
5857 * Parse server response
5861 * Pointer to array of field, value, NULL on success.
5864 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5866 LPWSTR * pTokenPair;
5870 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5872 pszColon = strchrW(buffer, ':');
5873 /* must have two tokens */
5876 HTTP_FreeTokens(pTokenPair);
5878 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5882 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5885 HTTP_FreeTokens(pTokenPair);
5888 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5889 pTokenPair[0][pszColon - buffer] = '\0';
5893 len = strlenW(pszColon);
5894 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5897 HTTP_FreeTokens(pTokenPair);
5900 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5902 strip_spaces(pTokenPair[0]);
5903 strip_spaces(pTokenPair[1]);
5905 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5909 /***********************************************************************
5910 * HTTP_ProcessHeader (internal)
5912 * Stuff header into header tables according to <dwModifier>
5916 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5918 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5920 LPHTTPHEADERW lphttpHdr = NULL;
5922 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5923 DWORD res = ERROR_HTTP_INVALID_HEADER;
5925 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5927 /* REPLACE wins out over ADD */
5928 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5929 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5931 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5934 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5938 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5939 return ERROR_HTTP_INVALID_HEADER;
5940 lphttpHdr = &request->custHeaders[index];
5946 hdr.lpszField = (LPWSTR)field;
5947 hdr.lpszValue = (LPWSTR)value;
5948 hdr.wFlags = hdr.wCount = 0;
5950 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5951 hdr.wFlags |= HDR_ISREQUEST;
5953 return HTTP_InsertCustomHeader(request, &hdr);
5955 /* no value to delete */
5956 else return ERROR_SUCCESS;
5958 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5959 lphttpHdr->wFlags |= HDR_ISREQUEST;
5961 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5963 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5965 HTTP_DeleteCustomHeader( request, index );
5971 hdr.lpszField = (LPWSTR)field;
5972 hdr.lpszValue = (LPWSTR)value;
5973 hdr.wFlags = hdr.wCount = 0;
5975 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5976 hdr.wFlags |= HDR_ISREQUEST;
5978 return HTTP_InsertCustomHeader(request, &hdr);
5981 return ERROR_SUCCESS;
5983 else if (dwModifier & COALESCEFLAGS)
5988 INT origlen = strlenW(lphttpHdr->lpszValue);
5989 INT valuelen = strlenW(value);
5991 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5994 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5996 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5999 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6002 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6004 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6007 lphttpHdr->lpszValue = lpsztmp;
6008 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6011 lphttpHdr->lpszValue[origlen] = ch;
6013 lphttpHdr->lpszValue[origlen] = ' ';
6017 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6018 lphttpHdr->lpszValue[len] = '\0';
6019 res = ERROR_SUCCESS;
6023 WARN("heap_realloc (%d bytes) failed\n",len+1);
6024 res = ERROR_OUTOFMEMORY;
6027 TRACE("<-- %d\n", res);
6031 /***********************************************************************
6032 * HTTP_GetCustomHeaderIndex (internal)
6034 * Return index of custom header from header array
6037 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6038 int requested_index, BOOL request_only)
6042 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6044 for (index = 0; index < request->nCustHeaders; index++)
6046 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6049 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6052 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6055 if (requested_index == 0)
6060 if (index >= request->nCustHeaders)
6063 TRACE("Return: %d\n", index);
6068 /***********************************************************************
6069 * HTTP_InsertCustomHeader (internal)
6071 * Insert header into array
6074 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6077 LPHTTPHEADERW lph = NULL;
6079 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6080 count = request->nCustHeaders + 1;
6082 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6084 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6087 return ERROR_OUTOFMEMORY;
6089 request->custHeaders = lph;
6090 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6091 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6092 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6093 request->custHeaders[count-1].wCount= lpHdr->wCount;
6094 request->nCustHeaders++;
6096 return ERROR_SUCCESS;
6100 /***********************************************************************
6101 * HTTP_DeleteCustomHeader (internal)
6103 * Delete header from array
6104 * If this function is called, the indexs may change.
6106 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6108 if( request->nCustHeaders <= 0 )
6110 if( index >= request->nCustHeaders )
6112 request->nCustHeaders--;
6114 heap_free(request->custHeaders[index].lpszField);
6115 heap_free(request->custHeaders[index].lpszValue);
6117 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6118 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6119 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6125 /***********************************************************************
6126 * HTTP_VerifyValidHeader (internal)
6128 * Verify the given header is not invalid for the given http request
6131 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6133 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6134 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6135 return ERROR_HTTP_INVALID_HEADER;
6137 return ERROR_SUCCESS;
6140 /***********************************************************************
6141 * IsHostInProxyBypassList (@)
6146 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6148 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6152 /***********************************************************************
6153 * InternetShowSecurityInfoByURLA (@)
6155 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6157 FIXME("stub: %s %p\n", url, window);
6161 /***********************************************************************
6162 * InternetShowSecurityInfoByURLW (@)
6164 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6166 FIXME("stub: %s %p\n", debugstr_w(url), window);
6170 /***********************************************************************
6171 * ShowX509EncodedCertificate (@)
6173 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6175 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6181 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6183 memset(&view, 0, sizeof(view));
6184 view.hwndParent = parent;
6185 view.pCertContext = certContext;
6186 if (CryptUIDlgViewCertificateW(&view, NULL))
6187 ret = ERROR_SUCCESS;
6189 ret = GetLastError();
6190 CertFreeCertificateContext(certContext);
6193 ret = GetLastError();