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: {
2165 if (*size < sizeof(DWORD))
2166 return ERROR_INSUFFICIENT_BUFFER;
2168 /* FIXME: Add support for:
2169 * INTERNET_REQFLAG_FROM_CACHE
2170 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2173 if(req->session->appInfo->proxy)
2174 flags |= INTERNET_REQFLAG_VIA_PROXY;
2175 if(!req->rawHeaders)
2176 flags |= INTERNET_REQFLAG_NO_HEADERS;
2178 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2180 *size = sizeof(DWORD);
2181 *(DWORD*)buffer = flags;
2182 return ERROR_SUCCESS;
2186 return INET_QueryOption(hdr, option, buffer, size, unicode);
2189 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2191 http_request_t *req = (http_request_t*)hdr;
2194 case INTERNET_OPTION_SECURITY_FLAGS:
2198 if (!buffer || size != sizeof(DWORD))
2199 return ERROR_INVALID_PARAMETER;
2200 flags = *(DWORD *)buffer;
2201 TRACE("%08x\n", flags);
2202 req->security_flags = flags;
2204 req->netconn->security_flags = flags;
2205 return ERROR_SUCCESS;
2207 case INTERNET_OPTION_CONNECT_TIMEOUT:
2208 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2209 req->connect_timeout = *(DWORD *)buffer;
2210 return ERROR_SUCCESS;
2212 case INTERNET_OPTION_SEND_TIMEOUT:
2213 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2214 req->send_timeout = *(DWORD *)buffer;
2215 return ERROR_SUCCESS;
2217 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2218 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2219 req->receive_timeout = *(DWORD *)buffer;
2220 return ERROR_SUCCESS;
2222 case INTERNET_OPTION_USERNAME:
2223 heap_free(req->session->userName);
2224 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2225 return ERROR_SUCCESS;
2227 case INTERNET_OPTION_PASSWORD:
2228 heap_free(req->session->password);
2229 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2230 return ERROR_SUCCESS;
2231 case INTERNET_OPTION_HTTP_DECODING:
2232 if(size != sizeof(BOOL))
2233 return ERROR_INVALID_PARAMETER;
2234 req->decoding = *(BOOL*)buffer;
2235 return ERROR_SUCCESS;
2238 return INET_SetOption(hdr, option, buffer, size);
2241 /* read some more data into the read buffer (the read section must be held) */
2242 static DWORD read_more_data( http_request_t *req, int maxlen )
2249 /* move existing data to the start of the buffer */
2251 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2255 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2257 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2258 maxlen - req->read_size, 0, &len );
2259 if(res == ERROR_SUCCESS)
2260 req->read_size += len;
2265 /* remove some amount of data from the read buffer (the read section must be held) */
2266 static void remove_data( http_request_t *req, int count )
2268 if (!(req->read_size -= count)) req->read_pos = 0;
2269 else req->read_pos += count;
2272 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2274 int count, bytes_read, pos = 0;
2277 EnterCriticalSection( &req->read_section );
2280 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2284 count = eol - (req->read_buf + req->read_pos);
2285 bytes_read = count + 1;
2287 else count = bytes_read = req->read_size;
2289 count = min( count, *len - pos );
2290 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2292 remove_data( req, bytes_read );
2295 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2298 TRACE( "returning empty string %u\n", res);
2299 LeaveCriticalSection( &req->read_section );
2300 INTERNET_SetLastError(res);
2304 LeaveCriticalSection( &req->read_section );
2308 if (pos && buffer[pos - 1] == '\r') pos--;
2311 buffer[*len - 1] = 0;
2312 TRACE( "returning %s\n", debugstr_a(buffer));
2316 /* check if we have reached the end of the data to read (the read section must be held) */
2317 static BOOL end_of_read_data( http_request_t *req )
2319 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2322 /* fetch some more data into the read buffer (the read section must be held) */
2323 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2327 if(req->read_size == sizeof(req->read_buf))
2328 return ERROR_SUCCESS;
2332 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2336 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2337 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2338 req->read_size += read;
2340 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2346 /* return the size of data available to be read immediately (the read section must be held) */
2347 static DWORD get_avail_data( http_request_t *req )
2349 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2352 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2354 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2358 NETCON_query_data_available(req->netconn, &avail);
2359 return netconn_stream->content_length == ~0u
2361 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2364 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2366 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2367 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2370 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2371 DWORD *read, read_mode_t read_mode)
2373 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2376 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2378 if(read_mode == READMODE_NOBLOCK)
2379 size = min(size, netconn_get_avail_data(stream, req));
2381 if(size && req->netconn) {
2382 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2385 netconn_stream->content_length = netconn_stream->content_read;
2388 netconn_stream->content_read += *read = len;
2389 TRACE("read %u bytes\n", len);
2390 return ERROR_SUCCESS;
2393 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2395 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2400 if(netconn_end_of_data(stream, req))
2404 avail = netconn_get_avail_data(stream, req);
2408 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2411 netconn_stream->content_read += len;
2412 }while(netconn_stream->content_read < netconn_stream->content_length);
2417 static void netconn_destroy(data_stream_t *stream)
2421 static const data_stream_vtbl_t netconn_stream_vtbl = {
2422 netconn_get_avail_data,
2423 netconn_end_of_data,
2425 netconn_drain_content,
2429 /* read some more data into the read buffer (the read section must be held) */
2430 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2435 if (stream->buf_pos)
2437 /* move existing data to the start of the buffer */
2438 if(stream->buf_size)
2439 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2440 stream->buf_pos = 0;
2443 if (maxlen == -1) maxlen = sizeof(stream->buf);
2445 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2446 maxlen - stream->buf_size, 0, &len );
2447 if(res == ERROR_SUCCESS)
2448 stream->buf_size += len;
2453 /* remove some amount of data from the read buffer (the read section must be held) */
2454 static void remove_chunked_data(chunked_stream_t *stream, int count)
2456 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2457 else stream->buf_pos += count;
2460 /* discard data contents until we reach end of line (the read section must be held) */
2461 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2467 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2470 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2473 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2474 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2475 } while (stream->buf_size);
2476 return ERROR_SUCCESS;
2479 /* read the size of the next chunk (the read section must be held) */
2480 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2483 DWORD chunk_size = 0, res;
2485 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2490 while (stream->buf_size)
2492 char ch = stream->buf[stream->buf_pos];
2493 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2494 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2495 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2496 else if (ch == ';' || ch == '\r' || ch == '\n')
2498 TRACE( "reading %u byte chunk\n", chunk_size );
2499 stream->chunk_size = chunk_size;
2500 req->contentLength += chunk_size;
2501 return discard_chunked_eol(stream, req);
2503 remove_chunked_data(stream, 1);
2505 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2506 if (!stream->buf_size)
2508 stream->chunk_size = 0;
2509 return ERROR_SUCCESS;
2514 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2516 /* Allow reading only from read buffer */
2520 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2522 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2523 return !chunked_stream->chunk_size;
2526 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2527 DWORD *read, read_mode_t read_mode)
2529 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2530 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2532 if(chunked_stream->chunk_size == ~0u) {
2533 res = start_next_chunk(chunked_stream, req);
2534 if(res != ERROR_SUCCESS)
2538 while(size && chunked_stream->chunk_size) {
2539 if(chunked_stream->buf_size) {
2540 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2542 /* this could block */
2543 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2546 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2547 remove_chunked_data(chunked_stream, read_bytes);
2549 read_bytes = min(size, chunked_stream->chunk_size);
2551 if(read_mode == READMODE_NOBLOCK) {
2554 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2556 if(read_bytes > avail)
2559 /* this could block */
2560 if(read_bytes == chunked_stream->chunk_size)
2564 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2565 if(res != ERROR_SUCCESS)
2569 chunked_stream->chunk_size -= read_bytes;
2571 ret_read += read_bytes;
2572 if(!chunked_stream->chunk_size) {
2573 assert(read_mode != READMODE_NOBLOCK);
2574 res = start_next_chunk(chunked_stream, req);
2575 if(res != ERROR_SUCCESS)
2579 if(read_mode == READMODE_ASYNC)
2580 read_mode = READMODE_NOBLOCK;
2583 TRACE("read %u bytes\n", ret_read);
2588 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2590 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2592 /* FIXME: we can do better */
2593 return !chunked_stream->chunk_size;
2596 static void chunked_destroy(data_stream_t *stream)
2598 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2599 heap_free(chunked_stream);
2602 static const data_stream_vtbl_t chunked_stream_vtbl = {
2603 chunked_get_avail_data,
2604 chunked_end_of_data,
2606 chunked_drain_content,
2610 /* set the request content length based on the headers */
2611 static DWORD set_content_length(http_request_t *request)
2613 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2617 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2618 request->contentLength = request->netconn_stream.content_length = 0;
2619 return ERROR_SUCCESS;
2622 size = sizeof(request->contentLength);
2623 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2624 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2625 request->contentLength = ~0u;
2626 request->netconn_stream.content_length = request->contentLength;
2627 request->netconn_stream.content_read = request->read_size;
2629 size = sizeof(encoding);
2630 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2631 !strcmpiW(encoding, szChunked))
2633 chunked_stream_t *chunked_stream;
2635 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2637 return ERROR_OUTOFMEMORY;
2639 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2640 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2641 chunked_stream->chunk_size = ~0u;
2643 if(request->read_size) {
2644 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2645 chunked_stream->buf_size = request->read_size;
2646 request->read_size = request->read_pos = 0;
2649 request->data_stream = &chunked_stream->data_stream;
2650 request->contentLength = ~0u;
2651 request->read_chunked = TRUE;
2654 if(request->decoding) {
2657 static const WCHAR gzipW[] = {'g','z','i','p',0};
2659 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2660 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2661 return init_gzip_stream(request);
2664 return ERROR_SUCCESS;
2667 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2669 INTERNET_ASYNC_RESULT iar;
2671 iar.dwResult = result;
2672 iar.dwError = error;
2674 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2675 sizeof(INTERNET_ASYNC_RESULT));
2678 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2680 DWORD res, read = 0, avail = 0;
2685 EnterCriticalSection( &req->read_section );
2687 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2688 res = refill_read_buffer(req, mode, &read);
2689 if(res == ERROR_SUCCESS && !first_notif)
2690 avail = get_avail_data(req);
2692 LeaveCriticalSection( &req->read_section );
2694 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2695 WARN("res %u read %u, closing connection\n", res, read);
2696 http_release_netconn(req, FALSE);
2699 if(res == ERROR_SUCCESS)
2700 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2702 send_request_complete(req, 0, res);
2705 /* read data from the http connection (the read section must be held) */
2706 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2708 DWORD current_read = 0, ret_read = 0;
2709 read_mode_t read_mode;
2710 DWORD res = ERROR_SUCCESS;
2712 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2714 EnterCriticalSection( &req->read_section );
2716 if(req->read_size) {
2717 ret_read = min(size, req->read_size);
2718 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2719 req->read_size -= ret_read;
2720 req->read_pos += ret_read;
2721 if(read_mode == READMODE_ASYNC)
2722 read_mode = READMODE_NOBLOCK;
2725 if(ret_read < size) {
2726 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2727 ret_read += current_read;
2730 LeaveCriticalSection( &req->read_section );
2733 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2735 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2739 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2741 WARN("WriteFile failed: %u\n", GetLastError());
2744 if(size && !ret_read)
2745 http_release_netconn(req, res == ERROR_SUCCESS);
2751 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2753 http_request_t *req = (http_request_t*)hdr;
2756 EnterCriticalSection( &req->read_section );
2757 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2758 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2760 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2761 if(res == ERROR_SUCCESS)
2763 LeaveCriticalSection( &req->read_section );
2768 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2770 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2771 http_request_t *req = (http_request_t*)workRequest->hdr;
2774 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2776 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2777 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2779 send_request_complete(req, res == ERROR_SUCCESS, res);
2782 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2783 DWORD flags, DWORD_PTR context)
2785 http_request_t *req = (http_request_t*)hdr;
2786 DWORD res, size, read, error = ERROR_SUCCESS;
2788 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2789 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2791 if (buffers->dwStructSize != sizeof(*buffers))
2792 return ERROR_INVALID_PARAMETER;
2794 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2796 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2798 WORKREQUEST workRequest;
2800 if (TryEnterCriticalSection( &req->read_section ))
2802 if (get_avail_data(req))
2804 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2805 &buffers->dwBufferLength, FALSE);
2806 size = buffers->dwBufferLength;
2807 LeaveCriticalSection( &req->read_section );
2810 LeaveCriticalSection( &req->read_section );
2813 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2814 workRequest.hdr = WININET_AddRef(&req->hdr);
2815 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2817 INTERNET_AsyncCall(&workRequest);
2819 return ERROR_IO_PENDING;
2823 size = buffers->dwBufferLength;
2825 EnterCriticalSection( &req->read_section );
2826 if(hdr->dwError == ERROR_SUCCESS)
2827 hdr->dwError = INTERNET_HANDLE_IN_USE;
2828 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2829 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2832 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2833 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2834 if(res != ERROR_SUCCESS)
2837 read += buffers->dwBufferLength;
2838 if(read == size || end_of_read_data(req))
2841 LeaveCriticalSection( &req->read_section );
2843 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2844 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2845 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2846 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2848 EnterCriticalSection( &req->read_section );
2851 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2852 hdr->dwError = ERROR_SUCCESS;
2854 error = hdr->dwError;
2856 LeaveCriticalSection( &req->read_section );
2857 size = buffers->dwBufferLength;
2858 buffers->dwBufferLength = read;
2861 if (res == ERROR_SUCCESS) {
2862 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2863 &size, sizeof(size));
2866 return res==ERROR_SUCCESS ? error : res;
2869 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2871 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2872 http_request_t *req = (http_request_t*)workRequest->hdr;
2875 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2877 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2878 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2880 send_request_complete(req, res == ERROR_SUCCESS, res);
2883 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2884 DWORD flags, DWORD_PTR context)
2887 http_request_t *req = (http_request_t*)hdr;
2888 DWORD res, size, read, error = ERROR_SUCCESS;
2890 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2891 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2893 if (buffers->dwStructSize != sizeof(*buffers))
2894 return ERROR_INVALID_PARAMETER;
2896 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2898 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2900 WORKREQUEST workRequest;
2902 if (TryEnterCriticalSection( &req->read_section ))
2904 if (get_avail_data(req))
2906 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2907 &buffers->dwBufferLength, FALSE);
2908 size = buffers->dwBufferLength;
2909 LeaveCriticalSection( &req->read_section );
2912 LeaveCriticalSection( &req->read_section );
2915 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2916 workRequest.hdr = WININET_AddRef(&req->hdr);
2917 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2919 INTERNET_AsyncCall(&workRequest);
2921 return ERROR_IO_PENDING;
2925 size = buffers->dwBufferLength;
2927 EnterCriticalSection( &req->read_section );
2928 if(hdr->dwError == ERROR_SUCCESS)
2929 hdr->dwError = INTERNET_HANDLE_IN_USE;
2930 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2931 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2934 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2935 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2936 if(res != ERROR_SUCCESS)
2939 read += buffers->dwBufferLength;
2940 if(read == size || end_of_read_data(req))
2943 LeaveCriticalSection( &req->read_section );
2945 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2946 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2947 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2948 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2950 EnterCriticalSection( &req->read_section );
2953 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2954 hdr->dwError = ERROR_SUCCESS;
2956 error = hdr->dwError;
2958 LeaveCriticalSection( &req->read_section );
2959 size = buffers->dwBufferLength;
2960 buffers->dwBufferLength = read;
2963 if (res == ERROR_SUCCESS) {
2964 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2965 &size, sizeof(size));
2968 return res==ERROR_SUCCESS ? error : res;
2971 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2974 http_request_t *request = (http_request_t*)hdr;
2976 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2979 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2980 if (res == ERROR_SUCCESS)
2981 request->bytesWritten += *written;
2983 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2987 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2989 http_request_t *req = (http_request_t*)workRequest->hdr;
2991 HTTP_ReceiveRequestData(req, FALSE);
2994 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2996 http_request_t *req = (http_request_t*)hdr;
2998 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3000 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3002 WORKREQUEST workRequest;
3004 /* never wait, if we can't enter the section we queue an async request right away */
3005 if (TryEnterCriticalSection( &req->read_section ))
3007 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3008 if ((*available = get_avail_data( req ))) goto done;
3009 if (end_of_read_data( req )) goto done;
3010 LeaveCriticalSection( &req->read_section );
3013 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3014 workRequest.hdr = WININET_AddRef( &req->hdr );
3016 INTERNET_AsyncCall(&workRequest);
3018 return ERROR_IO_PENDING;
3021 EnterCriticalSection( &req->read_section );
3023 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3025 refill_read_buffer( req, READMODE_ASYNC, NULL );
3026 *available = get_avail_data( req );
3030 LeaveCriticalSection( &req->read_section );
3032 TRACE( "returning %u\n", *available );
3033 return ERROR_SUCCESS;
3036 static const object_vtbl_t HTTPREQVtbl = {
3038 HTTPREQ_CloseConnection,
3039 HTTPREQ_QueryOption,
3042 HTTPREQ_ReadFileExA,
3043 HTTPREQ_ReadFileExW,
3045 HTTPREQ_QueryDataAvailable,
3049 /***********************************************************************
3050 * HTTP_HttpOpenRequestW (internal)
3052 * Open a HTTP request handle
3055 * HINTERNET a HTTP request handle on success
3059 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3060 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3061 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3062 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3064 appinfo_t *hIC = session->appInfo;
3065 http_request_t *request;
3066 DWORD len, res = ERROR_SUCCESS;
3070 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3072 return ERROR_OUTOFMEMORY;
3074 request->hdr.htype = WH_HHTTPREQ;
3075 request->hdr.dwFlags = dwFlags;
3076 request->hdr.dwContext = dwContext;
3077 request->contentLength = ~0u;
3079 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3080 request->data_stream = &request->netconn_stream.data_stream;
3081 request->connect_timeout = session->connect_timeout;
3082 request->send_timeout = session->send_timeout;
3083 request->receive_timeout = session->receive_timeout;
3085 InitializeCriticalSection( &request->read_section );
3086 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3088 WININET_AddRef( &session->hdr );
3089 request->session = session;
3090 list_add_head( &session->hdr.children, &request->hdr.entry );
3092 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3093 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3094 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3095 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3097 if (lpszObjectName && *lpszObjectName) {
3101 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3102 if (rc != E_POINTER)
3103 len = strlenW(lpszObjectName)+1;
3104 request->path = heap_alloc(len*sizeof(WCHAR));
3105 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3106 URL_ESCAPE_SPACES_ONLY);
3109 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3110 strcpyW(request->path,lpszObjectName);
3113 static const WCHAR slashW[] = {'/',0};
3115 request->path = heap_strdupW(slashW);
3118 if (lpszReferrer && *lpszReferrer)
3119 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3121 if (lpszAcceptTypes)
3124 for (i = 0; lpszAcceptTypes[i]; i++)
3126 if (!*lpszAcceptTypes[i]) continue;
3127 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3128 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3129 HTTP_ADDHDR_FLAG_REQ |
3130 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3134 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3135 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3137 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3138 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3139 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3143 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3145 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3147 res = ERROR_OUTOFMEMORY;
3151 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3152 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3153 heap_free(host_name);
3156 HTTP_ProcessHeader(request, hostW, session->hostName,
3157 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3159 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3160 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3161 INTERNET_DEFAULT_HTTPS_PORT :
3162 INTERNET_DEFAULT_HTTP_PORT);
3164 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3165 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3166 INTERNET_DEFAULT_HTTPS_PORT :
3167 INTERNET_DEFAULT_HTTP_PORT);
3169 if (hIC->proxy && hIC->proxy[0])
3170 HTTP_DealWithProxy( hIC, session, request );
3172 INTERNET_SendCallback(&session->hdr, dwContext,
3173 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3177 TRACE("<-- %u (%p)\n", res, request);
3179 if(res != ERROR_SUCCESS) {
3180 WININET_Release( &request->hdr );
3185 *ret = request->hdr.hInternet;
3186 return ERROR_SUCCESS;
3189 /***********************************************************************
3190 * HttpOpenRequestW (WININET.@)
3192 * Open a HTTP request handle
3195 * HINTERNET a HTTP request handle on success
3199 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3200 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3201 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3202 DWORD dwFlags, DWORD_PTR dwContext)
3204 http_session_t *session;
3205 HINTERNET handle = NULL;
3208 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3209 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3210 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3211 dwFlags, dwContext);
3212 if(lpszAcceptTypes!=NULL)
3215 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3216 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3219 session = (http_session_t*) get_handle_object( hHttpSession );
3220 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3222 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3227 * My tests seem to show that the windows version does not
3228 * become asynchronous until after this point. And anyhow
3229 * if this call was asynchronous then how would you get the
3230 * necessary HINTERNET pointer returned by this function.
3233 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3234 lpszVersion, lpszReferrer, lpszAcceptTypes,
3235 dwFlags, dwContext, &handle);
3238 WININET_Release( &session->hdr );
3239 TRACE("returning %p\n", handle);
3240 if(res != ERROR_SUCCESS)
3245 static const LPCWSTR header_lookup[] = {
3246 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3247 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3248 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3249 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3250 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3251 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3252 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3253 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3254 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3255 szDate, /* HTTP_QUERY_DATE = 9 */
3256 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3257 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3258 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3259 szURI, /* HTTP_QUERY_URI = 13 */
3260 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3261 NULL, /* HTTP_QUERY_COST = 15 */
3262 NULL, /* HTTP_QUERY_LINK = 16 */
3263 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3264 NULL, /* HTTP_QUERY_VERSION = 18 */
3265 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3266 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3267 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3268 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3269 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3270 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3271 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3272 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3273 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3274 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3275 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3276 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3277 NULL, /* HTTP_QUERY_FROM = 31 */
3278 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3279 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3280 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3281 szReferer, /* HTTP_QUERY_REFERER = 35 */
3282 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3283 szServer, /* HTTP_QUERY_SERVER = 37 */
3284 NULL, /* HTTP_TITLE = 38 */
3285 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3286 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3287 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3288 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3289 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3290 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3291 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3292 NULL, /* HTTP_QUERY_REFRESH = 46 */
3293 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3294 szAge, /* HTTP_QUERY_AGE = 48 */
3295 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3296 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3297 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3298 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3299 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3300 szETag, /* HTTP_QUERY_ETAG = 54 */
3301 hostW, /* HTTP_QUERY_HOST = 55 */
3302 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3303 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3304 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3305 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3306 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3307 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3308 szRange, /* HTTP_QUERY_RANGE = 62 */
3309 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3310 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3311 szVary, /* HTTP_QUERY_VARY = 65 */
3312 szVia, /* HTTP_QUERY_VIA = 66 */
3313 szWarning, /* HTTP_QUERY_WARNING = 67 */
3314 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3315 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3316 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3319 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3321 /***********************************************************************
3322 * HTTP_HttpQueryInfoW (internal)
3324 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3325 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3327 LPHTTPHEADERW lphttpHdr = NULL;
3328 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3329 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3330 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3333 /* Find requested header structure */
3336 case HTTP_QUERY_CUSTOM:
3337 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3338 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3340 case HTTP_QUERY_RAW_HEADERS_CRLF:
3344 DWORD res = ERROR_INVALID_PARAMETER;
3347 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3349 headers = request->rawHeaders;
3352 len = strlenW(headers) * sizeof(WCHAR);
3354 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3356 len += sizeof(WCHAR);
3357 res = ERROR_INSUFFICIENT_BUFFER;
3362 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3365 len = strlenW(szCrLf) * sizeof(WCHAR);
3366 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3368 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3369 res = ERROR_SUCCESS;
3371 *lpdwBufferLength = len;
3373 if (request_only) heap_free(headers);
3376 case HTTP_QUERY_RAW_HEADERS:
3378 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3380 LPWSTR pszString = lpBuffer;
3382 for (i = 0; ppszRawHeaderLines[i]; i++)
3383 size += strlenW(ppszRawHeaderLines[i]) + 1;
3385 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3387 HTTP_FreeTokens(ppszRawHeaderLines);
3388 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3389 return ERROR_INSUFFICIENT_BUFFER;
3393 for (i = 0; ppszRawHeaderLines[i]; i++)
3395 DWORD len = strlenW(ppszRawHeaderLines[i]);
3396 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3400 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3402 *lpdwBufferLength = size * sizeof(WCHAR);
3403 HTTP_FreeTokens(ppszRawHeaderLines);
3405 return ERROR_SUCCESS;
3407 case HTTP_QUERY_STATUS_TEXT:
3408 if (request->statusText)
3410 DWORD len = strlenW(request->statusText);
3411 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3413 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3414 return ERROR_INSUFFICIENT_BUFFER;
3418 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3419 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3421 *lpdwBufferLength = len * sizeof(WCHAR);
3422 return ERROR_SUCCESS;
3425 case HTTP_QUERY_VERSION:
3426 if (request->version)
3428 DWORD len = strlenW(request->version);
3429 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3431 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3432 return ERROR_INSUFFICIENT_BUFFER;
3436 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3437 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3439 *lpdwBufferLength = len * sizeof(WCHAR);
3440 return ERROR_SUCCESS;
3443 case HTTP_QUERY_CONTENT_ENCODING:
3444 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3445 requested_index,request_only);
3447 case HTTP_QUERY_STATUS_CODE: {
3448 DWORD res = ERROR_SUCCESS;
3451 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3456 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3457 if(*lpdwBufferLength >= sizeof(DWORD))
3458 *(DWORD*)lpBuffer = request->status_code;
3460 res = ERROR_INSUFFICIENT_BUFFER;
3461 *lpdwBufferLength = sizeof(DWORD);
3465 static const WCHAR formatW[] = {'%','u',0};
3467 size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3469 if(size <= *lpdwBufferLength)
3470 memcpy(lpBuffer, buf, size);
3472 res = ERROR_INSUFFICIENT_BUFFER;
3474 *lpdwBufferLength = size;
3479 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3481 if (level < LAST_TABLE_HEADER && header_lookup[level])
3482 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3483 requested_index,request_only);
3487 lphttpHdr = &request->custHeaders[index];
3489 /* Ensure header satisfies requested attributes */
3491 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3492 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3494 return ERROR_HTTP_HEADER_NOT_FOUND;
3497 if (lpdwIndex) (*lpdwIndex)++;
3499 /* coalesce value to requested type */
3500 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3502 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3503 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3505 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3511 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3513 tmpTM = *gmtime(&tmpTime);
3514 STHook = (SYSTEMTIME *)lpBuffer;
3515 STHook->wDay = tmpTM.tm_mday;
3516 STHook->wHour = tmpTM.tm_hour;
3517 STHook->wMilliseconds = 0;
3518 STHook->wMinute = tmpTM.tm_min;
3519 STHook->wDayOfWeek = tmpTM.tm_wday;
3520 STHook->wMonth = tmpTM.tm_mon + 1;
3521 STHook->wSecond = tmpTM.tm_sec;
3522 STHook->wYear = tmpTM.tm_year;
3524 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3525 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3526 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3528 else if (lphttpHdr->lpszValue)
3530 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3532 if (len > *lpdwBufferLength)
3534 *lpdwBufferLength = len;
3535 return ERROR_INSUFFICIENT_BUFFER;
3539 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3540 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3542 *lpdwBufferLength = len - sizeof(WCHAR);
3544 return ERROR_SUCCESS;
3547 /***********************************************************************
3548 * HttpQueryInfoW (WININET.@)
3550 * Queries for information about an HTTP request
3557 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3558 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3560 http_request_t *request;
3563 if (TRACE_ON(wininet)) {
3564 #define FE(x) { x, #x }
3565 static const wininet_flag_info query_flags[] = {
3566 FE(HTTP_QUERY_MIME_VERSION),
3567 FE(HTTP_QUERY_CONTENT_TYPE),
3568 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3569 FE(HTTP_QUERY_CONTENT_ID),
3570 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3571 FE(HTTP_QUERY_CONTENT_LENGTH),
3572 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3573 FE(HTTP_QUERY_ALLOW),
3574 FE(HTTP_QUERY_PUBLIC),
3575 FE(HTTP_QUERY_DATE),
3576 FE(HTTP_QUERY_EXPIRES),
3577 FE(HTTP_QUERY_LAST_MODIFIED),
3578 FE(HTTP_QUERY_MESSAGE_ID),
3580 FE(HTTP_QUERY_DERIVED_FROM),
3581 FE(HTTP_QUERY_COST),
3582 FE(HTTP_QUERY_LINK),
3583 FE(HTTP_QUERY_PRAGMA),
3584 FE(HTTP_QUERY_VERSION),
3585 FE(HTTP_QUERY_STATUS_CODE),
3586 FE(HTTP_QUERY_STATUS_TEXT),
3587 FE(HTTP_QUERY_RAW_HEADERS),
3588 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3589 FE(HTTP_QUERY_CONNECTION),
3590 FE(HTTP_QUERY_ACCEPT),
3591 FE(HTTP_QUERY_ACCEPT_CHARSET),
3592 FE(HTTP_QUERY_ACCEPT_ENCODING),
3593 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3594 FE(HTTP_QUERY_AUTHORIZATION),
3595 FE(HTTP_QUERY_CONTENT_ENCODING),
3596 FE(HTTP_QUERY_FORWARDED),
3597 FE(HTTP_QUERY_FROM),
3598 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3599 FE(HTTP_QUERY_LOCATION),
3600 FE(HTTP_QUERY_ORIG_URI),
3601 FE(HTTP_QUERY_REFERER),
3602 FE(HTTP_QUERY_RETRY_AFTER),
3603 FE(HTTP_QUERY_SERVER),
3604 FE(HTTP_QUERY_TITLE),
3605 FE(HTTP_QUERY_USER_AGENT),
3606 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3607 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3608 FE(HTTP_QUERY_ACCEPT_RANGES),
3609 FE(HTTP_QUERY_SET_COOKIE),
3610 FE(HTTP_QUERY_COOKIE),
3611 FE(HTTP_QUERY_REQUEST_METHOD),
3612 FE(HTTP_QUERY_REFRESH),
3613 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3615 FE(HTTP_QUERY_CACHE_CONTROL),
3616 FE(HTTP_QUERY_CONTENT_BASE),
3617 FE(HTTP_QUERY_CONTENT_LOCATION),
3618 FE(HTTP_QUERY_CONTENT_MD5),
3619 FE(HTTP_QUERY_CONTENT_RANGE),
3620 FE(HTTP_QUERY_ETAG),
3621 FE(HTTP_QUERY_HOST),
3622 FE(HTTP_QUERY_IF_MATCH),
3623 FE(HTTP_QUERY_IF_NONE_MATCH),
3624 FE(HTTP_QUERY_IF_RANGE),
3625 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3626 FE(HTTP_QUERY_MAX_FORWARDS),
3627 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3628 FE(HTTP_QUERY_RANGE),
3629 FE(HTTP_QUERY_TRANSFER_ENCODING),
3630 FE(HTTP_QUERY_UPGRADE),
3631 FE(HTTP_QUERY_VARY),
3633 FE(HTTP_QUERY_WARNING),
3634 FE(HTTP_QUERY_CUSTOM)
3636 static const wininet_flag_info modifier_flags[] = {
3637 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3638 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3639 FE(HTTP_QUERY_FLAG_NUMBER),
3640 FE(HTTP_QUERY_FLAG_COALESCE)
3643 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3644 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3647 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3648 TRACE(" Attribute:");
3649 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3650 if (query_flags[i].val == info) {
3651 TRACE(" %s", query_flags[i].name);
3655 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3656 TRACE(" Unknown (%08x)", info);
3659 TRACE(" Modifier:");
3660 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3661 if (modifier_flags[i].val & info_mod) {
3662 TRACE(" %s", modifier_flags[i].name);
3663 info_mod &= ~ modifier_flags[i].val;
3668 TRACE(" Unknown (%08x)", info_mod);
3673 request = (http_request_t*) get_handle_object( hHttpRequest );
3674 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3676 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3680 if (lpBuffer == NULL)
3681 *lpdwBufferLength = 0;
3682 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3683 lpBuffer, lpdwBufferLength, lpdwIndex);
3687 WININET_Release( &request->hdr );
3689 TRACE("%u <--\n", res);
3690 if(res != ERROR_SUCCESS)
3692 return res == ERROR_SUCCESS;
3695 /***********************************************************************
3696 * HttpQueryInfoA (WININET.@)
3698 * Queries for information about an HTTP request
3705 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3706 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3712 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3713 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3715 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3716 lpdwBufferLength, lpdwIndex );
3722 len = (*lpdwBufferLength)*sizeof(WCHAR);
3723 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3725 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3731 bufferW = heap_alloc(alloclen);
3732 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3733 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3734 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3741 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3745 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3746 lpBuffer, *lpdwBufferLength, NULL, NULL );
3747 *lpdwBufferLength = len - 1;
3749 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3752 /* since the strings being returned from HttpQueryInfoW should be
3753 * only ASCII characters, it is reasonable to assume that all of
3754 * the Unicode characters can be reduced to a single byte */
3755 *lpdwBufferLength = len / sizeof(WCHAR);
3757 heap_free( bufferW );
3761 /***********************************************************************
3762 * HTTP_GetRedirectURL (internal)
3764 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3766 static WCHAR szHttp[] = {'h','t','t','p',0};
3767 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3768 http_session_t *session = request->session;
3769 URL_COMPONENTSW urlComponents;
3770 DWORD url_length = 0;
3772 LPWSTR combined_url;
3774 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3775 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3776 urlComponents.dwSchemeLength = 0;
3777 urlComponents.lpszHostName = session->hostName;
3778 urlComponents.dwHostNameLength = 0;
3779 urlComponents.nPort = session->hostPort;
3780 urlComponents.lpszUserName = session->userName;
3781 urlComponents.dwUserNameLength = 0;
3782 urlComponents.lpszPassword = NULL;
3783 urlComponents.dwPasswordLength = 0;
3784 urlComponents.lpszUrlPath = request->path;
3785 urlComponents.dwUrlPathLength = 0;
3786 urlComponents.lpszExtraInfo = NULL;
3787 urlComponents.dwExtraInfoLength = 0;
3789 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3790 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3793 orig_url = heap_alloc(url_length);
3795 /* convert from bytes to characters */
3796 url_length = url_length / sizeof(WCHAR) - 1;
3797 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3799 heap_free(orig_url);
3804 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3805 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3807 heap_free(orig_url);
3810 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3812 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3814 heap_free(orig_url);
3815 heap_free(combined_url);
3818 heap_free(orig_url);
3819 return combined_url;
3823 /***********************************************************************
3824 * HTTP_HandleRedirect (internal)
3826 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3828 http_session_t *session = request->session;
3829 appinfo_t *hIC = session->appInfo;
3830 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3831 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3836 /* if it's an absolute path, keep the same session info */
3837 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3841 URL_COMPONENTSW urlComponents;
3842 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3843 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3844 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3845 BOOL custom_port = FALSE;
3847 static WCHAR httpW[] = {'h','t','t','p',0};
3848 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3854 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3855 urlComponents.lpszScheme = protocol;
3856 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3857 urlComponents.lpszHostName = hostName;
3858 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3859 urlComponents.lpszUserName = userName;
3860 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3861 urlComponents.lpszPassword = NULL;
3862 urlComponents.dwPasswordLength = 0;
3863 urlComponents.lpszUrlPath = path;
3864 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3865 urlComponents.lpszExtraInfo = NULL;
3866 urlComponents.dwExtraInfoLength = 0;
3867 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3868 return INTERNET_GetLastError();
3870 if(!strcmpiW(protocol, httpW)) {
3871 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3872 TRACE("redirect from secure page to non-secure page\n");
3873 /* FIXME: warn about from secure redirect to non-secure page */
3874 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3877 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3878 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3879 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3881 }else if(!strcmpiW(protocol, httpsW)) {
3882 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3883 TRACE("redirect from non-secure page to secure page\n");
3884 /* FIXME: notify about redirect to secure page */
3885 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3888 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3889 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3890 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3894 heap_free(session->hostName);
3898 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3899 len = lstrlenW(hostName);
3900 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3901 session->hostName = heap_alloc(len*sizeof(WCHAR));
3902 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3905 session->hostName = heap_strdupW(hostName);
3907 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3909 heap_free(session->userName);
3910 session->userName = NULL;
3912 session->userName = heap_strdupW(userName);
3914 reset_data_stream(request);
3917 if(strcmpiW(session->serverName, hostName)) {
3918 heap_free(session->serverName);
3919 session->serverName = heap_strdupW(hostName);
3921 session->serverPort = urlComponents.nPort;
3924 heap_free(request->path);
3931 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3932 if (rc != E_POINTER)
3933 needed = strlenW(path)+1;
3934 request->path = heap_alloc(needed*sizeof(WCHAR));
3935 rc = UrlEscapeW(path, request->path, &needed,
3936 URL_ESCAPE_SPACES_ONLY);
3939 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3940 strcpyW(request->path,path);
3944 /* Remove custom content-type/length headers on redirects. */
3945 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3947 HTTP_DeleteCustomHeader(request, index);
3948 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3950 HTTP_DeleteCustomHeader(request, index);
3952 return ERROR_SUCCESS;
3955 /***********************************************************************
3956 * HTTP_build_req (internal)
3958 * concatenate all the strings in the request together
3960 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3965 for( t = list; *t ; t++ )
3966 len += strlenW( *t );
3969 str = heap_alloc(len*sizeof(WCHAR));
3972 for( t = list; *t ; t++ )
3978 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3981 LPWSTR requestString;
3987 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3988 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3989 http_session_t *session = request->session;
3993 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3994 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3995 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3996 heap_free( lpszPath );
3998 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3999 NULL, 0, NULL, NULL );
4000 len--; /* the nul terminator isn't needed */
4001 ascii_req = heap_alloc(len);
4002 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4003 heap_free( requestString );
4005 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4007 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4008 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4009 heap_free( ascii_req );
4010 if (res != ERROR_SUCCESS)
4013 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4015 return ERROR_HTTP_INVALID_HEADER;
4017 return ERROR_SUCCESS;
4020 static void HTTP_InsertCookies(http_request_t *request)
4022 DWORD cookie_size, size, cnt = 0;
4026 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4028 host = HTTP_GetHeader(request, hostW);
4032 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4035 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4036 if(!(cookies = heap_alloc(size)))
4039 cnt += sprintfW(cookies, cookieW);
4040 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4041 strcatW(cookies, szCrLf);
4043 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4048 static WORD HTTP_ParseWkday(LPCWSTR day)
4050 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4058 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4059 if (!strcmpiW(day, days[i]))
4066 static WORD HTTP_ParseMonth(LPCWSTR month)
4068 static const WCHAR jan[] = { 'j','a','n',0 };
4069 static const WCHAR feb[] = { 'f','e','b',0 };
4070 static const WCHAR mar[] = { 'm','a','r',0 };
4071 static const WCHAR apr[] = { 'a','p','r',0 };
4072 static const WCHAR may[] = { 'm','a','y',0 };
4073 static const WCHAR jun[] = { 'j','u','n',0 };
4074 static const WCHAR jul[] = { 'j','u','l',0 };
4075 static const WCHAR aug[] = { 'a','u','g',0 };
4076 static const WCHAR sep[] = { 's','e','p',0 };
4077 static const WCHAR oct[] = { 'o','c','t',0 };
4078 static const WCHAR nov[] = { 'n','o','v',0 };
4079 static const WCHAR dec[] = { 'd','e','c',0 };
4081 if (!strcmpiW(month, jan)) return 1;
4082 if (!strcmpiW(month, feb)) return 2;
4083 if (!strcmpiW(month, mar)) return 3;
4084 if (!strcmpiW(month, apr)) return 4;
4085 if (!strcmpiW(month, may)) return 5;
4086 if (!strcmpiW(month, jun)) return 6;
4087 if (!strcmpiW(month, jul)) return 7;
4088 if (!strcmpiW(month, aug)) return 8;
4089 if (!strcmpiW(month, sep)) return 9;
4090 if (!strcmpiW(month, oct)) return 10;
4091 if (!strcmpiW(month, nov)) return 11;
4092 if (!strcmpiW(month, dec)) return 12;
4097 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4098 * optionally preceded by whitespace.
4099 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4100 * st, and sets *str to the first character after the time format.
4102 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4108 while (isspaceW(*ptr))
4111 num = strtoulW(ptr, &nextPtr, 10);
4112 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4114 ERR("unexpected time format %s\n", debugstr_w(ptr));
4119 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4123 st->wHour = (WORD)num;
4124 num = strtoulW(ptr, &nextPtr, 10);
4125 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4127 ERR("unexpected time format %s\n", debugstr_w(ptr));
4132 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4136 st->wMinute = (WORD)num;
4137 num = strtoulW(ptr, &nextPtr, 10);
4138 if (!nextPtr || nextPtr <= ptr)
4140 ERR("unexpected time format %s\n", debugstr_w(ptr));
4145 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4150 st->wSecond = (WORD)num;
4154 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4156 static const WCHAR gmt[]= { 'G','M','T',0 };
4157 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4159 SYSTEMTIME st = { 0 };
4162 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4163 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4166 st.wDayOfWeek = HTTP_ParseWkday(day);
4167 if (st.wDayOfWeek >= 7)
4169 ERR("unexpected weekday %s\n", debugstr_w(day));
4173 while (isspaceW(*ptr))
4176 for (monthPtr = month; !isspace(*ptr) &&
4177 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4181 st.wMonth = HTTP_ParseMonth(month);
4182 if (!st.wMonth || st.wMonth > 12)
4184 ERR("unexpected month %s\n", debugstr_w(month));
4188 while (isspaceW(*ptr))
4191 num = strtoulW(ptr, &nextPtr, 10);
4192 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4194 ERR("unexpected day %s\n", debugstr_w(ptr));
4198 st.wDay = (WORD)num;
4200 while (isspaceW(*ptr))
4203 if (!HTTP_ParseTime(&st, &ptr))
4206 while (isspaceW(*ptr))
4209 num = strtoulW(ptr, &nextPtr, 10);
4210 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4212 ERR("unexpected year %s\n", debugstr_w(ptr));
4216 st.wYear = (WORD)num;
4218 while (isspaceW(*ptr))
4221 /* asctime() doesn't report a timezone, but some web servers do, so accept
4222 * with or without GMT.
4224 if (*ptr && strcmpW(ptr, gmt))
4226 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4229 return SystemTimeToFileTime(&st, ft);
4232 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4234 static const WCHAR gmt[]= { 'G','M','T',0 };
4235 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4238 SYSTEMTIME st = { 0 };
4240 ptr = strchrW(value, ',');
4243 if (ptr - value != 3)
4245 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4248 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4250 st.wDayOfWeek = HTTP_ParseWkday(day);
4251 if (st.wDayOfWeek > 6)
4253 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4258 while (isspaceW(*ptr))
4261 num = strtoulW(ptr, &nextPtr, 10);
4262 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4264 WARN("unexpected day %s\n", debugstr_w(value));
4268 st.wDay = (WORD)num;
4270 while (isspaceW(*ptr))
4273 for (monthPtr = month; !isspace(*ptr) &&
4274 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4278 st.wMonth = HTTP_ParseMonth(month);
4279 if (!st.wMonth || st.wMonth > 12)
4281 WARN("unexpected month %s\n", debugstr_w(month));
4285 while (isspaceW(*ptr))
4288 num = strtoulW(ptr, &nextPtr, 10);
4289 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4291 ERR("unexpected year %s\n", debugstr_w(value));
4295 st.wYear = (WORD)num;
4297 if (!HTTP_ParseTime(&st, &ptr))
4300 while (isspaceW(*ptr))
4303 if (strcmpW(ptr, gmt))
4305 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4308 return SystemTimeToFileTime(&st, ft);
4311 static WORD HTTP_ParseWeekday(LPCWSTR day)
4313 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4314 { 'm','o','n','d','a','y',0 },
4315 { 't','u','e','s','d','a','y',0 },
4316 { 'w','e','d','n','e','s','d','a','y',0 },
4317 { 't','h','u','r','s','d','a','y',0 },
4318 { 'f','r','i','d','a','y',0 },
4319 { 's','a','t','u','r','d','a','y',0 }};
4321 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4322 if (!strcmpiW(day, days[i]))
4329 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4331 static const WCHAR gmt[]= { 'G','M','T',0 };
4332 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4335 SYSTEMTIME st = { 0 };
4337 ptr = strchrW(value, ',');
4340 if (ptr - value == 3)
4342 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4344 st.wDayOfWeek = HTTP_ParseWkday(day);
4345 if (st.wDayOfWeek > 6)
4347 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4351 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4353 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4354 day[ptr - value + 1] = 0;
4355 st.wDayOfWeek = HTTP_ParseWeekday(day);
4356 if (st.wDayOfWeek > 6)
4358 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4364 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4369 while (isspaceW(*ptr))
4372 num = strtoulW(ptr, &nextPtr, 10);
4373 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4375 ERR("unexpected day %s\n", debugstr_w(value));
4379 st.wDay = (WORD)num;
4383 ERR("unexpected month format %s\n", debugstr_w(ptr));
4388 for (monthPtr = month; *ptr != '-' &&
4389 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4393 st.wMonth = HTTP_ParseMonth(month);
4394 if (!st.wMonth || st.wMonth > 12)
4396 ERR("unexpected month %s\n", debugstr_w(month));
4402 ERR("unexpected year format %s\n", debugstr_w(ptr));
4407 num = strtoulW(ptr, &nextPtr, 10);
4408 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4410 ERR("unexpected year %s\n", debugstr_w(value));
4414 st.wYear = (WORD)num;
4416 if (!HTTP_ParseTime(&st, &ptr))
4419 while (isspaceW(*ptr))
4422 if (strcmpW(ptr, gmt))
4424 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4427 return SystemTimeToFileTime(&st, ft);
4430 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4432 static const WCHAR zero[] = { '0',0 };
4435 if (!strcmpW(value, zero))
4437 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4440 else if (strchrW(value, ','))
4442 ret = HTTP_ParseRfc1123Date(value, ft);
4445 ret = HTTP_ParseRfc850Date(value, ft);
4447 ERR("unexpected date format %s\n", debugstr_w(value));
4452 ret = HTTP_ParseDateAsAsctime(value, ft);
4454 ERR("unexpected date format %s\n", debugstr_w(value));
4459 static void HTTP_ProcessExpires(http_request_t *request)
4461 BOOL expirationFound = FALSE;
4464 /* Look for a Cache-Control header with a max-age directive, as it takes
4465 * precedence over the Expires header.
4467 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4468 if (headerIndex != -1)
4470 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4473 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4475 LPWSTR comma = strchrW(ptr, ','), end, equal;
4480 end = ptr + strlenW(ptr);
4481 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4485 static const WCHAR max_age[] = {
4486 'm','a','x','-','a','g','e',0 };
4488 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4493 age = strtoulW(equal + 1, &nextPtr, 10);
4494 if (nextPtr > equal + 1)
4498 NtQuerySystemTime( &ft );
4499 /* Age is in seconds, FILETIME resolution is in
4500 * 100 nanosecond intervals.
4502 ft.QuadPart += age * (ULONGLONG)1000000;
4503 request->expires.dwLowDateTime = ft.u.LowPart;
4504 request->expires.dwHighDateTime = ft.u.HighPart;
4505 expirationFound = TRUE;
4512 while (isspaceW(*ptr))
4519 if (!expirationFound)
4521 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4522 if (headerIndex != -1)
4524 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4527 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4529 expirationFound = TRUE;
4530 request->expires = ft;
4534 if (!expirationFound)
4538 /* With no known age, default to 10 minutes until expiration. */
4539 NtQuerySystemTime( &t );
4540 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4541 request->expires.dwLowDateTime = t.u.LowPart;
4542 request->expires.dwHighDateTime = t.u.HighPart;
4546 static void HTTP_ProcessLastModified(http_request_t *request)
4550 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4551 if (headerIndex != -1)
4553 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4556 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4557 request->last_modified = ft;
4561 static void http_process_keep_alive(http_request_t *req)
4565 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4567 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4569 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4572 static void HTTP_CacheRequest(http_request_t *request)
4574 WCHAR url[INTERNET_MAX_URL_LENGTH];
4575 WCHAR cacheFileName[MAX_PATH+1];
4578 b = HTTP_GetRequestURL(request, url);
4580 WARN("Could not get URL\n");
4584 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4586 heap_free(request->cacheFile);
4587 CloseHandle(request->hCacheFile);
4589 request->cacheFile = heap_strdupW(cacheFileName);
4590 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4591 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4592 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4593 WARN("Could not create file: %u\n", GetLastError());
4594 request->hCacheFile = NULL;
4597 WARN("Could not create cache entry: %08x\n", GetLastError());
4601 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4603 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4604 http_session_t *session = request->session;
4605 netconn_t *netconn = NULL;
4609 assert(!request->netconn);
4610 reset_data_stream(request);
4612 server = get_server(session->serverName, session->serverPort);
4614 return ERROR_OUTOFMEMORY;
4616 res = HTTP_ResolveName(request, server);
4617 if(res != ERROR_SUCCESS) {
4618 server_release(server);
4622 EnterCriticalSection(&connection_pool_cs);
4624 while(!list_empty(&server->conn_pool)) {
4625 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4626 list_remove(&netconn->pool_entry);
4628 if(NETCON_is_alive(netconn))
4631 TRACE("connection %p closed during idle\n", netconn);
4632 free_netconn(netconn);
4636 LeaveCriticalSection(&connection_pool_cs);
4639 TRACE("<-- reusing %p netconn\n", netconn);
4640 request->netconn = netconn;
4642 return ERROR_SUCCESS;
4645 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4646 INTERNET_STATUS_CONNECTING_TO_SERVER,
4648 strlen(server->addr_str)+1);
4650 res = create_netconn(is_https, server, request->security_flags, request->connect_timeout, &netconn);
4651 server_release(server);
4652 if(res != ERROR_SUCCESS) {
4653 ERR("create_netconn failed: %u\n", res);
4657 request->netconn = netconn;
4659 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4660 INTERNET_STATUS_CONNECTED_TO_SERVER,
4661 server->addr_str, strlen(server->addr_str)+1);
4664 /* Note: we differ from Microsoft's WinINet here. they seem to have
4665 * a bug that causes no status callbacks to be sent when starting
4666 * a tunnel to a proxy server using the CONNECT verb. i believe our
4667 * behaviour to be more correct and to not cause any incompatibilities
4668 * because using a secure connection through a proxy server is a rare
4669 * case that would be hard for anyone to depend on */
4670 if(session->appInfo->proxy)
4671 res = HTTP_SecureProxyConnect(request);
4672 if(res == ERROR_SUCCESS)
4673 res = NETCON_secure_connect(request->netconn);
4674 if(res != ERROR_SUCCESS)
4676 WARN("Couldn't connect securely to host\n");
4678 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4679 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4680 || res == ERROR_INTERNET_INVALID_CA
4681 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4682 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4683 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4684 || res == ERROR_INTERNET_SEC_INVALID_CERT
4685 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4686 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4690 if(res != ERROR_SUCCESS) {
4691 http_release_netconn(request, FALSE);
4696 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4697 return ERROR_SUCCESS;
4700 /***********************************************************************
4701 * HTTP_HttpSendRequestW (internal)
4703 * Sends the specified request to the HTTP server
4706 * ERROR_SUCCESS on success
4707 * win32 error code on failure
4710 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4711 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4712 DWORD dwContentLength, BOOL bEndRequest)
4715 BOOL redirected = FALSE;
4716 LPWSTR requestString = NULL;
4719 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4720 static const WCHAR szContentLength[] =
4721 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4722 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4725 TRACE("--> %p\n", request);
4727 assert(request->hdr.htype == WH_HHTTPREQ);
4729 /* if the verb is NULL default to GET */
4731 request->verb = heap_strdupW(szGET);
4733 if (dwContentLength || strcmpW(request->verb, szGET))
4735 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4736 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4737 request->bytesToWrite = dwContentLength;
4739 if (request->session->appInfo->agent)
4741 WCHAR *agent_header;
4742 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4745 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4746 agent_header = heap_alloc(len * sizeof(WCHAR));
4747 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4749 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4750 heap_free(agent_header);
4752 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4754 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4755 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4757 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4759 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4760 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4761 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4764 /* add the headers the caller supplied */
4765 if( lpszHeaders && dwHeaderLength )
4766 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4771 BOOL reusing_connection;
4776 /* like native, just in case the caller forgot to call InternetReadFile
4777 * for all the data */
4778 drain_content(request);
4780 request->contentLength = ~0u;
4781 request->bytesToWrite = 0;
4784 if (TRACE_ON(wininet))
4786 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4787 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4790 HTTP_FixURL(request);
4791 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4793 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4795 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4796 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4798 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4799 HTTP_InsertCookies(request);
4801 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4803 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4804 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4808 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4811 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4813 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4816 /* send the request as ASCII, tack on the optional data */
4817 if (!lpOptional || redirected)
4818 dwOptionalLength = 0;
4819 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4820 NULL, 0, NULL, NULL );
4821 ascii_req = heap_alloc(len + dwOptionalLength);
4822 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4823 ascii_req, len, NULL, NULL );
4825 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4826 len = (len + dwOptionalLength - 1);
4828 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4830 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4831 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4833 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4834 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4835 heap_free( ascii_req );
4836 if(res != ERROR_SUCCESS) {
4837 TRACE("send failed: %u\n", res);
4838 if(!reusing_connection)
4840 http_release_netconn(request, FALSE);
4845 request->bytesWritten = dwOptionalLength;
4847 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4848 INTERNET_STATUS_REQUEST_SENT,
4849 &len, sizeof(DWORD));
4855 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4856 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4858 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4859 /* FIXME: We should know that connection is closed before sending
4860 * headers. Otherwise wrong callbacks are executed */
4861 if(!responseLen && reusing_connection) {
4862 TRACE("Connection closed by server, reconnecting\n");
4863 http_release_netconn(request, FALSE);
4868 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4869 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4872 http_process_keep_alive(request);
4873 HTTP_ProcessCookies(request);
4874 HTTP_ProcessExpires(request);
4875 HTTP_ProcessLastModified(request);
4877 res = set_content_length(request);
4878 if(res != ERROR_SUCCESS)
4880 if(!request->contentLength)
4881 http_release_netconn(request, TRUE);
4883 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4885 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4886 dwBufferSize=sizeof(szNewLocation);
4887 switch(request->status_code) {
4888 case HTTP_STATUS_REDIRECT:
4889 case HTTP_STATUS_MOVED:
4890 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4891 case HTTP_STATUS_REDIRECT_METHOD:
4892 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4895 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4896 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4898 heap_free(request->verb);
4899 request->verb = heap_strdupW(szGET);
4901 drain_content(request);
4902 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4904 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4905 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4906 res = HTTP_HandleRedirect(request, new_url);
4907 if (res == ERROR_SUCCESS)
4909 heap_free(requestString);
4912 heap_free( new_url );
4917 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4919 WCHAR szAuthValue[2048];
4921 if (request->status_code == HTTP_STATUS_DENIED)
4923 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4925 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4927 if (HTTP_DoAuthorization(request, szAuthValue,
4929 request->session->userName,
4930 request->session->password,
4933 heap_free(requestString);
4940 TRACE("Cleaning wrong authorization data\n");
4941 destroy_authinfo(request->authInfo);
4942 request->authInfo = NULL;
4945 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4948 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4950 if (HTTP_DoAuthorization(request, szAuthValue,
4951 &request->proxyAuthInfo,
4952 request->session->appInfo->proxyUsername,
4953 request->session->appInfo->proxyPassword,
4962 TRACE("Cleaning wrong proxy authorization data\n");
4963 destroy_authinfo(request->proxyAuthInfo);
4964 request->proxyAuthInfo = NULL;
4970 res = ERROR_SUCCESS;
4974 if(res == ERROR_SUCCESS)
4975 HTTP_CacheRequest(request);
4978 heap_free(requestString);
4980 /* TODO: send notification for P3P header */
4982 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4984 if (res == ERROR_SUCCESS) {
4985 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4986 HTTP_ReceiveRequestData(request, TRUE);
4988 send_request_complete(request,
4989 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4991 send_request_complete(request, 0, res);
4999 /***********************************************************************
5001 * Helper functions for the HttpSendRequest(Ex) functions
5004 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
5006 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
5007 http_request_t *request = (http_request_t*) workRequest->hdr;
5009 TRACE("%p\n", request);
5011 HTTP_HttpSendRequestW(request, req->lpszHeader,
5012 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5013 req->dwContentLength, req->bEndRequest);
5015 heap_free(req->lpszHeader);
5019 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5023 DWORD res = ERROR_SUCCESS;
5025 if(!request->netconn) {
5026 WARN("Not connected\n");
5027 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5028 return ERROR_INTERNET_OPERATION_CANCELLED;
5031 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5032 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5034 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5036 res = ERROR_HTTP_HEADER_NOT_FOUND;
5038 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5039 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5041 /* process cookies here. Is this right? */
5042 http_process_keep_alive(request);
5043 HTTP_ProcessCookies(request);
5044 HTTP_ProcessExpires(request);
5045 HTTP_ProcessLastModified(request);
5047 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5048 if(!request->contentLength)
5049 http_release_netconn(request, TRUE);
5052 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5054 switch(request->status_code) {
5055 case HTTP_STATUS_REDIRECT:
5056 case HTTP_STATUS_MOVED:
5057 case HTTP_STATUS_REDIRECT_METHOD:
5058 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5059 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5060 dwBufferSize=sizeof(szNewLocation);
5061 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5064 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5065 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5067 heap_free(request->verb);
5068 request->verb = heap_strdupW(szGET);
5070 drain_content(request);
5071 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5073 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5074 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5075 res = HTTP_HandleRedirect(request, new_url);
5076 if (res == ERROR_SUCCESS)
5077 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5078 heap_free( new_url );
5084 if (res == ERROR_SUCCESS && request->contentLength)
5085 HTTP_ReceiveRequestData(request, TRUE);
5087 send_request_complete(request, res == ERROR_SUCCESS, res);
5092 /***********************************************************************
5093 * HttpEndRequestA (WININET.@)
5095 * Ends an HTTP request that was started by HttpSendRequestEx
5098 * TRUE if successful
5102 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5103 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5105 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5109 SetLastError(ERROR_INVALID_PARAMETER);
5113 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5116 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5118 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5119 http_request_t *request = (http_request_t*)work->hdr;
5121 TRACE("%p\n", request);
5123 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5126 /***********************************************************************
5127 * HttpEndRequestW (WININET.@)
5129 * Ends an HTTP request that was started by HttpSendRequestEx
5132 * TRUE if successful
5136 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5137 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5139 http_request_t *request;
5142 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5146 SetLastError(ERROR_INVALID_PARAMETER);
5150 request = (http_request_t*) get_handle_object( hRequest );
5152 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5154 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5156 WININET_Release( &request->hdr );
5159 request->hdr.dwFlags |= dwFlags;
5161 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5164 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5166 work.asyncproc = AsyncHttpEndRequestProc;
5167 work.hdr = WININET_AddRef( &request->hdr );
5169 work_endrequest = &work.u.HttpEndRequestW;
5170 work_endrequest->dwFlags = dwFlags;
5171 work_endrequest->dwContext = dwContext;
5173 INTERNET_AsyncCall(&work);
5174 res = ERROR_IO_PENDING;
5177 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5179 WININET_Release( &request->hdr );
5180 TRACE("%u <--\n", res);
5181 if(res != ERROR_SUCCESS)
5183 return res == ERROR_SUCCESS;
5186 /***********************************************************************
5187 * HttpSendRequestExA (WININET.@)
5189 * Sends the specified request to the HTTP server and allows chunked
5194 * Failure: FALSE, call GetLastError() for more information.
5196 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5197 LPINTERNET_BUFFERSA lpBuffersIn,
5198 LPINTERNET_BUFFERSA lpBuffersOut,
5199 DWORD dwFlags, DWORD_PTR dwContext)
5201 INTERNET_BUFFERSW BuffersInW;
5204 LPWSTR header = NULL;
5206 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5207 lpBuffersOut, dwFlags, dwContext);
5211 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5212 if (lpBuffersIn->lpcszHeader)
5214 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5215 lpBuffersIn->dwHeadersLength,0,0);
5216 header = heap_alloc(headerlen*sizeof(WCHAR));
5217 if (!(BuffersInW.lpcszHeader = header))
5219 SetLastError(ERROR_OUTOFMEMORY);
5222 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5223 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5227 BuffersInW.lpcszHeader = NULL;
5228 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5229 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5230 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5231 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5232 BuffersInW.Next = NULL;
5235 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5241 /***********************************************************************
5242 * HttpSendRequestExW (WININET.@)
5244 * Sends the specified request to the HTTP server and allows chunked
5249 * Failure: FALSE, call GetLastError() for more information.
5251 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5252 LPINTERNET_BUFFERSW lpBuffersIn,
5253 LPINTERNET_BUFFERSW lpBuffersOut,
5254 DWORD dwFlags, DWORD_PTR dwContext)
5256 http_request_t *request;
5257 http_session_t *session;
5261 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5262 lpBuffersOut, dwFlags, dwContext);
5264 request = (http_request_t*) get_handle_object( hRequest );
5266 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5268 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5272 session = request->session;
5273 assert(session->hdr.htype == WH_HHTTPSESSION);
5274 hIC = session->appInfo;
5275 assert(hIC->hdr.htype == WH_HINIT);
5277 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5279 WORKREQUEST workRequest;
5280 struct WORKREQ_HTTPSENDREQUESTW *req;
5282 workRequest.asyncproc = AsyncHttpSendRequestProc;
5283 workRequest.hdr = WININET_AddRef( &request->hdr );
5284 req = &workRequest.u.HttpSendRequestW;
5289 if (lpBuffersIn->lpcszHeader)
5291 if (lpBuffersIn->dwHeadersLength == ~0u)
5292 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5294 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5296 req->lpszHeader = heap_alloc(size);
5297 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5299 else req->lpszHeader = NULL;
5301 req->dwHeaderLength = size / sizeof(WCHAR);
5302 req->lpOptional = lpBuffersIn->lpvBuffer;
5303 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5304 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5308 req->lpszHeader = NULL;
5309 req->dwHeaderLength = 0;
5310 req->lpOptional = NULL;
5311 req->dwOptionalLength = 0;
5312 req->dwContentLength = 0;
5315 req->bEndRequest = FALSE;
5317 INTERNET_AsyncCall(&workRequest);
5319 * This is from windows.
5321 res = ERROR_IO_PENDING;
5326 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5327 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5328 lpBuffersIn->dwBufferTotal, FALSE);
5330 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5335 WININET_Release( &request->hdr );
5339 return res == ERROR_SUCCESS;
5342 /***********************************************************************
5343 * HttpSendRequestW (WININET.@)
5345 * Sends the specified request to the HTTP server
5352 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5353 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5355 http_request_t *request;
5356 http_session_t *session = NULL;
5357 appinfo_t *hIC = NULL;
5358 DWORD res = ERROR_SUCCESS;
5360 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5361 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5363 request = (http_request_t*) get_handle_object( hHttpRequest );
5364 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5366 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5370 session = request->session;
5371 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5373 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5377 hIC = session->appInfo;
5378 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5380 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5384 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5386 WORKREQUEST workRequest;
5387 struct WORKREQ_HTTPSENDREQUESTW *req;
5389 workRequest.asyncproc = AsyncHttpSendRequestProc;
5390 workRequest.hdr = WININET_AddRef( &request->hdr );
5391 req = &workRequest.u.HttpSendRequestW;
5396 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5397 else size = dwHeaderLength * sizeof(WCHAR);
5399 req->lpszHeader = heap_alloc(size);
5400 memcpy(req->lpszHeader, lpszHeaders, size);
5403 req->lpszHeader = 0;
5404 req->dwHeaderLength = dwHeaderLength;
5405 req->lpOptional = lpOptional;
5406 req->dwOptionalLength = dwOptionalLength;
5407 req->dwContentLength = dwOptionalLength;
5408 req->bEndRequest = TRUE;
5410 INTERNET_AsyncCall(&workRequest);
5412 * This is from windows.
5414 res = ERROR_IO_PENDING;
5418 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5419 dwHeaderLength, lpOptional, dwOptionalLength,
5420 dwOptionalLength, TRUE);
5424 WININET_Release( &request->hdr );
5427 return res == ERROR_SUCCESS;
5430 /***********************************************************************
5431 * HttpSendRequestA (WININET.@)
5433 * Sends the specified request to the HTTP server
5440 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5441 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5444 LPWSTR szHeaders=NULL;
5445 DWORD nLen=dwHeaderLength;
5446 if(lpszHeaders!=NULL)
5448 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5449 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5450 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5452 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5453 heap_free(szHeaders);
5457 /***********************************************************************
5458 * HTTPSESSION_Destroy (internal)
5460 * Deallocate session handle
5463 static void HTTPSESSION_Destroy(object_header_t *hdr)
5465 http_session_t *session = (http_session_t*) hdr;
5467 TRACE("%p\n", session);
5469 WININET_Release(&session->appInfo->hdr);
5471 heap_free(session->hostName);
5472 heap_free(session->serverName);
5473 heap_free(session->password);
5474 heap_free(session->userName);
5477 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5479 http_session_t *ses = (http_session_t *)hdr;
5482 case INTERNET_OPTION_HANDLE_TYPE:
5483 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5485 if (*size < sizeof(ULONG))
5486 return ERROR_INSUFFICIENT_BUFFER;
5488 *size = sizeof(DWORD);
5489 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5490 return ERROR_SUCCESS;
5491 case INTERNET_OPTION_CONNECT_TIMEOUT:
5492 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5494 if (*size < sizeof(DWORD))
5495 return ERROR_INSUFFICIENT_BUFFER;
5497 *size = sizeof(DWORD);
5498 *(DWORD *)buffer = ses->connect_timeout;
5499 return ERROR_SUCCESS;
5501 case INTERNET_OPTION_SEND_TIMEOUT:
5502 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5504 if (*size < sizeof(DWORD))
5505 return ERROR_INSUFFICIENT_BUFFER;
5507 *size = sizeof(DWORD);
5508 *(DWORD *)buffer = ses->send_timeout;
5509 return ERROR_SUCCESS;
5511 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5512 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5514 if (*size < sizeof(DWORD))
5515 return ERROR_INSUFFICIENT_BUFFER;
5517 *size = sizeof(DWORD);
5518 *(DWORD *)buffer = ses->receive_timeout;
5519 return ERROR_SUCCESS;
5522 return INET_QueryOption(hdr, option, buffer, size, unicode);
5525 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5527 http_session_t *ses = (http_session_t*)hdr;
5530 case INTERNET_OPTION_USERNAME:
5532 heap_free(ses->userName);
5533 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5534 return ERROR_SUCCESS;
5536 case INTERNET_OPTION_PASSWORD:
5538 heap_free(ses->password);
5539 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5540 return ERROR_SUCCESS;
5542 case INTERNET_OPTION_CONNECT_TIMEOUT:
5544 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5545 ses->connect_timeout = *(DWORD *)buffer;
5546 return ERROR_SUCCESS;
5548 case INTERNET_OPTION_SEND_TIMEOUT:
5550 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5551 ses->send_timeout = *(DWORD *)buffer;
5552 return ERROR_SUCCESS;
5554 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5556 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5557 ses->receive_timeout = *(DWORD *)buffer;
5558 return ERROR_SUCCESS;
5563 return INET_SetOption(hdr, option, buffer, size);
5566 static const object_vtbl_t HTTPSESSIONVtbl = {
5567 HTTPSESSION_Destroy,
5569 HTTPSESSION_QueryOption,
5570 HTTPSESSION_SetOption,
5579 /***********************************************************************
5580 * HTTP_Connect (internal)
5582 * Create http session handle
5585 * HINTERNET a session handle on success
5589 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5590 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5591 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5592 DWORD dwInternalFlags, HINTERNET *ret)
5594 http_session_t *session = NULL;
5598 if (!lpszServerName || !lpszServerName[0])
5599 return ERROR_INVALID_PARAMETER;
5601 assert( hIC->hdr.htype == WH_HINIT );
5603 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5605 return ERROR_OUTOFMEMORY;
5608 * According to my tests. The name is not resolved until a request is sent
5611 session->hdr.htype = WH_HHTTPSESSION;
5612 session->hdr.dwFlags = dwFlags;
5613 session->hdr.dwContext = dwContext;
5614 session->hdr.dwInternalFlags |= dwInternalFlags;
5616 WININET_AddRef( &hIC->hdr );
5617 session->appInfo = hIC;
5618 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5620 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5621 if(hIC->proxyBypass)
5622 FIXME("Proxy bypass is ignored.\n");
5624 session->serverName = heap_strdupW(lpszServerName);
5625 session->hostName = heap_strdupW(lpszServerName);
5626 if (lpszUserName && lpszUserName[0])
5627 session->userName = heap_strdupW(lpszUserName);
5628 if (lpszPassword && lpszPassword[0])
5629 session->password = heap_strdupW(lpszPassword);
5630 session->serverPort = serverPort;
5631 session->hostPort = serverPort;
5632 session->connect_timeout = hIC->connect_timeout;
5633 session->send_timeout = INFINITE;
5634 session->receive_timeout = INFINITE;
5636 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5637 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5639 INTERNET_SendCallback(&hIC->hdr, dwContext,
5640 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5645 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5649 TRACE("%p --> %p\n", hIC, session);
5651 *ret = session->hdr.hInternet;
5652 return ERROR_SUCCESS;
5655 /***********************************************************************
5656 * HTTP_clear_response_headers (internal)
5658 * clear out any old response headers
5660 static void HTTP_clear_response_headers( http_request_t *request )
5664 for( i=0; i<request->nCustHeaders; i++)
5666 if( !request->custHeaders[i].lpszField )
5668 if( !request->custHeaders[i].lpszValue )
5670 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5672 HTTP_DeleteCustomHeader( request, i );
5677 /***********************************************************************
5678 * HTTP_GetResponseHeaders (internal)
5680 * Read server response
5687 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5690 WCHAR buffer[MAX_REPLY_LEN];
5691 DWORD buflen = MAX_REPLY_LEN;
5692 BOOL bSuccess = FALSE;
5694 char bufferA[MAX_REPLY_LEN];
5695 LPWSTR status_code = NULL, status_text = NULL;
5696 DWORD cchMaxRawHeaders = 1024;
5697 LPWSTR lpszRawHeaders = NULL;
5699 DWORD cchRawHeaders = 0;
5700 BOOL codeHundred = FALSE;
5704 if(!request->netconn)
5707 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5709 static const WCHAR szHundred[] = {'1','0','0',0};
5711 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5713 buflen = MAX_REPLY_LEN;
5714 if (!read_line(request, bufferA, &buflen))
5717 /* clear old response headers (eg. from a redirect response) */
5719 HTTP_clear_response_headers( request );
5724 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5725 /* check is this a status code line? */
5726 if (!strncmpW(buffer, g_szHttp1_0, 4))
5728 /* split the version from the status code */
5729 status_code = strchrW( buffer, ' ' );
5734 /* split the status code from the status text */
5735 status_text = strchrW( status_code, ' ' );
5740 request->status_code = atoiW(status_code);
5742 TRACE("version [%s] status code [%s] status text [%s]\n",
5743 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5745 codeHundred = (!strcmpW(status_code, szHundred));
5747 else if (!codeHundred)
5749 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5751 heap_free(request->version);
5752 heap_free(request->statusText);
5754 request->status_code = HTTP_STATUS_OK;
5755 request->version = heap_strdupW(g_szHttp1_0);
5756 request->statusText = heap_strdupW(szOK);
5758 heap_free(request->rawHeaders);
5759 request->rawHeaders = heap_strdupW(szDefaultHeader);
5764 } while (codeHundred);
5766 /* Add status code */
5767 HTTP_ProcessHeader(request, szStatus, status_code,
5768 HTTP_ADDHDR_FLAG_REPLACE);
5770 heap_free(request->version);
5771 heap_free(request->statusText);
5773 request->version = heap_strdupW(buffer);
5774 request->statusText = heap_strdupW(status_text);
5776 /* Restore the spaces */
5777 *(status_code-1) = ' ';
5778 *(status_text-1) = ' ';
5780 /* regenerate raw headers */
5781 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5782 if (!lpszRawHeaders) goto lend;
5784 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5785 cchMaxRawHeaders *= 2;
5786 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5787 if (temp == NULL) goto lend;
5788 lpszRawHeaders = temp;
5789 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5790 cchRawHeaders += (buflen-1);
5791 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5792 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5793 lpszRawHeaders[cchRawHeaders] = '\0';
5795 /* Parse each response line */
5798 buflen = MAX_REPLY_LEN;
5799 if (read_line(request, bufferA, &buflen))
5801 LPWSTR * pFieldAndValue;
5803 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5805 if (!bufferA[0]) break;
5806 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5808 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5811 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5812 cchMaxRawHeaders *= 2;
5813 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5814 if (temp == NULL) goto lend;
5815 lpszRawHeaders = temp;
5816 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5817 cchRawHeaders += (buflen-1);
5818 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5819 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5820 lpszRawHeaders[cchRawHeaders] = '\0';
5822 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5823 HTTP_ADDREQ_FLAG_ADD );
5825 HTTP_FreeTokens(pFieldAndValue);
5836 /* make sure the response header is terminated with an empty line. Some apps really
5837 truly care about that empty line being there for some reason. Just add it to the
5839 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5841 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5842 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5843 if (temp == NULL) goto lend;
5844 lpszRawHeaders = temp;
5847 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5849 heap_free(request->rawHeaders);
5850 request->rawHeaders = lpszRawHeaders;
5851 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5861 heap_free(lpszRawHeaders);
5866 /***********************************************************************
5867 * HTTP_InterpretHttpHeader (internal)
5869 * Parse server response
5873 * Pointer to array of field, value, NULL on success.
5876 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5878 LPWSTR * pTokenPair;
5882 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5884 pszColon = strchrW(buffer, ':');
5885 /* must have two tokens */
5888 HTTP_FreeTokens(pTokenPair);
5890 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5894 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5897 HTTP_FreeTokens(pTokenPair);
5900 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5901 pTokenPair[0][pszColon - buffer] = '\0';
5905 len = strlenW(pszColon);
5906 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5909 HTTP_FreeTokens(pTokenPair);
5912 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5914 strip_spaces(pTokenPair[0]);
5915 strip_spaces(pTokenPair[1]);
5917 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5921 /***********************************************************************
5922 * HTTP_ProcessHeader (internal)
5924 * Stuff header into header tables according to <dwModifier>
5928 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5930 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5932 LPHTTPHEADERW lphttpHdr = NULL;
5934 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5935 DWORD res = ERROR_HTTP_INVALID_HEADER;
5937 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5939 /* REPLACE wins out over ADD */
5940 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5941 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5943 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5946 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5950 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5951 return ERROR_HTTP_INVALID_HEADER;
5952 lphttpHdr = &request->custHeaders[index];
5958 hdr.lpszField = (LPWSTR)field;
5959 hdr.lpszValue = (LPWSTR)value;
5960 hdr.wFlags = hdr.wCount = 0;
5962 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5963 hdr.wFlags |= HDR_ISREQUEST;
5965 return HTTP_InsertCustomHeader(request, &hdr);
5967 /* no value to delete */
5968 else return ERROR_SUCCESS;
5970 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5971 lphttpHdr->wFlags |= HDR_ISREQUEST;
5973 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5975 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5977 HTTP_DeleteCustomHeader( request, index );
5983 hdr.lpszField = (LPWSTR)field;
5984 hdr.lpszValue = (LPWSTR)value;
5985 hdr.wFlags = hdr.wCount = 0;
5987 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5988 hdr.wFlags |= HDR_ISREQUEST;
5990 return HTTP_InsertCustomHeader(request, &hdr);
5993 return ERROR_SUCCESS;
5995 else if (dwModifier & COALESCEFLAGS)
6000 INT origlen = strlenW(lphttpHdr->lpszValue);
6001 INT valuelen = strlenW(value);
6003 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6006 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6008 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6011 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6014 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6016 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6019 lphttpHdr->lpszValue = lpsztmp;
6020 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6023 lphttpHdr->lpszValue[origlen] = ch;
6025 lphttpHdr->lpszValue[origlen] = ' ';
6029 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6030 lphttpHdr->lpszValue[len] = '\0';
6031 res = ERROR_SUCCESS;
6035 WARN("heap_realloc (%d bytes) failed\n",len+1);
6036 res = ERROR_OUTOFMEMORY;
6039 TRACE("<-- %d\n", res);
6043 /***********************************************************************
6044 * HTTP_GetCustomHeaderIndex (internal)
6046 * Return index of custom header from header array
6049 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6050 int requested_index, BOOL request_only)
6054 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6056 for (index = 0; index < request->nCustHeaders; index++)
6058 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6061 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6064 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6067 if (requested_index == 0)
6072 if (index >= request->nCustHeaders)
6075 TRACE("Return: %d\n", index);
6080 /***********************************************************************
6081 * HTTP_InsertCustomHeader (internal)
6083 * Insert header into array
6086 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6089 LPHTTPHEADERW lph = NULL;
6091 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6092 count = request->nCustHeaders + 1;
6094 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6096 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6099 return ERROR_OUTOFMEMORY;
6101 request->custHeaders = lph;
6102 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6103 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6104 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6105 request->custHeaders[count-1].wCount= lpHdr->wCount;
6106 request->nCustHeaders++;
6108 return ERROR_SUCCESS;
6112 /***********************************************************************
6113 * HTTP_DeleteCustomHeader (internal)
6115 * Delete header from array
6116 * If this function is called, the indexs may change.
6118 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6120 if( request->nCustHeaders <= 0 )
6122 if( index >= request->nCustHeaders )
6124 request->nCustHeaders--;
6126 heap_free(request->custHeaders[index].lpszField);
6127 heap_free(request->custHeaders[index].lpszValue);
6129 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6130 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6131 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6137 /***********************************************************************
6138 * HTTP_VerifyValidHeader (internal)
6140 * Verify the given header is not invalid for the given http request
6143 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6145 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6146 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6147 return ERROR_HTTP_INVALID_HEADER;
6149 return ERROR_SUCCESS;
6152 /***********************************************************************
6153 * IsHostInProxyBypassList (@)
6158 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6160 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6164 /***********************************************************************
6165 * InternetShowSecurityInfoByURLA (@)
6167 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6169 FIXME("stub: %s %p\n", url, window);
6173 /***********************************************************************
6174 * InternetShowSecurityInfoByURLW (@)
6176 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6178 FIXME("stub: %s %p\n", debugstr_w(url), window);
6182 /***********************************************************************
6183 * ShowX509EncodedCertificate (@)
6185 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6187 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6193 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6195 memset(&view, 0, sizeof(view));
6196 view.hwndParent = parent;
6197 view.pCertContext = certContext;
6198 if (CryptUIDlgViewCertificateW(&view, NULL))
6199 ret = ERROR_SUCCESS;
6201 ret = GetLastError();
6202 CertFreeCertificateContext(certContext);
6205 ret = GetLastError();