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);
222 static BOOL drain_content(http_request_t*,BOOL);
224 static CRITICAL_SECTION connection_pool_cs;
225 static CRITICAL_SECTION_DEBUG connection_pool_debug =
227 0, 0, &connection_pool_cs,
228 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
229 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
231 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
233 static struct list connection_pool = LIST_INIT(connection_pool);
234 static BOOL collector_running;
236 void server_addref(server_t *server)
238 InterlockedIncrement(&server->ref);
241 void server_release(server_t *server)
243 if(InterlockedDecrement(&server->ref))
246 list_remove(&server->entry);
248 if(server->cert_chain)
249 CertFreeCertificateChain(server->cert_chain);
250 heap_free(server->name);
254 server_t *get_server(const WCHAR *name, INTERNET_PORT port, BOOL is_https, BOOL do_create)
256 server_t *iter, *server = NULL;
258 EnterCriticalSection(&connection_pool_cs);
260 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
261 if(iter->port == port && !strcmpW(iter->name, name) && iter->is_https == is_https) {
263 server_addref(server);
268 if(!server && do_create) {
269 server = heap_alloc_zero(sizeof(*server));
271 server->ref = 2; /* list reference and return */
273 server->is_https = is_https;
274 list_init(&server->conn_pool);
275 server->name = heap_strdupW(name);
277 list_add_head(&connection_pool, &server->entry);
285 LeaveCriticalSection(&connection_pool_cs);
290 BOOL collect_connections(collect_type_t collect_type)
292 netconn_t *netconn, *netconn_safe;
293 server_t *server, *server_safe;
294 BOOL remaining = FALSE;
297 now = GetTickCount64();
299 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
300 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
301 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
302 TRACE("freeing %p\n", netconn);
303 list_remove(&netconn->pool_entry);
304 free_netconn(netconn);
310 if(collect_type == COLLECT_CLEANUP) {
311 list_remove(&server->entry);
312 list_init(&server->entry);
313 server_release(server);
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(COLLECT_TIMEOUT);
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_INVARIANT, 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->server->name : 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 server_t *new_server;
1702 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1703 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1704 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1706 memset( &UrlComponents, 0, sizeof UrlComponents );
1707 UrlComponents.dwStructSize = sizeof UrlComponents;
1708 UrlComponents.lpszHostName = buf;
1709 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1711 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1713 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1714 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1715 sprintfW(proxy, szFormat, protoProxy);
1717 strcpyW(proxy, protoProxy);
1718 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1720 if( UrlComponents.dwHostNameLength == 0 )
1723 if( !request->path )
1724 request->path = szNul;
1726 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1727 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1729 new_server = get_server(UrlComponents.lpszHostName, UrlComponents.nPort, UrlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
1733 request->proxy = new_server;
1735 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1739 static DWORD HTTP_ResolveName(http_request_t *request)
1741 server_t *server = request->proxy ? request->proxy : request->server;
1745 if(server->addr_len)
1746 return ERROR_SUCCESS;
1748 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1749 INTERNET_STATUS_RESOLVING_NAME,
1751 (strlenW(server->name)+1) * sizeof(WCHAR));
1753 addr_len = sizeof(server->addr);
1754 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1755 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1757 switch(server->addr.ss_family) {
1759 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1762 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1765 WARN("unsupported family %d\n", server->addr.ss_family);
1766 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1769 server->addr_len = addr_len;
1770 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1771 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1772 INTERNET_STATUS_NAME_RESOLVED,
1773 server->addr_str, strlen(server->addr_str)+1);
1775 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1776 return ERROR_SUCCESS;
1779 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1781 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1782 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1783 static const WCHAR slash[] = { '/',0 };
1784 LPHTTPHEADERW host_header;
1787 host_header = HTTP_GetHeader(req, hostW);
1791 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1795 strcpyW(buf, scheme);
1796 strcatW(buf, host_header->lpszValue);
1797 if (req->path[0] != '/')
1798 strcatW(buf, slash);
1799 strcatW(buf, req->path);
1804 /***********************************************************************
1805 * HTTPREQ_Destroy (internal)
1807 * Deallocate request handle
1810 static void HTTPREQ_Destroy(object_header_t *hdr)
1812 http_request_t *request = (http_request_t*) hdr;
1817 if(request->hCacheFile) {
1818 CloseHandle(request->hCacheFile);
1819 DeleteFileW(request->cacheFile);
1821 heap_free(request->cacheFile);
1823 request->read_section.DebugInfo->Spare[0] = 0;
1824 DeleteCriticalSection( &request->read_section );
1825 WININET_Release(&request->session->hdr);
1827 destroy_authinfo(request->authInfo);
1828 destroy_authinfo(request->proxyAuthInfo);
1831 server_release(request->server);
1833 server_release(request->proxy);
1835 heap_free(request->path);
1836 heap_free(request->verb);
1837 heap_free(request->rawHeaders);
1838 heap_free(request->version);
1839 heap_free(request->statusText);
1841 for (i = 0; i < request->nCustHeaders; i++)
1843 heap_free(request->custHeaders[i].lpszField);
1844 heap_free(request->custHeaders[i].lpszValue);
1846 destroy_data_stream(request->data_stream);
1847 heap_free(request->custHeaders);
1850 static void http_release_netconn(http_request_t *req, BOOL reuse)
1852 TRACE("%p %p\n",req, req->netconn);
1857 if(reuse && req->netconn->keep_alive) {
1860 EnterCriticalSection(&connection_pool_cs);
1862 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1863 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1864 req->netconn = NULL;
1866 run_collector = !collector_running;
1867 collector_running = TRUE;
1869 LeaveCriticalSection(&connection_pool_cs);
1872 HANDLE thread = NULL;
1875 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1877 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1879 EnterCriticalSection(&connection_pool_cs);
1880 collector_running = FALSE;
1881 LeaveCriticalSection(&connection_pool_cs);
1884 FreeLibrary(module);
1887 CloseHandle(thread);
1892 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1893 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1895 free_netconn(req->netconn);
1896 req->netconn = NULL;
1898 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1899 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1902 static BOOL HTTP_KeepAlive(http_request_t *request)
1904 WCHAR szVersion[10];
1905 WCHAR szConnectionResponse[20];
1906 DWORD dwBufferSize = sizeof(szVersion);
1907 BOOL keepalive = FALSE;
1909 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1910 * the connection is keep-alive by default */
1911 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1912 && !strcmpiW(szVersion, g_szHttp1_1))
1917 dwBufferSize = sizeof(szConnectionResponse);
1918 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1919 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1921 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1927 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1929 http_request_t *req = (http_request_t*)hdr;
1931 http_release_netconn(req, drain_content(req, FALSE));
1934 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1936 http_request_t *req = (http_request_t*)hdr;
1939 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1941 http_session_t *session = req->session;
1942 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1944 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1946 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1947 return ERROR_INSUFFICIENT_BUFFER;
1948 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1949 /* FIXME: can't get a SOCKET from our connection since we don't use
1953 /* FIXME: get source port from req->netConnection */
1954 info->SourcePort = 0;
1955 info->DestPort = session->hostPort;
1957 if (HTTP_KeepAlive(req))
1958 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1959 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1960 info->Flags |= IDSI_FLAG_PROXY;
1961 if (req->netconn->useSSL)
1962 info->Flags |= IDSI_FLAG_SECURE;
1964 return ERROR_SUCCESS;
1968 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1970 case INTERNET_OPTION_SECURITY_FLAGS:
1974 if (*size < sizeof(ULONG))
1975 return ERROR_INSUFFICIENT_BUFFER;
1977 *size = sizeof(DWORD);
1978 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
1979 *(DWORD *)buffer = flags;
1981 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
1982 return ERROR_SUCCESS;
1985 case INTERNET_OPTION_HANDLE_TYPE:
1986 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1988 if (*size < sizeof(ULONG))
1989 return ERROR_INSUFFICIENT_BUFFER;
1991 *size = sizeof(DWORD);
1992 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1993 return ERROR_SUCCESS;
1995 case INTERNET_OPTION_URL: {
1996 WCHAR url[INTERNET_MAX_URL_LENGTH];
2001 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2003 TRACE("INTERNET_OPTION_URL\n");
2005 host = HTTP_GetHeader(req, hostW);
2006 strcpyW(url, httpW);
2007 strcatW(url, host->lpszValue);
2008 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2010 strcatW(url, req->path);
2012 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2015 len = (strlenW(url)+1) * sizeof(WCHAR);
2017 return ERROR_INSUFFICIENT_BUFFER;
2020 strcpyW(buffer, url);
2021 return ERROR_SUCCESS;
2023 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2025 return ERROR_INSUFFICIENT_BUFFER;
2028 return ERROR_SUCCESS;
2032 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2033 INTERNET_CACHE_ENTRY_INFOW *info;
2034 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2035 WCHAR url[INTERNET_MAX_URL_LENGTH];
2036 DWORD nbytes, error;
2039 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2041 if (*size < sizeof(*ts))
2043 *size = sizeof(*ts);
2044 return ERROR_INSUFFICIENT_BUFFER;
2047 HTTP_GetRequestURL(req, url);
2048 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2049 error = GetLastError();
2050 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2052 if (!(info = heap_alloc(nbytes)))
2053 return ERROR_OUTOFMEMORY;
2055 GetUrlCacheEntryInfoW(url, info, &nbytes);
2057 ts->ftExpires = info->ExpireTime;
2058 ts->ftLastModified = info->LastModifiedTime;
2061 *size = sizeof(*ts);
2062 return ERROR_SUCCESS;
2067 case INTERNET_OPTION_DATAFILE_NAME: {
2070 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2072 if(!req->cacheFile) {
2074 return ERROR_INTERNET_ITEM_NOT_FOUND;
2078 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2079 if(*size < req_size)
2080 return ERROR_INSUFFICIENT_BUFFER;
2083 memcpy(buffer, req->cacheFile, *size);
2084 return ERROR_SUCCESS;
2086 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2087 if (req_size > *size)
2088 return ERROR_INSUFFICIENT_BUFFER;
2090 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2091 -1, buffer, *size, NULL, NULL);
2092 return ERROR_SUCCESS;
2096 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2097 PCCERT_CONTEXT context;
2099 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2100 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2101 return ERROR_INSUFFICIENT_BUFFER;
2104 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2106 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2109 memset(info, 0, sizeof(*info));
2110 info->ftExpiry = context->pCertInfo->NotAfter;
2111 info->ftStart = context->pCertInfo->NotBefore;
2112 len = CertNameToStrA(context->dwCertEncodingType,
2113 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2114 info->lpszSubjectInfo = LocalAlloc(0, len);
2115 if(info->lpszSubjectInfo)
2116 CertNameToStrA(context->dwCertEncodingType,
2117 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2118 info->lpszSubjectInfo, len);
2119 len = CertNameToStrA(context->dwCertEncodingType,
2120 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2121 info->lpszIssuerInfo = LocalAlloc(0, len);
2122 if(info->lpszIssuerInfo)
2123 CertNameToStrA(context->dwCertEncodingType,
2124 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2125 info->lpszIssuerInfo, len);
2126 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2127 CertFreeCertificateContext(context);
2128 return ERROR_SUCCESS;
2130 return ERROR_NOT_SUPPORTED;
2132 case INTERNET_OPTION_CONNECT_TIMEOUT:
2133 if (*size < sizeof(DWORD))
2134 return ERROR_INSUFFICIENT_BUFFER;
2136 *size = sizeof(DWORD);
2137 *(DWORD *)buffer = req->connect_timeout;
2138 return ERROR_SUCCESS;
2139 case INTERNET_OPTION_REQUEST_FLAGS: {
2142 if (*size < sizeof(DWORD))
2143 return ERROR_INSUFFICIENT_BUFFER;
2145 /* FIXME: Add support for:
2146 * INTERNET_REQFLAG_FROM_CACHE
2147 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2150 if(req->session->appInfo->proxy)
2151 flags |= INTERNET_REQFLAG_VIA_PROXY;
2152 if(!req->rawHeaders)
2153 flags |= INTERNET_REQFLAG_NO_HEADERS;
2155 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2157 *size = sizeof(DWORD);
2158 *(DWORD*)buffer = flags;
2159 return ERROR_SUCCESS;
2163 return INET_QueryOption(hdr, option, buffer, size, unicode);
2166 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2168 http_request_t *req = (http_request_t*)hdr;
2171 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2172 TRACE("Undocumented option 99\n");
2174 if (!buffer || size != sizeof(DWORD))
2175 return ERROR_INVALID_PARAMETER;
2176 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2177 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2180 case INTERNET_OPTION_SECURITY_FLAGS:
2184 if (!buffer || size != sizeof(DWORD))
2185 return ERROR_INVALID_PARAMETER;
2186 flags = *(DWORD *)buffer;
2187 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2188 flags &= SECURITY_SET_MASK;
2189 req->security_flags |= flags;
2191 req->netconn->security_flags |= flags;
2192 return ERROR_SUCCESS;
2194 case INTERNET_OPTION_CONNECT_TIMEOUT:
2195 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2196 req->connect_timeout = *(DWORD *)buffer;
2197 return ERROR_SUCCESS;
2199 case INTERNET_OPTION_SEND_TIMEOUT:
2200 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2201 req->send_timeout = *(DWORD *)buffer;
2202 return ERROR_SUCCESS;
2204 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2205 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2206 req->receive_timeout = *(DWORD *)buffer;
2207 return ERROR_SUCCESS;
2209 case INTERNET_OPTION_USERNAME:
2210 heap_free(req->session->userName);
2211 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2212 return ERROR_SUCCESS;
2214 case INTERNET_OPTION_PASSWORD:
2215 heap_free(req->session->password);
2216 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2217 return ERROR_SUCCESS;
2218 case INTERNET_OPTION_HTTP_DECODING:
2219 if(size != sizeof(BOOL))
2220 return ERROR_INVALID_PARAMETER;
2221 req->decoding = *(BOOL*)buffer;
2222 return ERROR_SUCCESS;
2225 return INET_SetOption(hdr, option, buffer, size);
2228 static void commit_cache_entry(http_request_t *req)
2230 WCHAR url[INTERNET_MAX_URL_LENGTH];
2234 CloseHandle(req->hCacheFile);
2235 req->hCacheFile = NULL;
2237 if(HTTP_GetRequestURL(req, url)) {
2240 headersLen = req->rawHeaders ? strlenW(req->rawHeaders) : 0;
2241 CommitUrlCacheEntryW(url, req->cacheFile, req->expires,
2242 req->last_modified, NORMAL_CACHE_ENTRY,
2243 req->rawHeaders, headersLen, NULL, 0);
2247 static void create_cache_entry(http_request_t *req)
2249 WCHAR url[INTERNET_MAX_URL_LENGTH];
2250 WCHAR file_name[MAX_PATH+1];
2253 /* FIXME: We should free previous cache file earlier */
2254 heap_free(req->cacheFile);
2255 CloseHandle(req->hCacheFile);
2256 req->hCacheFile = NULL;
2258 b = HTTP_GetRequestURL(req, url);
2260 WARN("Could not get URL\n");
2264 b = CreateUrlCacheEntryW(url, req->contentLength, NULL, file_name, 0);
2266 WARN("Could not create cache entry: %08x\n", GetLastError());
2270 req->cacheFile = heap_strdupW(file_name);
2271 req->hCacheFile = CreateFileW(req->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2272 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2273 if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2274 WARN("Could not create file: %u\n", GetLastError());
2275 req->hCacheFile = NULL;
2279 if(req->read_size) {
2282 b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2284 FIXME("WriteFile failed: %u\n", GetLastError());
2286 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2287 commit_cache_entry(req);
2291 /* read some more data into the read buffer (the read section must be held) */
2292 static DWORD read_more_data( http_request_t *req, int maxlen )
2299 /* move existing data to the start of the buffer */
2301 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2305 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2307 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2308 maxlen - req->read_size, 0, &len );
2309 if(res == ERROR_SUCCESS)
2310 req->read_size += len;
2315 /* remove some amount of data from the read buffer (the read section must be held) */
2316 static void remove_data( http_request_t *req, int count )
2318 if (!(req->read_size -= count)) req->read_pos = 0;
2319 else req->read_pos += count;
2322 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2324 int count, bytes_read, pos = 0;
2327 EnterCriticalSection( &req->read_section );
2330 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2334 count = eol - (req->read_buf + req->read_pos);
2335 bytes_read = count + 1;
2337 else count = bytes_read = req->read_size;
2339 count = min( count, *len - pos );
2340 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2342 remove_data( req, bytes_read );
2345 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2348 TRACE( "returning empty string %u\n", res);
2349 LeaveCriticalSection( &req->read_section );
2350 INTERNET_SetLastError(res);
2354 LeaveCriticalSection( &req->read_section );
2358 if (pos && buffer[pos - 1] == '\r') pos--;
2361 buffer[*len - 1] = 0;
2362 TRACE( "returning %s\n", debugstr_a(buffer));
2366 /* check if we have reached the end of the data to read (the read section must be held) */
2367 static BOOL end_of_read_data( http_request_t *req )
2369 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2372 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
2376 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
2377 assert(*read <= size);
2379 if(req->hCacheFile) {
2384 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2386 FIXME("WriteFile failed: %u\n", GetLastError());
2389 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2390 commit_cache_entry(req);
2396 /* fetch some more data into the read buffer (the read section must be held) */
2397 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2401 if(req->read_size == sizeof(req->read_buf))
2402 return ERROR_SUCCESS;
2406 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2410 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2412 req->read_size += read;
2414 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2420 /* return the size of data available to be read immediately (the read section must be held) */
2421 static DWORD get_avail_data( http_request_t *req )
2423 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2426 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2428 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2432 NETCON_query_data_available(req->netconn, &avail);
2433 return netconn_stream->content_length == ~0u
2435 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2438 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2440 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2441 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2444 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2445 DWORD *read, read_mode_t read_mode)
2447 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2450 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2452 if(read_mode == READMODE_NOBLOCK) {
2453 DWORD avail = netconn_get_avail_data(stream, req);
2458 if(size && req->netconn) {
2459 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2462 netconn_stream->content_length = netconn_stream->content_read;
2465 netconn_stream->content_read += *read = len;
2466 TRACE("read %u bytes\n", len);
2467 return ERROR_SUCCESS;
2470 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2472 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2477 if(netconn_end_of_data(stream, req))
2481 avail = netconn_get_avail_data(stream, req);
2485 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2488 netconn_stream->content_read += len;
2489 }while(netconn_stream->content_read < netconn_stream->content_length);
2494 static void netconn_destroy(data_stream_t *stream)
2498 static const data_stream_vtbl_t netconn_stream_vtbl = {
2499 netconn_get_avail_data,
2500 netconn_end_of_data,
2502 netconn_drain_content,
2506 /* read some more data into the read buffer (the read section must be held) */
2507 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2512 if (stream->buf_pos)
2514 /* move existing data to the start of the buffer */
2515 if(stream->buf_size)
2516 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2517 stream->buf_pos = 0;
2520 if (maxlen == -1) maxlen = sizeof(stream->buf);
2522 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2523 maxlen - stream->buf_size, 0, &len );
2524 if(res == ERROR_SUCCESS)
2525 stream->buf_size += len;
2530 /* remove some amount of data from the read buffer (the read section must be held) */
2531 static void remove_chunked_data(chunked_stream_t *stream, int count)
2533 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2534 else stream->buf_pos += count;
2537 /* discard data contents until we reach end of line (the read section must be held) */
2538 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2544 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2547 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2550 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2551 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2552 } while (stream->buf_size);
2553 return ERROR_SUCCESS;
2556 /* read the size of the next chunk (the read section must be held) */
2557 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2560 DWORD chunk_size = 0, res;
2562 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2567 while (stream->buf_size)
2569 char ch = stream->buf[stream->buf_pos];
2570 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2571 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2572 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2573 else if (ch == ';' || ch == '\r' || ch == '\n')
2575 TRACE( "reading %u byte chunk\n", chunk_size );
2576 stream->chunk_size = chunk_size;
2577 req->contentLength += chunk_size;
2578 return discard_chunked_eol(stream, req);
2580 remove_chunked_data(stream, 1);
2582 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2583 if (!stream->buf_size)
2585 stream->chunk_size = 0;
2586 return ERROR_SUCCESS;
2591 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2593 /* Allow reading only from read buffer */
2597 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2599 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2600 return !chunked_stream->chunk_size;
2603 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2604 DWORD *read, read_mode_t read_mode)
2606 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2607 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2609 if(chunked_stream->chunk_size == ~0u) {
2610 res = start_next_chunk(chunked_stream, req);
2611 if(res != ERROR_SUCCESS)
2615 while(size && chunked_stream->chunk_size) {
2616 if(chunked_stream->buf_size) {
2617 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2619 /* this could block */
2620 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2623 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2624 remove_chunked_data(chunked_stream, read_bytes);
2626 read_bytes = min(size, chunked_stream->chunk_size);
2628 if(read_mode == READMODE_NOBLOCK) {
2631 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2633 if(read_bytes > avail)
2636 /* this could block */
2637 if(read_bytes == chunked_stream->chunk_size)
2641 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2642 if(res != ERROR_SUCCESS)
2646 chunked_stream->chunk_size -= read_bytes;
2648 ret_read += read_bytes;
2649 if(!chunked_stream->chunk_size) {
2650 assert(read_mode != READMODE_NOBLOCK);
2651 res = start_next_chunk(chunked_stream, req);
2652 if(res != ERROR_SUCCESS)
2656 if(read_mode == READMODE_ASYNC)
2657 read_mode = READMODE_NOBLOCK;
2660 TRACE("read %u bytes\n", ret_read);
2665 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2667 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2669 /* FIXME: we can do better */
2670 return !chunked_stream->chunk_size;
2673 static void chunked_destroy(data_stream_t *stream)
2675 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2676 heap_free(chunked_stream);
2679 static const data_stream_vtbl_t chunked_stream_vtbl = {
2680 chunked_get_avail_data,
2681 chunked_end_of_data,
2683 chunked_drain_content,
2687 /* set the request content length based on the headers */
2688 static DWORD set_content_length(http_request_t *request)
2690 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2694 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2695 request->contentLength = request->netconn_stream.content_length = 0;
2696 return ERROR_SUCCESS;
2699 size = sizeof(request->contentLength);
2700 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2701 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2702 request->contentLength = ~0u;
2703 request->netconn_stream.content_length = request->contentLength;
2704 request->netconn_stream.content_read = request->read_size;
2706 size = sizeof(encoding);
2707 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2708 !strcmpiW(encoding, szChunked))
2710 chunked_stream_t *chunked_stream;
2712 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2714 return ERROR_OUTOFMEMORY;
2716 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2717 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2718 chunked_stream->chunk_size = ~0u;
2720 if(request->read_size) {
2721 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2722 chunked_stream->buf_size = request->read_size;
2723 request->read_size = request->read_pos = 0;
2726 request->data_stream = &chunked_stream->data_stream;
2727 request->contentLength = ~0u;
2728 request->read_chunked = TRUE;
2731 if(request->decoding) {
2734 static const WCHAR gzipW[] = {'g','z','i','p',0};
2736 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2737 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2738 return init_gzip_stream(request);
2741 return ERROR_SUCCESS;
2744 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2746 INTERNET_ASYNC_RESULT iar;
2748 iar.dwResult = result;
2749 iar.dwError = error;
2751 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2752 sizeof(INTERNET_ASYNC_RESULT));
2755 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2757 DWORD res, read = 0, avail = 0;
2762 EnterCriticalSection( &req->read_section );
2764 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2765 res = refill_read_buffer(req, mode, &read);
2766 if(res == ERROR_SUCCESS && !first_notif)
2767 avail = get_avail_data(req);
2769 LeaveCriticalSection( &req->read_section );
2771 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2772 WARN("res %u read %u, closing connection\n", res, read);
2773 http_release_netconn(req, FALSE);
2776 if(res == ERROR_SUCCESS)
2777 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2779 send_request_complete(req, 0, res);
2782 /* read data from the http connection (the read section must be held) */
2783 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2785 DWORD current_read = 0, ret_read = 0;
2786 read_mode_t read_mode;
2787 DWORD res = ERROR_SUCCESS;
2789 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2791 EnterCriticalSection( &req->read_section );
2793 if(req->read_size) {
2794 ret_read = min(size, req->read_size);
2795 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2796 req->read_size -= ret_read;
2797 req->read_pos += ret_read;
2798 if(read_mode == READMODE_ASYNC)
2799 read_mode = READMODE_NOBLOCK;
2802 if(ret_read < size) {
2803 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2804 ret_read += current_read;
2807 LeaveCriticalSection( &req->read_section );
2810 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2812 if(size && !ret_read)
2813 http_release_netconn(req, res == ERROR_SUCCESS);
2818 static BOOL drain_content(http_request_t *req, BOOL blocking)
2822 if(!req->netconn || req->contentLength == -1)
2825 if(!strcmpW(req->verb, szHEAD))
2829 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2831 EnterCriticalSection( &req->read_section );
2834 DWORD bytes_read, res;
2837 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2838 if(res != ERROR_SUCCESS) {
2848 LeaveCriticalSection( &req->read_section );
2852 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2854 http_request_t *req = (http_request_t*)hdr;
2857 EnterCriticalSection( &req->read_section );
2858 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2859 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2861 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2862 if(res == ERROR_SUCCESS)
2864 LeaveCriticalSection( &req->read_section );
2869 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2871 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2872 http_request_t *req = (http_request_t*)workRequest->hdr;
2875 TRACE("INTERNETREADFILEEXA %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_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2884 DWORD flags, DWORD_PTR context)
2886 http_request_t *req = (http_request_t*)hdr;
2887 DWORD res, size, read, error = ERROR_SUCCESS;
2889 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2890 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2892 if (buffers->dwStructSize != sizeof(*buffers))
2893 return ERROR_INVALID_PARAMETER;
2895 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2897 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2899 WORKREQUEST workRequest;
2901 if (TryEnterCriticalSection( &req->read_section ))
2903 if (get_avail_data(req))
2905 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2906 &buffers->dwBufferLength, FALSE);
2907 size = buffers->dwBufferLength;
2908 LeaveCriticalSection( &req->read_section );
2911 LeaveCriticalSection( &req->read_section );
2914 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2915 workRequest.hdr = WININET_AddRef(&req->hdr);
2916 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2918 INTERNET_AsyncCall(&workRequest);
2920 return ERROR_IO_PENDING;
2924 size = buffers->dwBufferLength;
2926 EnterCriticalSection( &req->read_section );
2927 if(hdr->dwError == ERROR_SUCCESS)
2928 hdr->dwError = INTERNET_HANDLE_IN_USE;
2929 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2930 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2933 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2934 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2935 if(res != ERROR_SUCCESS)
2938 read += buffers->dwBufferLength;
2939 if(read == size || end_of_read_data(req))
2942 LeaveCriticalSection( &req->read_section );
2944 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2945 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2946 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2947 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2949 EnterCriticalSection( &req->read_section );
2952 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2953 hdr->dwError = ERROR_SUCCESS;
2955 error = hdr->dwError;
2957 LeaveCriticalSection( &req->read_section );
2958 size = buffers->dwBufferLength;
2959 buffers->dwBufferLength = read;
2962 if (res == ERROR_SUCCESS) {
2963 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2964 &size, sizeof(size));
2967 return res==ERROR_SUCCESS ? error : res;
2970 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2972 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2973 http_request_t *req = (http_request_t*)workRequest->hdr;
2976 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2978 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2979 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2981 send_request_complete(req, res == ERROR_SUCCESS, res);
2984 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2985 DWORD flags, DWORD_PTR context)
2988 http_request_t *req = (http_request_t*)hdr;
2989 DWORD res, size, read, error = ERROR_SUCCESS;
2991 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2992 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2994 if (buffers->dwStructSize != sizeof(*buffers))
2995 return ERROR_INVALID_PARAMETER;
2997 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2999 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
3001 WORKREQUEST workRequest;
3003 if (TryEnterCriticalSection( &req->read_section ))
3005 if (get_avail_data(req))
3007 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
3008 &buffers->dwBufferLength, FALSE);
3009 size = buffers->dwBufferLength;
3010 LeaveCriticalSection( &req->read_section );
3013 LeaveCriticalSection( &req->read_section );
3016 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
3017 workRequest.hdr = WININET_AddRef(&req->hdr);
3018 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
3020 INTERNET_AsyncCall(&workRequest);
3022 return ERROR_IO_PENDING;
3026 size = buffers->dwBufferLength;
3028 EnterCriticalSection( &req->read_section );
3029 if(hdr->dwError == ERROR_SUCCESS)
3030 hdr->dwError = INTERNET_HANDLE_IN_USE;
3031 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3032 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3035 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
3036 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
3037 if(res != ERROR_SUCCESS)
3040 read += buffers->dwBufferLength;
3041 if(read == size || end_of_read_data(req))
3044 LeaveCriticalSection( &req->read_section );
3046 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3047 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
3048 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
3049 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3051 EnterCriticalSection( &req->read_section );
3054 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3055 hdr->dwError = ERROR_SUCCESS;
3057 error = hdr->dwError;
3059 LeaveCriticalSection( &req->read_section );
3060 size = buffers->dwBufferLength;
3061 buffers->dwBufferLength = read;
3064 if (res == ERROR_SUCCESS) {
3065 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3066 &size, sizeof(size));
3069 return res==ERROR_SUCCESS ? error : res;
3072 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3075 http_request_t *request = (http_request_t*)hdr;
3077 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3080 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3081 if (res == ERROR_SUCCESS)
3082 request->bytesWritten += *written;
3084 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3088 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
3090 http_request_t *req = (http_request_t*)workRequest->hdr;
3092 HTTP_ReceiveRequestData(req, FALSE);
3095 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3097 http_request_t *req = (http_request_t*)hdr;
3099 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3101 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3103 WORKREQUEST workRequest;
3105 /* never wait, if we can't enter the section we queue an async request right away */
3106 if (TryEnterCriticalSection( &req->read_section ))
3108 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3109 if ((*available = get_avail_data( req ))) goto done;
3110 if (end_of_read_data( req )) goto done;
3111 LeaveCriticalSection( &req->read_section );
3114 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3115 workRequest.hdr = WININET_AddRef( &req->hdr );
3117 INTERNET_AsyncCall(&workRequest);
3119 return ERROR_IO_PENDING;
3122 EnterCriticalSection( &req->read_section );
3124 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3126 refill_read_buffer( req, READMODE_ASYNC, NULL );
3127 *available = get_avail_data( req );
3131 LeaveCriticalSection( &req->read_section );
3133 TRACE( "returning %u\n", *available );
3134 return ERROR_SUCCESS;
3137 static const object_vtbl_t HTTPREQVtbl = {
3139 HTTPREQ_CloseConnection,
3140 HTTPREQ_QueryOption,
3143 HTTPREQ_ReadFileExA,
3144 HTTPREQ_ReadFileExW,
3146 HTTPREQ_QueryDataAvailable,
3150 /***********************************************************************
3151 * HTTP_HttpOpenRequestW (internal)
3153 * Open a HTTP request handle
3156 * HINTERNET a HTTP request handle on success
3160 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3161 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3162 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3163 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3165 appinfo_t *hIC = session->appInfo;
3166 http_request_t *request;
3168 DWORD len, res = ERROR_SUCCESS;
3172 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3174 return ERROR_OUTOFMEMORY;
3176 request->hdr.htype = WH_HHTTPREQ;
3177 request->hdr.dwFlags = dwFlags;
3178 request->hdr.dwContext = dwContext;
3179 request->contentLength = ~0u;
3181 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3182 request->data_stream = &request->netconn_stream.data_stream;
3183 request->connect_timeout = session->connect_timeout;
3184 request->send_timeout = session->send_timeout;
3185 request->receive_timeout = session->receive_timeout;
3187 InitializeCriticalSection( &request->read_section );
3188 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3190 WININET_AddRef( &session->hdr );
3191 request->session = session;
3192 list_add_head( &session->hdr.children, &request->hdr.entry );
3194 port = session->hostPort;
3195 if(port == INTERNET_INVALID_PORT_NUMBER)
3196 port = dwFlags & INTERNET_FLAG_SECURE ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3198 request->server = get_server(session->hostName, port, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3199 if(!request->server) {
3200 WININET_Release(&request->hdr);
3201 return ERROR_OUTOFMEMORY;
3204 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3205 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3206 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3207 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3209 if (lpszObjectName && *lpszObjectName) {
3213 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3214 if (rc != E_POINTER)
3215 len = strlenW(lpszObjectName)+1;
3216 request->path = heap_alloc(len*sizeof(WCHAR));
3217 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3218 URL_ESCAPE_SPACES_ONLY);
3221 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3222 strcpyW(request->path,lpszObjectName);
3225 static const WCHAR slashW[] = {'/',0};
3227 request->path = heap_strdupW(slashW);
3230 if (lpszReferrer && *lpszReferrer)
3231 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3233 if (lpszAcceptTypes)
3236 for (i = 0; lpszAcceptTypes[i]; i++)
3238 if (!*lpszAcceptTypes[i]) continue;
3239 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3240 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3241 HTTP_ADDHDR_FLAG_REQ |
3242 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3246 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3247 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3249 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3250 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3251 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3255 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3257 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3259 res = ERROR_OUTOFMEMORY;
3263 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3264 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3265 heap_free(host_name);
3268 HTTP_ProcessHeader(request, hostW, session->hostName,
3269 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3271 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3272 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3273 INTERNET_DEFAULT_HTTPS_PORT :
3274 INTERNET_DEFAULT_HTTP_PORT);
3276 if (hIC->proxy && hIC->proxy[0])
3277 HTTP_DealWithProxy( hIC, session, request );
3279 INTERNET_SendCallback(&session->hdr, dwContext,
3280 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3284 TRACE("<-- %u (%p)\n", res, request);
3286 if(res != ERROR_SUCCESS) {
3287 WININET_Release( &request->hdr );
3292 *ret = request->hdr.hInternet;
3293 return ERROR_SUCCESS;
3296 /***********************************************************************
3297 * HttpOpenRequestW (WININET.@)
3299 * Open a HTTP request handle
3302 * HINTERNET a HTTP request handle on success
3306 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3307 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3308 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3309 DWORD dwFlags, DWORD_PTR dwContext)
3311 http_session_t *session;
3312 HINTERNET handle = NULL;
3315 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3316 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3317 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3318 dwFlags, dwContext);
3319 if(lpszAcceptTypes!=NULL)
3322 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3323 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3326 session = (http_session_t*) get_handle_object( hHttpSession );
3327 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3329 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3334 * My tests seem to show that the windows version does not
3335 * become asynchronous until after this point. And anyhow
3336 * if this call was asynchronous then how would you get the
3337 * necessary HINTERNET pointer returned by this function.
3340 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3341 lpszVersion, lpszReferrer, lpszAcceptTypes,
3342 dwFlags, dwContext, &handle);
3345 WININET_Release( &session->hdr );
3346 TRACE("returning %p\n", handle);
3347 if(res != ERROR_SUCCESS)
3352 static const LPCWSTR header_lookup[] = {
3353 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3354 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3355 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3356 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3357 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3358 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3359 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3360 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3361 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3362 szDate, /* HTTP_QUERY_DATE = 9 */
3363 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3364 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3365 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3366 szURI, /* HTTP_QUERY_URI = 13 */
3367 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3368 NULL, /* HTTP_QUERY_COST = 15 */
3369 NULL, /* HTTP_QUERY_LINK = 16 */
3370 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3371 NULL, /* HTTP_QUERY_VERSION = 18 */
3372 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3373 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3374 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3375 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3376 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3377 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3378 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3379 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3380 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3381 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3382 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3383 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3384 NULL, /* HTTP_QUERY_FROM = 31 */
3385 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3386 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3387 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3388 szReferer, /* HTTP_QUERY_REFERER = 35 */
3389 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3390 szServer, /* HTTP_QUERY_SERVER = 37 */
3391 NULL, /* HTTP_TITLE = 38 */
3392 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3393 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3394 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3395 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3396 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3397 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3398 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3399 NULL, /* HTTP_QUERY_REFRESH = 46 */
3400 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3401 szAge, /* HTTP_QUERY_AGE = 48 */
3402 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3403 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3404 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3405 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3406 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3407 szETag, /* HTTP_QUERY_ETAG = 54 */
3408 hostW, /* HTTP_QUERY_HOST = 55 */
3409 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3410 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3411 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3412 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3413 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3414 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3415 szRange, /* HTTP_QUERY_RANGE = 62 */
3416 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3417 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3418 szVary, /* HTTP_QUERY_VARY = 65 */
3419 szVia, /* HTTP_QUERY_VIA = 66 */
3420 szWarning, /* HTTP_QUERY_WARNING = 67 */
3421 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3422 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3423 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3426 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3428 /***********************************************************************
3429 * HTTP_HttpQueryInfoW (internal)
3431 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3432 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3434 LPHTTPHEADERW lphttpHdr = NULL;
3435 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3436 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3437 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3440 /* Find requested header structure */
3443 case HTTP_QUERY_CUSTOM:
3444 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3445 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3447 case HTTP_QUERY_RAW_HEADERS_CRLF:
3451 DWORD res = ERROR_INVALID_PARAMETER;
3454 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3456 headers = request->rawHeaders;
3459 len = strlenW(headers) * sizeof(WCHAR);
3461 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3463 len += sizeof(WCHAR);
3464 res = ERROR_INSUFFICIENT_BUFFER;
3469 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3472 len = strlenW(szCrLf) * sizeof(WCHAR);
3473 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3475 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3476 res = ERROR_SUCCESS;
3478 *lpdwBufferLength = len;
3480 if (request_only) heap_free(headers);
3483 case HTTP_QUERY_RAW_HEADERS:
3485 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3487 LPWSTR pszString = lpBuffer;
3489 for (i = 0; ppszRawHeaderLines[i]; i++)
3490 size += strlenW(ppszRawHeaderLines[i]) + 1;
3492 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3494 HTTP_FreeTokens(ppszRawHeaderLines);
3495 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3496 return ERROR_INSUFFICIENT_BUFFER;
3500 for (i = 0; ppszRawHeaderLines[i]; i++)
3502 DWORD len = strlenW(ppszRawHeaderLines[i]);
3503 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3507 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3509 *lpdwBufferLength = size * sizeof(WCHAR);
3510 HTTP_FreeTokens(ppszRawHeaderLines);
3512 return ERROR_SUCCESS;
3514 case HTTP_QUERY_STATUS_TEXT:
3515 if (request->statusText)
3517 DWORD len = strlenW(request->statusText);
3518 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3520 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3521 return ERROR_INSUFFICIENT_BUFFER;
3525 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3526 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3528 *lpdwBufferLength = len * sizeof(WCHAR);
3529 return ERROR_SUCCESS;
3532 case HTTP_QUERY_VERSION:
3533 if (request->version)
3535 DWORD len = strlenW(request->version);
3536 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3538 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3539 return ERROR_INSUFFICIENT_BUFFER;
3543 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3544 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3546 *lpdwBufferLength = len * sizeof(WCHAR);
3547 return ERROR_SUCCESS;
3550 case HTTP_QUERY_CONTENT_ENCODING:
3551 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3552 requested_index,request_only);
3554 case HTTP_QUERY_STATUS_CODE: {
3555 DWORD res = ERROR_SUCCESS;
3558 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3563 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3564 if(*lpdwBufferLength >= sizeof(DWORD))
3565 *(DWORD*)lpBuffer = request->status_code;
3567 res = ERROR_INSUFFICIENT_BUFFER;
3568 *lpdwBufferLength = sizeof(DWORD);
3572 static const WCHAR formatW[] = {'%','u',0};
3574 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3576 if(size <= *lpdwBufferLength) {
3577 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3579 size += sizeof(WCHAR);
3580 res = ERROR_INSUFFICIENT_BUFFER;
3583 *lpdwBufferLength = size;
3588 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3590 if (level < LAST_TABLE_HEADER && header_lookup[level])
3591 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3592 requested_index,request_only);
3596 lphttpHdr = &request->custHeaders[index];
3598 /* Ensure header satisfies requested attributes */
3600 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3601 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3603 return ERROR_HTTP_HEADER_NOT_FOUND;
3606 if (lpdwIndex) (*lpdwIndex)++;
3608 /* coalesce value to requested type */
3609 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3611 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3612 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3614 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3620 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3622 tmpTM = *gmtime(&tmpTime);
3623 STHook = (SYSTEMTIME *)lpBuffer;
3624 STHook->wDay = tmpTM.tm_mday;
3625 STHook->wHour = tmpTM.tm_hour;
3626 STHook->wMilliseconds = 0;
3627 STHook->wMinute = tmpTM.tm_min;
3628 STHook->wDayOfWeek = tmpTM.tm_wday;
3629 STHook->wMonth = tmpTM.tm_mon + 1;
3630 STHook->wSecond = tmpTM.tm_sec;
3631 STHook->wYear = tmpTM.tm_year;
3633 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3634 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3635 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3637 else if (lphttpHdr->lpszValue)
3639 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3641 if (len > *lpdwBufferLength)
3643 *lpdwBufferLength = len;
3644 return ERROR_INSUFFICIENT_BUFFER;
3648 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3649 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3651 *lpdwBufferLength = len - sizeof(WCHAR);
3653 return ERROR_SUCCESS;
3656 /***********************************************************************
3657 * HttpQueryInfoW (WININET.@)
3659 * Queries for information about an HTTP request
3666 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3667 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3669 http_request_t *request;
3672 if (TRACE_ON(wininet)) {
3673 #define FE(x) { x, #x }
3674 static const wininet_flag_info query_flags[] = {
3675 FE(HTTP_QUERY_MIME_VERSION),
3676 FE(HTTP_QUERY_CONTENT_TYPE),
3677 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3678 FE(HTTP_QUERY_CONTENT_ID),
3679 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3680 FE(HTTP_QUERY_CONTENT_LENGTH),
3681 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3682 FE(HTTP_QUERY_ALLOW),
3683 FE(HTTP_QUERY_PUBLIC),
3684 FE(HTTP_QUERY_DATE),
3685 FE(HTTP_QUERY_EXPIRES),
3686 FE(HTTP_QUERY_LAST_MODIFIED),
3687 FE(HTTP_QUERY_MESSAGE_ID),
3689 FE(HTTP_QUERY_DERIVED_FROM),
3690 FE(HTTP_QUERY_COST),
3691 FE(HTTP_QUERY_LINK),
3692 FE(HTTP_QUERY_PRAGMA),
3693 FE(HTTP_QUERY_VERSION),
3694 FE(HTTP_QUERY_STATUS_CODE),
3695 FE(HTTP_QUERY_STATUS_TEXT),
3696 FE(HTTP_QUERY_RAW_HEADERS),
3697 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3698 FE(HTTP_QUERY_CONNECTION),
3699 FE(HTTP_QUERY_ACCEPT),
3700 FE(HTTP_QUERY_ACCEPT_CHARSET),
3701 FE(HTTP_QUERY_ACCEPT_ENCODING),
3702 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3703 FE(HTTP_QUERY_AUTHORIZATION),
3704 FE(HTTP_QUERY_CONTENT_ENCODING),
3705 FE(HTTP_QUERY_FORWARDED),
3706 FE(HTTP_QUERY_FROM),
3707 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3708 FE(HTTP_QUERY_LOCATION),
3709 FE(HTTP_QUERY_ORIG_URI),
3710 FE(HTTP_QUERY_REFERER),
3711 FE(HTTP_QUERY_RETRY_AFTER),
3712 FE(HTTP_QUERY_SERVER),
3713 FE(HTTP_QUERY_TITLE),
3714 FE(HTTP_QUERY_USER_AGENT),
3715 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3716 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3717 FE(HTTP_QUERY_ACCEPT_RANGES),
3718 FE(HTTP_QUERY_SET_COOKIE),
3719 FE(HTTP_QUERY_COOKIE),
3720 FE(HTTP_QUERY_REQUEST_METHOD),
3721 FE(HTTP_QUERY_REFRESH),
3722 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3724 FE(HTTP_QUERY_CACHE_CONTROL),
3725 FE(HTTP_QUERY_CONTENT_BASE),
3726 FE(HTTP_QUERY_CONTENT_LOCATION),
3727 FE(HTTP_QUERY_CONTENT_MD5),
3728 FE(HTTP_QUERY_CONTENT_RANGE),
3729 FE(HTTP_QUERY_ETAG),
3730 FE(HTTP_QUERY_HOST),
3731 FE(HTTP_QUERY_IF_MATCH),
3732 FE(HTTP_QUERY_IF_NONE_MATCH),
3733 FE(HTTP_QUERY_IF_RANGE),
3734 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3735 FE(HTTP_QUERY_MAX_FORWARDS),
3736 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3737 FE(HTTP_QUERY_RANGE),
3738 FE(HTTP_QUERY_TRANSFER_ENCODING),
3739 FE(HTTP_QUERY_UPGRADE),
3740 FE(HTTP_QUERY_VARY),
3742 FE(HTTP_QUERY_WARNING),
3743 FE(HTTP_QUERY_CUSTOM)
3745 static const wininet_flag_info modifier_flags[] = {
3746 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3747 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3748 FE(HTTP_QUERY_FLAG_NUMBER),
3749 FE(HTTP_QUERY_FLAG_COALESCE)
3752 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3753 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3756 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3757 TRACE(" Attribute:");
3758 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3759 if (query_flags[i].val == info) {
3760 TRACE(" %s", query_flags[i].name);
3764 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3765 TRACE(" Unknown (%08x)", info);
3768 TRACE(" Modifier:");
3769 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3770 if (modifier_flags[i].val & info_mod) {
3771 TRACE(" %s", modifier_flags[i].name);
3772 info_mod &= ~ modifier_flags[i].val;
3777 TRACE(" Unknown (%08x)", info_mod);
3782 request = (http_request_t*) get_handle_object( hHttpRequest );
3783 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3785 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3789 if (lpBuffer == NULL)
3790 *lpdwBufferLength = 0;
3791 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3792 lpBuffer, lpdwBufferLength, lpdwIndex);
3796 WININET_Release( &request->hdr );
3798 TRACE("%u <--\n", res);
3799 if(res != ERROR_SUCCESS)
3801 return res == ERROR_SUCCESS;
3804 /***********************************************************************
3805 * HttpQueryInfoA (WININET.@)
3807 * Queries for information about an HTTP request
3814 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3815 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3821 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3822 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3824 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3825 lpdwBufferLength, lpdwIndex );
3831 len = (*lpdwBufferLength)*sizeof(WCHAR);
3832 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3834 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3840 bufferW = heap_alloc(alloclen);
3841 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3842 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3843 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3850 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3854 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3855 lpBuffer, *lpdwBufferLength, NULL, NULL );
3856 *lpdwBufferLength = len - 1;
3858 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3861 /* since the strings being returned from HttpQueryInfoW should be
3862 * only ASCII characters, it is reasonable to assume that all of
3863 * the Unicode characters can be reduced to a single byte */
3864 *lpdwBufferLength = len / sizeof(WCHAR);
3866 heap_free( bufferW );
3870 /***********************************************************************
3871 * HTTP_GetRedirectURL (internal)
3873 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3875 static WCHAR szHttp[] = {'h','t','t','p',0};
3876 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3877 http_session_t *session = request->session;
3878 URL_COMPONENTSW urlComponents;
3879 DWORD url_length = 0;
3881 LPWSTR combined_url;
3883 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3884 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3885 urlComponents.dwSchemeLength = 0;
3886 urlComponents.lpszHostName = session->hostName;
3887 urlComponents.dwHostNameLength = 0;
3888 urlComponents.nPort = session->hostPort;
3889 urlComponents.lpszUserName = session->userName;
3890 urlComponents.dwUserNameLength = 0;
3891 urlComponents.lpszPassword = NULL;
3892 urlComponents.dwPasswordLength = 0;
3893 urlComponents.lpszUrlPath = request->path;
3894 urlComponents.dwUrlPathLength = 0;
3895 urlComponents.lpszExtraInfo = NULL;
3896 urlComponents.dwExtraInfoLength = 0;
3898 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3899 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3902 orig_url = heap_alloc(url_length);
3904 /* convert from bytes to characters */
3905 url_length = url_length / sizeof(WCHAR) - 1;
3906 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3908 heap_free(orig_url);
3913 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3914 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3916 heap_free(orig_url);
3919 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3921 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3923 heap_free(orig_url);
3924 heap_free(combined_url);
3927 heap_free(orig_url);
3928 return combined_url;
3932 /***********************************************************************
3933 * HTTP_HandleRedirect (internal)
3935 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3937 http_session_t *session = request->session;
3938 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3943 /* if it's an absolute path, keep the same session info */
3944 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3948 URL_COMPONENTSW urlComponents;
3949 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3950 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3951 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3952 BOOL custom_port = FALSE;
3954 static WCHAR httpW[] = {'h','t','t','p',0};
3955 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3961 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3962 urlComponents.lpszScheme = protocol;
3963 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3964 urlComponents.lpszHostName = hostName;
3965 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3966 urlComponents.lpszUserName = userName;
3967 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3968 urlComponents.lpszPassword = NULL;
3969 urlComponents.dwPasswordLength = 0;
3970 urlComponents.lpszUrlPath = path;
3971 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3972 urlComponents.lpszExtraInfo = NULL;
3973 urlComponents.dwExtraInfoLength = 0;
3974 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3975 return INTERNET_GetLastError();
3977 if(!strcmpiW(protocol, httpW)) {
3978 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3979 TRACE("redirect from secure page to non-secure page\n");
3980 /* FIXME: warn about from secure redirect to non-secure page */
3981 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3984 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3985 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3986 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3988 }else if(!strcmpiW(protocol, httpsW)) {
3989 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3990 TRACE("redirect from non-secure page to secure page\n");
3991 /* FIXME: notify about redirect to secure page */
3992 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3995 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3996 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3997 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
4001 heap_free(session->hostName);
4005 static const WCHAR fmt[] = {'%','s',':','%','u',0};
4006 len = lstrlenW(hostName);
4007 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
4008 session->hostName = heap_alloc(len*sizeof(WCHAR));
4009 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
4012 session->hostName = heap_strdupW(hostName);
4014 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
4016 heap_free(session->userName);
4017 session->userName = NULL;
4019 session->userName = heap_strdupW(userName);
4021 reset_data_stream(request);
4023 if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
4024 server_t *new_server;
4026 new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
4027 server_release(request->server);
4028 request->server = new_server;
4031 heap_free(request->path);
4038 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
4039 if (rc != E_POINTER)
4040 needed = strlenW(path)+1;
4041 request->path = heap_alloc(needed*sizeof(WCHAR));
4042 rc = UrlEscapeW(path, request->path, &needed,
4043 URL_ESCAPE_SPACES_ONLY);
4046 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
4047 strcpyW(request->path,path);
4051 /* Remove custom content-type/length headers on redirects. */
4052 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
4054 HTTP_DeleteCustomHeader(request, index);
4055 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
4057 HTTP_DeleteCustomHeader(request, index);
4059 return ERROR_SUCCESS;
4062 /***********************************************************************
4063 * HTTP_build_req (internal)
4065 * concatenate all the strings in the request together
4067 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
4072 for( t = list; *t ; t++ )
4073 len += strlenW( *t );
4076 str = heap_alloc(len*sizeof(WCHAR));
4079 for( t = list; *t ; t++ )
4085 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
4087 server_t *server = request->server;
4089 LPWSTR requestString;
4095 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
4096 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
4100 lpszPath = heap_alloc((lstrlenW(server->name) + 13)*sizeof(WCHAR));
4101 sprintfW(lpszPath, szFormat, server->name, server->port);
4102 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
4103 heap_free( lpszPath );
4105 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4106 NULL, 0, NULL, NULL );
4107 len--; /* the nul terminator isn't needed */
4108 ascii_req = heap_alloc(len);
4109 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4110 heap_free( requestString );
4112 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4114 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4115 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4116 heap_free( ascii_req );
4117 if (res != ERROR_SUCCESS)
4120 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4122 return ERROR_HTTP_INVALID_HEADER;
4124 return ERROR_SUCCESS;
4127 static void HTTP_InsertCookies(http_request_t *request)
4129 DWORD cookie_size, size, cnt = 0;
4133 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4135 host = HTTP_GetHeader(request, hostW);
4139 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4142 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4143 if(!(cookies = heap_alloc(size)))
4146 cnt += sprintfW(cookies, cookieW);
4147 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4148 strcatW(cookies, szCrLf);
4150 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4155 static WORD HTTP_ParseWkday(LPCWSTR day)
4157 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4165 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4166 if (!strcmpiW(day, days[i]))
4173 static WORD HTTP_ParseMonth(LPCWSTR month)
4175 static const WCHAR jan[] = { 'j','a','n',0 };
4176 static const WCHAR feb[] = { 'f','e','b',0 };
4177 static const WCHAR mar[] = { 'm','a','r',0 };
4178 static const WCHAR apr[] = { 'a','p','r',0 };
4179 static const WCHAR may[] = { 'm','a','y',0 };
4180 static const WCHAR jun[] = { 'j','u','n',0 };
4181 static const WCHAR jul[] = { 'j','u','l',0 };
4182 static const WCHAR aug[] = { 'a','u','g',0 };
4183 static const WCHAR sep[] = { 's','e','p',0 };
4184 static const WCHAR oct[] = { 'o','c','t',0 };
4185 static const WCHAR nov[] = { 'n','o','v',0 };
4186 static const WCHAR dec[] = { 'd','e','c',0 };
4188 if (!strcmpiW(month, jan)) return 1;
4189 if (!strcmpiW(month, feb)) return 2;
4190 if (!strcmpiW(month, mar)) return 3;
4191 if (!strcmpiW(month, apr)) return 4;
4192 if (!strcmpiW(month, may)) return 5;
4193 if (!strcmpiW(month, jun)) return 6;
4194 if (!strcmpiW(month, jul)) return 7;
4195 if (!strcmpiW(month, aug)) return 8;
4196 if (!strcmpiW(month, sep)) return 9;
4197 if (!strcmpiW(month, oct)) return 10;
4198 if (!strcmpiW(month, nov)) return 11;
4199 if (!strcmpiW(month, dec)) return 12;
4204 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4205 * optionally preceded by whitespace.
4206 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4207 * st, and sets *str to the first character after the time format.
4209 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4215 while (isspaceW(*ptr))
4218 num = strtoulW(ptr, &nextPtr, 10);
4219 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4221 ERR("unexpected time format %s\n", debugstr_w(ptr));
4226 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4230 st->wHour = (WORD)num;
4231 num = strtoulW(ptr, &nextPtr, 10);
4232 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4234 ERR("unexpected time format %s\n", debugstr_w(ptr));
4239 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4243 st->wMinute = (WORD)num;
4244 num = strtoulW(ptr, &nextPtr, 10);
4245 if (!nextPtr || nextPtr <= ptr)
4247 ERR("unexpected time format %s\n", debugstr_w(ptr));
4252 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4257 st->wSecond = (WORD)num;
4261 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4263 static const WCHAR gmt[]= { 'G','M','T',0 };
4264 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4266 SYSTEMTIME st = { 0 };
4269 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4270 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4273 st.wDayOfWeek = HTTP_ParseWkday(day);
4274 if (st.wDayOfWeek >= 7)
4276 ERR("unexpected weekday %s\n", debugstr_w(day));
4280 while (isspaceW(*ptr))
4283 for (monthPtr = month; !isspace(*ptr) &&
4284 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4288 st.wMonth = HTTP_ParseMonth(month);
4289 if (!st.wMonth || st.wMonth > 12)
4291 ERR("unexpected month %s\n", debugstr_w(month));
4295 while (isspaceW(*ptr))
4298 num = strtoulW(ptr, &nextPtr, 10);
4299 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4301 ERR("unexpected day %s\n", debugstr_w(ptr));
4305 st.wDay = (WORD)num;
4307 while (isspaceW(*ptr))
4310 if (!HTTP_ParseTime(&st, &ptr))
4313 while (isspaceW(*ptr))
4316 num = strtoulW(ptr, &nextPtr, 10);
4317 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4319 ERR("unexpected year %s\n", debugstr_w(ptr));
4323 st.wYear = (WORD)num;
4325 while (isspaceW(*ptr))
4328 /* asctime() doesn't report a timezone, but some web servers do, so accept
4329 * with or without GMT.
4331 if (*ptr && strcmpW(ptr, gmt))
4333 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4336 return SystemTimeToFileTime(&st, ft);
4339 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4341 static const WCHAR gmt[]= { 'G','M','T',0 };
4342 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4345 SYSTEMTIME st = { 0 };
4347 ptr = strchrW(value, ',');
4350 if (ptr - value != 3)
4352 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4355 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4357 st.wDayOfWeek = HTTP_ParseWkday(day);
4358 if (st.wDayOfWeek > 6)
4360 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4365 while (isspaceW(*ptr))
4368 num = strtoulW(ptr, &nextPtr, 10);
4369 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4371 WARN("unexpected day %s\n", debugstr_w(value));
4375 st.wDay = (WORD)num;
4377 while (isspaceW(*ptr))
4380 for (monthPtr = month; !isspace(*ptr) &&
4381 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4385 st.wMonth = HTTP_ParseMonth(month);
4386 if (!st.wMonth || st.wMonth > 12)
4388 WARN("unexpected month %s\n", debugstr_w(month));
4392 while (isspaceW(*ptr))
4395 num = strtoulW(ptr, &nextPtr, 10);
4396 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4398 ERR("unexpected year %s\n", debugstr_w(value));
4402 st.wYear = (WORD)num;
4404 if (!HTTP_ParseTime(&st, &ptr))
4407 while (isspaceW(*ptr))
4410 if (strcmpW(ptr, gmt))
4412 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4415 return SystemTimeToFileTime(&st, ft);
4418 static WORD HTTP_ParseWeekday(LPCWSTR day)
4420 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4421 { 'm','o','n','d','a','y',0 },
4422 { 't','u','e','s','d','a','y',0 },
4423 { 'w','e','d','n','e','s','d','a','y',0 },
4424 { 't','h','u','r','s','d','a','y',0 },
4425 { 'f','r','i','d','a','y',0 },
4426 { 's','a','t','u','r','d','a','y',0 }};
4428 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4429 if (!strcmpiW(day, days[i]))
4436 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4438 static const WCHAR gmt[]= { 'G','M','T',0 };
4439 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4442 SYSTEMTIME st = { 0 };
4444 ptr = strchrW(value, ',');
4447 if (ptr - value == 3)
4449 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4451 st.wDayOfWeek = HTTP_ParseWkday(day);
4452 if (st.wDayOfWeek > 6)
4454 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4458 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4460 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4461 day[ptr - value + 1] = 0;
4462 st.wDayOfWeek = HTTP_ParseWeekday(day);
4463 if (st.wDayOfWeek > 6)
4465 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4471 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4476 while (isspaceW(*ptr))
4479 num = strtoulW(ptr, &nextPtr, 10);
4480 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4482 ERR("unexpected day %s\n", debugstr_w(value));
4486 st.wDay = (WORD)num;
4490 ERR("unexpected month format %s\n", debugstr_w(ptr));
4495 for (monthPtr = month; *ptr != '-' &&
4496 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4500 st.wMonth = HTTP_ParseMonth(month);
4501 if (!st.wMonth || st.wMonth > 12)
4503 ERR("unexpected month %s\n", debugstr_w(month));
4509 ERR("unexpected year format %s\n", debugstr_w(ptr));
4514 num = strtoulW(ptr, &nextPtr, 10);
4515 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4517 ERR("unexpected year %s\n", debugstr_w(value));
4521 st.wYear = (WORD)num;
4523 if (!HTTP_ParseTime(&st, &ptr))
4526 while (isspaceW(*ptr))
4529 if (strcmpW(ptr, gmt))
4531 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4534 return SystemTimeToFileTime(&st, ft);
4537 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4539 static const WCHAR zero[] = { '0',0 };
4542 if (!strcmpW(value, zero))
4544 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4547 else if (strchrW(value, ','))
4549 ret = HTTP_ParseRfc1123Date(value, ft);
4552 ret = HTTP_ParseRfc850Date(value, ft);
4554 ERR("unexpected date format %s\n", debugstr_w(value));
4559 ret = HTTP_ParseDateAsAsctime(value, ft);
4561 ERR("unexpected date format %s\n", debugstr_w(value));
4566 static void HTTP_ProcessExpires(http_request_t *request)
4568 BOOL expirationFound = FALSE;
4571 /* Look for a Cache-Control header with a max-age directive, as it takes
4572 * precedence over the Expires header.
4574 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4575 if (headerIndex != -1)
4577 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4580 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4582 LPWSTR comma = strchrW(ptr, ','), end, equal;
4587 end = ptr + strlenW(ptr);
4588 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4592 static const WCHAR max_age[] = {
4593 'm','a','x','-','a','g','e',0 };
4595 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4600 age = strtoulW(equal + 1, &nextPtr, 10);
4601 if (nextPtr > equal + 1)
4605 NtQuerySystemTime( &ft );
4606 /* Age is in seconds, FILETIME resolution is in
4607 * 100 nanosecond intervals.
4609 ft.QuadPart += age * (ULONGLONG)1000000;
4610 request->expires.dwLowDateTime = ft.u.LowPart;
4611 request->expires.dwHighDateTime = ft.u.HighPart;
4612 expirationFound = TRUE;
4619 while (isspaceW(*ptr))
4626 if (!expirationFound)
4628 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4629 if (headerIndex != -1)
4631 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4634 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4636 expirationFound = TRUE;
4637 request->expires = ft;
4641 if (!expirationFound)
4645 /* With no known age, default to 10 minutes until expiration. */
4646 NtQuerySystemTime( &t );
4647 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4648 request->expires.dwLowDateTime = t.u.LowPart;
4649 request->expires.dwHighDateTime = t.u.HighPart;
4653 static void HTTP_ProcessLastModified(http_request_t *request)
4657 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4658 if (headerIndex != -1)
4660 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4663 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4664 request->last_modified = ft;
4668 static void http_process_keep_alive(http_request_t *req)
4672 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4674 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4676 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4679 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4681 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4682 netconn_t *netconn = NULL;
4685 assert(!request->netconn);
4686 reset_data_stream(request);
4688 res = HTTP_ResolveName(request);
4689 if(res != ERROR_SUCCESS)
4692 EnterCriticalSection(&connection_pool_cs);
4694 while(!list_empty(&request->server->conn_pool)) {
4695 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4696 list_remove(&netconn->pool_entry);
4698 if(NETCON_is_alive(netconn))
4701 TRACE("connection %p closed during idle\n", netconn);
4702 free_netconn(netconn);
4706 LeaveCriticalSection(&connection_pool_cs);
4709 TRACE("<-- reusing %p netconn\n", netconn);
4710 request->netconn = netconn;
4712 return ERROR_SUCCESS;
4715 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4716 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4718 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4719 INTERNET_STATUS_CONNECTING_TO_SERVER,
4720 request->server->addr_str,
4721 strlen(request->server->addr_str)+1);
4723 res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4724 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4725 request->connect_timeout, &netconn);
4726 if(res != ERROR_SUCCESS) {
4727 ERR("create_netconn failed: %u\n", res);
4731 request->netconn = netconn;
4733 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4734 INTERNET_STATUS_CONNECTED_TO_SERVER,
4735 request->server->addr_str, strlen(request->server->addr_str)+1);
4738 /* Note: we differ from Microsoft's WinINet here. they seem to have
4739 * a bug that causes no status callbacks to be sent when starting
4740 * a tunnel to a proxy server using the CONNECT verb. i believe our
4741 * behaviour to be more correct and to not cause any incompatibilities
4742 * because using a secure connection through a proxy server is a rare
4743 * case that would be hard for anyone to depend on */
4745 res = HTTP_SecureProxyConnect(request);
4746 if(res == ERROR_SUCCESS)
4747 res = NETCON_secure_connect(request->netconn, request->server);
4750 if(res != ERROR_SUCCESS) {
4751 http_release_netconn(request, FALSE);
4756 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4757 return ERROR_SUCCESS;
4760 /***********************************************************************
4761 * HTTP_HttpSendRequestW (internal)
4763 * Sends the specified request to the HTTP server
4766 * ERROR_SUCCESS on success
4767 * win32 error code on failure
4770 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4771 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4772 DWORD dwContentLength, BOOL bEndRequest)
4775 BOOL redirected = FALSE;
4776 LPWSTR requestString = NULL;
4779 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4780 static const WCHAR szContentLength[] =
4781 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4782 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4785 TRACE("--> %p\n", request);
4787 assert(request->hdr.htype == WH_HHTTPREQ);
4789 /* if the verb is NULL default to GET */
4791 request->verb = heap_strdupW(szGET);
4793 if (dwContentLength || strcmpW(request->verb, szGET))
4795 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4796 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4797 request->bytesToWrite = dwContentLength;
4799 if (request->session->appInfo->agent)
4801 WCHAR *agent_header;
4802 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4805 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4806 agent_header = heap_alloc(len * sizeof(WCHAR));
4807 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4809 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4810 heap_free(agent_header);
4812 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4814 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4815 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4817 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4819 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4820 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4821 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4824 /* add the headers the caller supplied */
4825 if( lpszHeaders && dwHeaderLength )
4826 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4831 BOOL reusing_connection;
4835 reusing_connection = request->netconn != NULL;
4838 request->contentLength = ~0u;
4839 request->bytesToWrite = 0;
4842 if (TRACE_ON(wininet))
4844 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4845 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4848 HTTP_FixURL(request);
4849 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4851 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4853 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4854 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4856 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4857 HTTP_InsertCookies(request);
4859 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4861 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4862 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4866 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4869 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4871 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4874 /* send the request as ASCII, tack on the optional data */
4875 if (!lpOptional || redirected)
4876 dwOptionalLength = 0;
4877 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4878 NULL, 0, NULL, NULL );
4879 ascii_req = heap_alloc(len + dwOptionalLength);
4880 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4881 ascii_req, len, NULL, NULL );
4883 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4884 len = (len + dwOptionalLength - 1);
4886 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4888 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4889 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4891 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4892 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4893 heap_free( ascii_req );
4894 if(res != ERROR_SUCCESS) {
4895 TRACE("send failed: %u\n", res);
4896 if(!reusing_connection)
4898 http_release_netconn(request, FALSE);
4903 request->bytesWritten = dwOptionalLength;
4905 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4906 INTERNET_STATUS_REQUEST_SENT,
4907 &len, sizeof(DWORD));
4913 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4914 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4916 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4917 /* FIXME: We should know that connection is closed before sending
4918 * headers. Otherwise wrong callbacks are executed */
4919 if(!responseLen && reusing_connection) {
4920 TRACE("Connection closed by server, reconnecting\n");
4921 http_release_netconn(request, FALSE);
4926 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4927 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4930 http_process_keep_alive(request);
4931 HTTP_ProcessCookies(request);
4932 HTTP_ProcessExpires(request);
4933 HTTP_ProcessLastModified(request);
4935 res = set_content_length(request);
4936 if(res != ERROR_SUCCESS)
4938 if(!request->contentLength)
4939 http_release_netconn(request, TRUE);
4941 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4943 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4944 dwBufferSize=sizeof(szNewLocation);
4945 switch(request->status_code) {
4946 case HTTP_STATUS_REDIRECT:
4947 case HTTP_STATUS_MOVED:
4948 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4949 case HTTP_STATUS_REDIRECT_METHOD:
4950 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4953 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4954 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4956 heap_free(request->verb);
4957 request->verb = heap_strdupW(szGET);
4959 http_release_netconn(request, drain_content(request, FALSE));
4960 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4962 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4963 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4964 res = HTTP_HandleRedirect(request, new_url);
4965 if (res == ERROR_SUCCESS)
4967 heap_free(requestString);
4970 heap_free( new_url );
4975 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4977 WCHAR szAuthValue[2048];
4979 if (request->status_code == HTTP_STATUS_DENIED)
4981 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4983 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4985 if (HTTP_DoAuthorization(request, szAuthValue,
4987 request->session->userName,
4988 request->session->password,
4991 heap_free(requestString);
4992 if(!drain_content(request, TRUE)) {
4993 FIXME("Could not drain content\n");
4994 http_release_netconn(request, FALSE);
5002 TRACE("Cleaning wrong authorization data\n");
5003 destroy_authinfo(request->authInfo);
5004 request->authInfo = NULL;
5007 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
5010 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
5012 if (HTTP_DoAuthorization(request, szAuthValue,
5013 &request->proxyAuthInfo,
5014 request->session->appInfo->proxyUsername,
5015 request->session->appInfo->proxyPassword,
5018 if(!drain_content(request, TRUE)) {
5019 FIXME("Could not drain content\n");
5020 http_release_netconn(request, FALSE);
5028 TRACE("Cleaning wrong proxy authorization data\n");
5029 destroy_authinfo(request->proxyAuthInfo);
5030 request->proxyAuthInfo = NULL;
5036 res = ERROR_SUCCESS;
5041 heap_free(requestString);
5043 /* TODO: send notification for P3P header */
5045 if(res == ERROR_SUCCESS)
5046 create_cache_entry(request);
5048 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5050 if (res == ERROR_SUCCESS) {
5051 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
5052 HTTP_ReceiveRequestData(request, TRUE);
5054 send_request_complete(request,
5055 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
5057 send_request_complete(request, 0, res);
5065 /***********************************************************************
5067 * Helper functions for the HttpSendRequest(Ex) functions
5070 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
5072 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
5073 http_request_t *request = (http_request_t*) workRequest->hdr;
5075 TRACE("%p\n", request);
5077 HTTP_HttpSendRequestW(request, req->lpszHeader,
5078 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5079 req->dwContentLength, req->bEndRequest);
5081 heap_free(req->lpszHeader);
5085 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5089 DWORD res = ERROR_SUCCESS;
5091 if(!request->netconn) {
5092 WARN("Not connected\n");
5093 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5094 return ERROR_INTERNET_OPERATION_CANCELLED;
5097 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5098 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5100 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5102 res = ERROR_HTTP_HEADER_NOT_FOUND;
5104 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5105 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5107 /* process cookies here. Is this right? */
5108 http_process_keep_alive(request);
5109 HTTP_ProcessCookies(request);
5110 HTTP_ProcessExpires(request);
5111 HTTP_ProcessLastModified(request);
5113 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5114 if(!request->contentLength)
5115 http_release_netconn(request, TRUE);
5118 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5120 switch(request->status_code) {
5121 case HTTP_STATUS_REDIRECT:
5122 case HTTP_STATUS_MOVED:
5123 case HTTP_STATUS_REDIRECT_METHOD:
5124 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5125 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5126 dwBufferSize=sizeof(szNewLocation);
5127 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5130 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5131 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5133 heap_free(request->verb);
5134 request->verb = heap_strdupW(szGET);
5136 http_release_netconn(request, drain_content(request, FALSE));
5137 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5139 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5140 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5141 res = HTTP_HandleRedirect(request, new_url);
5142 if (res == ERROR_SUCCESS)
5143 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5144 heap_free( new_url );
5150 if(res == ERROR_SUCCESS)
5151 create_cache_entry(request);
5153 if (res == ERROR_SUCCESS && request->contentLength)
5154 HTTP_ReceiveRequestData(request, TRUE);
5156 send_request_complete(request, res == ERROR_SUCCESS, res);
5161 /***********************************************************************
5162 * HttpEndRequestA (WININET.@)
5164 * Ends an HTTP request that was started by HttpSendRequestEx
5167 * TRUE if successful
5171 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5172 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5174 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5178 SetLastError(ERROR_INVALID_PARAMETER);
5182 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5185 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5187 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5188 http_request_t *request = (http_request_t*)work->hdr;
5190 TRACE("%p\n", request);
5192 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5195 /***********************************************************************
5196 * HttpEndRequestW (WININET.@)
5198 * Ends an HTTP request that was started by HttpSendRequestEx
5201 * TRUE if successful
5205 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5206 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5208 http_request_t *request;
5211 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5215 SetLastError(ERROR_INVALID_PARAMETER);
5219 request = (http_request_t*) get_handle_object( hRequest );
5221 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5223 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5225 WININET_Release( &request->hdr );
5228 request->hdr.dwFlags |= dwFlags;
5230 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5233 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5235 work.asyncproc = AsyncHttpEndRequestProc;
5236 work.hdr = WININET_AddRef( &request->hdr );
5238 work_endrequest = &work.u.HttpEndRequestW;
5239 work_endrequest->dwFlags = dwFlags;
5240 work_endrequest->dwContext = dwContext;
5242 INTERNET_AsyncCall(&work);
5243 res = ERROR_IO_PENDING;
5246 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5248 WININET_Release( &request->hdr );
5249 TRACE("%u <--\n", res);
5250 if(res != ERROR_SUCCESS)
5252 return res == ERROR_SUCCESS;
5255 /***********************************************************************
5256 * HttpSendRequestExA (WININET.@)
5258 * Sends the specified request to the HTTP server and allows chunked
5263 * Failure: FALSE, call GetLastError() for more information.
5265 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5266 LPINTERNET_BUFFERSA lpBuffersIn,
5267 LPINTERNET_BUFFERSA lpBuffersOut,
5268 DWORD dwFlags, DWORD_PTR dwContext)
5270 INTERNET_BUFFERSW BuffersInW;
5273 LPWSTR header = NULL;
5275 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5276 lpBuffersOut, dwFlags, dwContext);
5280 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5281 if (lpBuffersIn->lpcszHeader)
5283 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5284 lpBuffersIn->dwHeadersLength,0,0);
5285 header = heap_alloc(headerlen*sizeof(WCHAR));
5286 if (!(BuffersInW.lpcszHeader = header))
5288 SetLastError(ERROR_OUTOFMEMORY);
5291 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5292 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5296 BuffersInW.lpcszHeader = NULL;
5297 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5298 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5299 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5300 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5301 BuffersInW.Next = NULL;
5304 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5310 /***********************************************************************
5311 * HttpSendRequestExW (WININET.@)
5313 * Sends the specified request to the HTTP server and allows chunked
5318 * Failure: FALSE, call GetLastError() for more information.
5320 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5321 LPINTERNET_BUFFERSW lpBuffersIn,
5322 LPINTERNET_BUFFERSW lpBuffersOut,
5323 DWORD dwFlags, DWORD_PTR dwContext)
5325 http_request_t *request;
5326 http_session_t *session;
5330 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5331 lpBuffersOut, dwFlags, dwContext);
5333 request = (http_request_t*) get_handle_object( hRequest );
5335 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5337 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5341 session = request->session;
5342 assert(session->hdr.htype == WH_HHTTPSESSION);
5343 hIC = session->appInfo;
5344 assert(hIC->hdr.htype == WH_HINIT);
5346 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5348 WORKREQUEST workRequest;
5349 struct WORKREQ_HTTPSENDREQUESTW *req;
5351 workRequest.asyncproc = AsyncHttpSendRequestProc;
5352 workRequest.hdr = WININET_AddRef( &request->hdr );
5353 req = &workRequest.u.HttpSendRequestW;
5358 if (lpBuffersIn->lpcszHeader)
5360 if (lpBuffersIn->dwHeadersLength == ~0u)
5361 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5363 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5365 req->lpszHeader = heap_alloc(size);
5366 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5368 else req->lpszHeader = NULL;
5370 req->dwHeaderLength = size / sizeof(WCHAR);
5371 req->lpOptional = lpBuffersIn->lpvBuffer;
5372 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5373 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5377 req->lpszHeader = NULL;
5378 req->dwHeaderLength = 0;
5379 req->lpOptional = NULL;
5380 req->dwOptionalLength = 0;
5381 req->dwContentLength = 0;
5384 req->bEndRequest = FALSE;
5386 INTERNET_AsyncCall(&workRequest);
5388 * This is from windows.
5390 res = ERROR_IO_PENDING;
5395 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5396 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5397 lpBuffersIn->dwBufferTotal, FALSE);
5399 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5404 WININET_Release( &request->hdr );
5408 return res == ERROR_SUCCESS;
5411 /***********************************************************************
5412 * HttpSendRequestW (WININET.@)
5414 * Sends the specified request to the HTTP server
5421 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5422 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5424 http_request_t *request;
5425 http_session_t *session = NULL;
5426 appinfo_t *hIC = NULL;
5427 DWORD res = ERROR_SUCCESS;
5429 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5430 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5432 request = (http_request_t*) get_handle_object( hHttpRequest );
5433 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5435 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5439 session = request->session;
5440 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5442 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5446 hIC = session->appInfo;
5447 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5449 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5453 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5455 WORKREQUEST workRequest;
5456 struct WORKREQ_HTTPSENDREQUESTW *req;
5458 workRequest.asyncproc = AsyncHttpSendRequestProc;
5459 workRequest.hdr = WININET_AddRef( &request->hdr );
5460 req = &workRequest.u.HttpSendRequestW;
5465 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5466 else size = dwHeaderLength * sizeof(WCHAR);
5468 req->lpszHeader = heap_alloc(size);
5469 memcpy(req->lpszHeader, lpszHeaders, size);
5472 req->lpszHeader = 0;
5473 req->dwHeaderLength = dwHeaderLength;
5474 req->lpOptional = lpOptional;
5475 req->dwOptionalLength = dwOptionalLength;
5476 req->dwContentLength = dwOptionalLength;
5477 req->bEndRequest = TRUE;
5479 INTERNET_AsyncCall(&workRequest);
5481 * This is from windows.
5483 res = ERROR_IO_PENDING;
5487 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5488 dwHeaderLength, lpOptional, dwOptionalLength,
5489 dwOptionalLength, TRUE);
5493 WININET_Release( &request->hdr );
5496 return res == ERROR_SUCCESS;
5499 /***********************************************************************
5500 * HttpSendRequestA (WININET.@)
5502 * Sends the specified request to the HTTP server
5509 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5510 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5513 LPWSTR szHeaders=NULL;
5514 DWORD nLen=dwHeaderLength;
5515 if(lpszHeaders!=NULL)
5517 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5518 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5519 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5521 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5522 heap_free(szHeaders);
5526 /***********************************************************************
5527 * HTTPSESSION_Destroy (internal)
5529 * Deallocate session handle
5532 static void HTTPSESSION_Destroy(object_header_t *hdr)
5534 http_session_t *session = (http_session_t*) hdr;
5536 TRACE("%p\n", session);
5538 WININET_Release(&session->appInfo->hdr);
5540 heap_free(session->hostName);
5541 heap_free(session->password);
5542 heap_free(session->userName);
5545 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5547 http_session_t *ses = (http_session_t *)hdr;
5550 case INTERNET_OPTION_HANDLE_TYPE:
5551 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5553 if (*size < sizeof(ULONG))
5554 return ERROR_INSUFFICIENT_BUFFER;
5556 *size = sizeof(DWORD);
5557 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5558 return ERROR_SUCCESS;
5559 case INTERNET_OPTION_CONNECT_TIMEOUT:
5560 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5562 if (*size < sizeof(DWORD))
5563 return ERROR_INSUFFICIENT_BUFFER;
5565 *size = sizeof(DWORD);
5566 *(DWORD *)buffer = ses->connect_timeout;
5567 return ERROR_SUCCESS;
5569 case INTERNET_OPTION_SEND_TIMEOUT:
5570 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5572 if (*size < sizeof(DWORD))
5573 return ERROR_INSUFFICIENT_BUFFER;
5575 *size = sizeof(DWORD);
5576 *(DWORD *)buffer = ses->send_timeout;
5577 return ERROR_SUCCESS;
5579 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5580 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5582 if (*size < sizeof(DWORD))
5583 return ERROR_INSUFFICIENT_BUFFER;
5585 *size = sizeof(DWORD);
5586 *(DWORD *)buffer = ses->receive_timeout;
5587 return ERROR_SUCCESS;
5590 return INET_QueryOption(hdr, option, buffer, size, unicode);
5593 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5595 http_session_t *ses = (http_session_t*)hdr;
5598 case INTERNET_OPTION_USERNAME:
5600 heap_free(ses->userName);
5601 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5602 return ERROR_SUCCESS;
5604 case INTERNET_OPTION_PASSWORD:
5606 heap_free(ses->password);
5607 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5608 return ERROR_SUCCESS;
5610 case INTERNET_OPTION_CONNECT_TIMEOUT:
5612 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5613 ses->connect_timeout = *(DWORD *)buffer;
5614 return ERROR_SUCCESS;
5616 case INTERNET_OPTION_SEND_TIMEOUT:
5618 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5619 ses->send_timeout = *(DWORD *)buffer;
5620 return ERROR_SUCCESS;
5622 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5624 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5625 ses->receive_timeout = *(DWORD *)buffer;
5626 return ERROR_SUCCESS;
5631 return INET_SetOption(hdr, option, buffer, size);
5634 static const object_vtbl_t HTTPSESSIONVtbl = {
5635 HTTPSESSION_Destroy,
5637 HTTPSESSION_QueryOption,
5638 HTTPSESSION_SetOption,
5647 /***********************************************************************
5648 * HTTP_Connect (internal)
5650 * Create http session handle
5653 * HINTERNET a session handle on success
5657 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5658 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5659 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5660 DWORD dwInternalFlags, HINTERNET *ret)
5662 http_session_t *session = NULL;
5666 if (!lpszServerName || !lpszServerName[0])
5667 return ERROR_INVALID_PARAMETER;
5669 assert( hIC->hdr.htype == WH_HINIT );
5671 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5673 return ERROR_OUTOFMEMORY;
5676 * According to my tests. The name is not resolved until a request is sent
5679 session->hdr.htype = WH_HHTTPSESSION;
5680 session->hdr.dwFlags = dwFlags;
5681 session->hdr.dwContext = dwContext;
5682 session->hdr.dwInternalFlags |= dwInternalFlags;
5684 WININET_AddRef( &hIC->hdr );
5685 session->appInfo = hIC;
5686 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5688 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5689 if(hIC->proxyBypass)
5690 FIXME("Proxy bypass is ignored.\n");
5692 session->hostName = heap_strdupW(lpszServerName);
5693 if (lpszUserName && lpszUserName[0])
5694 session->userName = heap_strdupW(lpszUserName);
5695 if (lpszPassword && lpszPassword[0])
5696 session->password = heap_strdupW(lpszPassword);
5697 session->hostPort = serverPort;
5698 session->connect_timeout = hIC->connect_timeout;
5699 session->send_timeout = INFINITE;
5700 session->receive_timeout = INFINITE;
5702 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5703 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5705 INTERNET_SendCallback(&hIC->hdr, dwContext,
5706 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5711 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5715 TRACE("%p --> %p\n", hIC, session);
5717 *ret = session->hdr.hInternet;
5718 return ERROR_SUCCESS;
5721 /***********************************************************************
5722 * HTTP_clear_response_headers (internal)
5724 * clear out any old response headers
5726 static void HTTP_clear_response_headers( http_request_t *request )
5730 for( i=0; i<request->nCustHeaders; i++)
5732 if( !request->custHeaders[i].lpszField )
5734 if( !request->custHeaders[i].lpszValue )
5736 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5738 HTTP_DeleteCustomHeader( request, i );
5743 /***********************************************************************
5744 * HTTP_GetResponseHeaders (internal)
5746 * Read server response
5753 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5756 WCHAR buffer[MAX_REPLY_LEN];
5757 DWORD buflen = MAX_REPLY_LEN;
5758 BOOL bSuccess = FALSE;
5760 char bufferA[MAX_REPLY_LEN];
5761 LPWSTR status_code = NULL, status_text = NULL;
5762 DWORD cchMaxRawHeaders = 1024;
5763 LPWSTR lpszRawHeaders = NULL;
5765 DWORD cchRawHeaders = 0;
5766 BOOL codeHundred = FALSE;
5770 if(!request->netconn)
5773 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5775 static const WCHAR szHundred[] = {'1','0','0',0};
5777 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5779 buflen = MAX_REPLY_LEN;
5780 if (!read_line(request, bufferA, &buflen))
5783 /* clear old response headers (eg. from a redirect response) */
5785 HTTP_clear_response_headers( request );
5790 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5791 /* check is this a status code line? */
5792 if (!strncmpW(buffer, g_szHttp1_0, 4))
5794 /* split the version from the status code */
5795 status_code = strchrW( buffer, ' ' );
5800 /* split the status code from the status text */
5801 status_text = strchrW( status_code, ' ' );
5806 request->status_code = atoiW(status_code);
5808 TRACE("version [%s] status code [%s] status text [%s]\n",
5809 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5811 codeHundred = (!strcmpW(status_code, szHundred));
5813 else if (!codeHundred)
5815 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5817 heap_free(request->version);
5818 heap_free(request->statusText);
5820 request->status_code = HTTP_STATUS_OK;
5821 request->version = heap_strdupW(g_szHttp1_0);
5822 request->statusText = heap_strdupW(szOK);
5824 heap_free(request->rawHeaders);
5825 request->rawHeaders = heap_strdupW(szDefaultHeader);
5830 } while (codeHundred);
5832 /* Add status code */
5833 HTTP_ProcessHeader(request, szStatus, status_code,
5834 HTTP_ADDHDR_FLAG_REPLACE);
5836 heap_free(request->version);
5837 heap_free(request->statusText);
5839 request->version = heap_strdupW(buffer);
5840 request->statusText = heap_strdupW(status_text);
5842 /* Restore the spaces */
5843 *(status_code-1) = ' ';
5844 *(status_text-1) = ' ';
5846 /* regenerate raw headers */
5847 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5848 if (!lpszRawHeaders) goto lend;
5850 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5851 cchMaxRawHeaders *= 2;
5852 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5853 if (temp == NULL) goto lend;
5854 lpszRawHeaders = temp;
5855 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5856 cchRawHeaders += (buflen-1);
5857 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5858 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5859 lpszRawHeaders[cchRawHeaders] = '\0';
5861 /* Parse each response line */
5864 buflen = MAX_REPLY_LEN;
5865 if (read_line(request, bufferA, &buflen))
5867 LPWSTR * pFieldAndValue;
5869 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5871 if (!bufferA[0]) break;
5872 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5874 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5877 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5878 cchMaxRawHeaders *= 2;
5879 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5880 if (temp == NULL) goto lend;
5881 lpszRawHeaders = temp;
5882 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5883 cchRawHeaders += (buflen-1);
5884 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5885 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5886 lpszRawHeaders[cchRawHeaders] = '\0';
5888 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5889 HTTP_ADDREQ_FLAG_ADD );
5891 HTTP_FreeTokens(pFieldAndValue);
5902 /* make sure the response header is terminated with an empty line. Some apps really
5903 truly care about that empty line being there for some reason. Just add it to the
5905 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5907 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5908 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5909 if (temp == NULL) goto lend;
5910 lpszRawHeaders = temp;
5913 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5915 heap_free(request->rawHeaders);
5916 request->rawHeaders = lpszRawHeaders;
5917 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5927 heap_free(lpszRawHeaders);
5932 /***********************************************************************
5933 * HTTP_InterpretHttpHeader (internal)
5935 * Parse server response
5939 * Pointer to array of field, value, NULL on success.
5942 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5944 LPWSTR * pTokenPair;
5948 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5950 pszColon = strchrW(buffer, ':');
5951 /* must have two tokens */
5954 HTTP_FreeTokens(pTokenPair);
5956 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5960 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5963 HTTP_FreeTokens(pTokenPair);
5966 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5967 pTokenPair[0][pszColon - buffer] = '\0';
5971 len = strlenW(pszColon);
5972 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5975 HTTP_FreeTokens(pTokenPair);
5978 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5980 strip_spaces(pTokenPair[0]);
5981 strip_spaces(pTokenPair[1]);
5983 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5987 /***********************************************************************
5988 * HTTP_ProcessHeader (internal)
5990 * Stuff header into header tables according to <dwModifier>
5994 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5996 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5998 LPHTTPHEADERW lphttpHdr = NULL;
6000 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
6001 DWORD res = ERROR_HTTP_INVALID_HEADER;
6003 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
6005 /* REPLACE wins out over ADD */
6006 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6007 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
6009 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
6012 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
6016 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
6017 return ERROR_HTTP_INVALID_HEADER;
6018 lphttpHdr = &request->custHeaders[index];
6024 hdr.lpszField = (LPWSTR)field;
6025 hdr.lpszValue = (LPWSTR)value;
6026 hdr.wFlags = hdr.wCount = 0;
6028 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6029 hdr.wFlags |= HDR_ISREQUEST;
6031 return HTTP_InsertCustomHeader(request, &hdr);
6033 /* no value to delete */
6034 else return ERROR_SUCCESS;
6036 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6037 lphttpHdr->wFlags |= HDR_ISREQUEST;
6039 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
6041 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6043 HTTP_DeleteCustomHeader( request, index );
6049 hdr.lpszField = (LPWSTR)field;
6050 hdr.lpszValue = (LPWSTR)value;
6051 hdr.wFlags = hdr.wCount = 0;
6053 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6054 hdr.wFlags |= HDR_ISREQUEST;
6056 return HTTP_InsertCustomHeader(request, &hdr);
6059 return ERROR_SUCCESS;
6061 else if (dwModifier & COALESCEFLAGS)
6066 INT origlen = strlenW(lphttpHdr->lpszValue);
6067 INT valuelen = strlenW(value);
6069 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6072 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6074 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6077 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6080 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6082 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6085 lphttpHdr->lpszValue = lpsztmp;
6086 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6089 lphttpHdr->lpszValue[origlen] = ch;
6091 lphttpHdr->lpszValue[origlen] = ' ';
6095 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6096 lphttpHdr->lpszValue[len] = '\0';
6097 res = ERROR_SUCCESS;
6101 WARN("heap_realloc (%d bytes) failed\n",len+1);
6102 res = ERROR_OUTOFMEMORY;
6105 TRACE("<-- %d\n", res);
6109 /***********************************************************************
6110 * HTTP_GetCustomHeaderIndex (internal)
6112 * Return index of custom header from header array
6115 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6116 int requested_index, BOOL request_only)
6120 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6122 for (index = 0; index < request->nCustHeaders; index++)
6124 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6127 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6130 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6133 if (requested_index == 0)
6138 if (index >= request->nCustHeaders)
6141 TRACE("Return: %d\n", index);
6146 /***********************************************************************
6147 * HTTP_InsertCustomHeader (internal)
6149 * Insert header into array
6152 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6155 LPHTTPHEADERW lph = NULL;
6157 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6158 count = request->nCustHeaders + 1;
6160 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6162 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6165 return ERROR_OUTOFMEMORY;
6167 request->custHeaders = lph;
6168 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6169 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6170 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6171 request->custHeaders[count-1].wCount= lpHdr->wCount;
6172 request->nCustHeaders++;
6174 return ERROR_SUCCESS;
6178 /***********************************************************************
6179 * HTTP_DeleteCustomHeader (internal)
6181 * Delete header from array
6182 * If this function is called, the indexs may change.
6184 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6186 if( request->nCustHeaders <= 0 )
6188 if( index >= request->nCustHeaders )
6190 request->nCustHeaders--;
6192 heap_free(request->custHeaders[index].lpszField);
6193 heap_free(request->custHeaders[index].lpszValue);
6195 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6196 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6197 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6203 /***********************************************************************
6204 * HTTP_VerifyValidHeader (internal)
6206 * Verify the given header is not invalid for the given http request
6209 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6211 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6212 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6213 return ERROR_HTTP_INVALID_HEADER;
6215 return ERROR_SUCCESS;
6218 /***********************************************************************
6219 * IsHostInProxyBypassList (@)
6224 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6226 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6230 /***********************************************************************
6231 * InternetShowSecurityInfoByURLA (@)
6233 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6235 FIXME("stub: %s %p\n", url, window);
6239 /***********************************************************************
6240 * InternetShowSecurityInfoByURLW (@)
6242 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6244 FIXME("stub: %s %p\n", debugstr_w(url), window);
6248 /***********************************************************************
6249 * ShowX509EncodedCertificate (@)
6251 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6253 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6259 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6261 memset(&view, 0, sizeof(view));
6262 view.hwndParent = parent;
6263 view.pCertContext = certContext;
6264 if (CryptUIDlgViewCertificateW(&view, NULL))
6265 ret = ERROR_SUCCESS;
6267 ret = GetLastError();
6268 CertFreeCertificateContext(certContext);
6271 ret = GetLastError();