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 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)) {
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 list_init(&server->conn_pool);
274 server->name = heap_strdupW(name);
276 list_add_head(&connection_pool, &server->entry);
284 LeaveCriticalSection(&connection_pool_cs);
289 BOOL collect_connections(collect_type_t collect_type)
291 netconn_t *netconn, *netconn_safe;
292 server_t *server, *server_safe;
293 BOOL remaining = FALSE;
296 now = GetTickCount64();
298 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
299 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
300 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
301 TRACE("freeing %p\n", netconn);
302 list_remove(&netconn->pool_entry);
303 free_netconn(netconn);
309 if(collect_type == COLLECT_CLEANUP) {
310 list_remove(&server->entry);
311 list_init(&server->entry);
312 server_release(server);
319 static DWORD WINAPI collect_connections_proc(void *arg)
321 BOOL remaining_conns;
324 /* FIXME: Use more sophisticated method */
327 EnterCriticalSection(&connection_pool_cs);
329 remaining_conns = collect_connections(COLLECT_TIMEOUT);
331 collector_running = FALSE;
333 LeaveCriticalSection(&connection_pool_cs);
334 }while(remaining_conns);
336 FreeLibraryAndExitThread(WININET_hModule, 0);
339 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
342 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
343 if (HeaderIndex == -1)
346 return &req->custHeaders[HeaderIndex];
355 struct data_stream_vtbl_t {
356 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
357 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
358 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
359 BOOL (*drain_content)(data_stream_t*,http_request_t*);
360 void (*destroy)(data_stream_t*);
364 data_stream_t data_stream;
366 BYTE buf[READ_BUFFER_SIZE];
372 static inline void destroy_data_stream(data_stream_t *stream)
374 stream->vtbl->destroy(stream);
377 static void reset_data_stream(http_request_t *req)
379 destroy_data_stream(req->data_stream);
380 req->data_stream = &req->netconn_stream.data_stream;
381 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
382 req->read_chunked = req->read_gzip = FALSE;
388 data_stream_t stream;
389 data_stream_t *parent_stream;
391 BYTE buf[READ_BUFFER_SIZE];
397 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
399 /* Allow reading only from read buffer */
403 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
405 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
406 return gzip_stream->end_of_data;
409 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
410 DWORD *read, read_mode_t read_mode)
412 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
413 z_stream *zstream = &gzip_stream->zstream;
414 DWORD current_read, ret_read = 0;
417 DWORD res = ERROR_SUCCESS;
419 while(size && !gzip_stream->end_of_data) {
420 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
422 if(gzip_stream->buf_size <= 64 && !end) {
423 if(gzip_stream->buf_pos) {
424 if(gzip_stream->buf_size)
425 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
426 gzip_stream->buf_pos = 0;
428 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
429 sizeof(gzip_stream->buf)-gzip_stream->buf_size, ¤t_read, read_mode);
430 gzip_stream->buf_size += current_read;
431 if(res != ERROR_SUCCESS)
433 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
434 if(!current_read && !end) {
435 if(read_mode != READMODE_NOBLOCK) {
436 WARN("unexpected end of data\n");
437 gzip_stream->end_of_data = TRUE;
441 if(gzip_stream->buf_size <= 64 && !end)
445 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
446 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
447 zstream->next_out = buf+ret_read;
448 zstream->avail_out = size;
449 zres = inflate(&gzip_stream->zstream, 0);
450 current_read = size - zstream->avail_out;
451 size -= current_read;
452 ret_read += current_read;
453 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
454 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
455 if(zres == Z_STREAM_END) {
456 TRACE("end of data\n");
457 gzip_stream->end_of_data = TRUE;
459 }else if(zres != Z_OK) {
460 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
462 res = ERROR_INTERNET_DECODING_FAILED;
466 if(ret_read && read_mode == READMODE_ASYNC)
467 read_mode = READMODE_NOBLOCK;
470 TRACE("read %u bytes\n", ret_read);
475 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
477 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
478 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
481 static void gzip_destroy(data_stream_t *stream)
483 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
485 destroy_data_stream(gzip_stream->parent_stream);
487 if(!gzip_stream->end_of_data)
488 inflateEnd(&gzip_stream->zstream);
489 heap_free(gzip_stream);
492 static const data_stream_vtbl_t gzip_stream_vtbl = {
500 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
502 return heap_alloc(items*size);
505 static void wininet_zfree(voidpf opaque, voidpf address)
510 static DWORD init_gzip_stream(http_request_t *req)
512 gzip_stream_t *gzip_stream;
515 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
517 return ERROR_OUTOFMEMORY;
519 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
520 gzip_stream->zstream.zalloc = wininet_zalloc;
521 gzip_stream->zstream.zfree = wininet_zfree;
523 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
525 ERR("inflateInit failed: %d\n", zres);
526 heap_free(gzip_stream);
527 return ERROR_OUTOFMEMORY;
530 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
532 HTTP_DeleteCustomHeader(req, index);
535 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
536 gzip_stream->buf_size = req->read_size;
537 req->read_pos = req->read_size = 0;
540 req->read_gzip = TRUE;
541 gzip_stream->parent_stream = req->data_stream;
542 req->data_stream = &gzip_stream->stream;
543 return ERROR_SUCCESS;
548 static DWORD init_gzip_stream(http_request_t *req)
550 ERR("gzip stream not supported, missing zlib.\n");
551 return ERROR_SUCCESS;
556 /***********************************************************************
557 * HTTP_Tokenize (internal)
559 * Tokenize a string, allocating memory for the tokens.
561 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
563 LPWSTR * token_array;
570 /* empty string has no tokens */
574 for (i = 0; string[i]; i++)
576 if (!strncmpW(string+i, token_string, strlenW(token_string)))
580 /* we want to skip over separators, but not the null terminator */
581 for (j = 0; j < strlenW(token_string) - 1; j++)
589 /* add 1 for terminating NULL */
590 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
591 token_array[tokens] = NULL;
594 for (i = 0; i < tokens; i++)
597 next_token = strstrW(string, token_string);
598 if (!next_token) next_token = string+strlenW(string);
599 len = next_token - string;
600 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
601 memcpy(token_array[i], string, len*sizeof(WCHAR));
602 token_array[i][len] = '\0';
603 string = next_token+strlenW(token_string);
608 /***********************************************************************
609 * HTTP_FreeTokens (internal)
611 * Frees memory returned from HTTP_Tokenize.
613 static void HTTP_FreeTokens(LPWSTR * token_array)
616 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
617 heap_free(token_array);
620 static void HTTP_FixURL(http_request_t *request)
622 static const WCHAR szSlash[] = { '/',0 };
623 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
625 /* If we don't have a path we set it to root */
626 if (NULL == request->path)
627 request->path = heap_strdupW(szSlash);
628 else /* remove \r and \n*/
630 int nLen = strlenW(request->path);
631 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
634 request->path[nLen]='\0';
636 /* Replace '\' with '/' */
639 if (request->path[nLen] == '\\') request->path[nLen]='/';
643 if(CSTR_EQUAL != CompareStringW( LOCALE_INVARIANT, NORM_IGNORECASE,
644 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
645 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
647 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
649 strcpyW(fixurl + 1, request->path);
650 heap_free( request->path );
651 request->path = fixurl;
655 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
657 LPWSTR requestString;
663 static const WCHAR szSpace[] = { ' ',0 };
664 static const WCHAR szColon[] = { ':',' ',0 };
665 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
667 /* allocate space for an array of all the string pointers to be added */
668 len = (request->nCustHeaders)*4 + 10;
669 req = heap_alloc(len*sizeof(LPCWSTR));
671 /* add the verb, path and HTTP version string */
679 /* Append custom request headers */
680 for (i = 0; i < request->nCustHeaders; i++)
682 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
685 req[n++] = request->custHeaders[i].lpszField;
687 req[n++] = request->custHeaders[i].lpszValue;
689 TRACE("Adding custom header %s (%s)\n",
690 debugstr_w(request->custHeaders[i].lpszField),
691 debugstr_w(request->custHeaders[i].lpszValue));
696 ERR("oops. buffer overrun\n");
699 requestString = HTTP_build_req( req, 4 );
703 * Set (header) termination string for request
704 * Make sure there's exactly two new lines at the end of the request
706 p = &requestString[strlenW(requestString)-1];
707 while ( (*p == '\n') || (*p == '\r') )
709 strcpyW( p+1, sztwocrlf );
711 return requestString;
714 static void HTTP_ProcessCookies( http_request_t *request )
718 LPHTTPHEADERW setCookieHeader;
720 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
723 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
729 setCookieHeader = &request->custHeaders[HeaderIndex];
731 if (!setCookieHeader->lpszValue)
734 host = HTTP_GetHeader(request, hostW);
738 data = strchrW(setCookieHeader->lpszValue, '=');
742 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
747 set_cookie(host->lpszValue, request->path, name, data);
752 static void strip_spaces(LPWSTR start)
757 while (*str == ' ' && *str != '\0')
761 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
763 end = start + strlenW(start) - 1;
764 while (end >= start && *end == ' ')
771 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
773 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
774 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
776 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
777 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
778 if (is_basic && pszRealm)
781 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
785 token = strchrW(ptr,'=');
789 while (*realm == ' ' && *realm != '\0')
791 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
792 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
795 while (*token == ' ' && *token != '\0')
799 *pszRealm = heap_strdupW(token);
800 strip_spaces(*pszRealm);
807 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
809 if (!authinfo) return;
811 if (SecIsValidHandle(&authinfo->ctx))
812 DeleteSecurityContext(&authinfo->ctx);
813 if (SecIsValidHandle(&authinfo->cred))
814 FreeCredentialsHandle(&authinfo->cred);
816 heap_free(authinfo->auth_data);
817 heap_free(authinfo->scheme);
821 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
823 basicAuthorizationData *ad;
826 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
828 EnterCriticalSection(&authcache_cs);
829 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
831 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
833 TRACE("Authorization found in cache\n");
834 *auth_data = heap_alloc(ad->authorizationLen);
835 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
836 rc = ad->authorizationLen;
840 LeaveCriticalSection(&authcache_cs);
844 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
847 basicAuthorizationData* ad = NULL;
849 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
851 EnterCriticalSection(&authcache_cs);
852 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
854 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
855 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
864 TRACE("Found match in cache, replacing\n");
865 heap_free(ad->authorization);
866 ad->authorization = heap_alloc(auth_data_len);
867 memcpy(ad->authorization, auth_data, auth_data_len);
868 ad->authorizationLen = auth_data_len;
872 ad = heap_alloc(sizeof(basicAuthorizationData));
873 ad->host = heap_strdupW(host);
874 ad->realm = heap_strdupW(realm);
875 ad->authorization = heap_alloc(auth_data_len);
876 memcpy(ad->authorization, auth_data, auth_data_len);
877 ad->authorizationLen = auth_data_len;
878 list_add_head(&basicAuthorizationCache,&ad->entry);
879 TRACE("authorization cached\n");
881 LeaveCriticalSection(&authcache_cs);
884 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
885 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
887 authorizationData *ad;
889 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
891 EnterCriticalSection(&authcache_cs);
892 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
893 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
894 TRACE("Authorization found in cache\n");
896 nt_auth_identity->User = heap_strdupW(ad->user);
897 nt_auth_identity->Password = heap_strdupW(ad->password);
898 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
899 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
900 (!nt_auth_identity->Domain && ad->domain_len)) {
901 heap_free(nt_auth_identity->User);
902 heap_free(nt_auth_identity->Password);
903 heap_free(nt_auth_identity->Domain);
907 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
908 nt_auth_identity->UserLength = ad->user_len;
909 nt_auth_identity->PasswordLength = ad->password_len;
910 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
911 nt_auth_identity->DomainLength = ad->domain_len;
912 LeaveCriticalSection(&authcache_cs);
916 LeaveCriticalSection(&authcache_cs);
921 static void cache_authorization(LPWSTR host, LPWSTR scheme,
922 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
924 authorizationData *ad;
927 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
929 EnterCriticalSection(&authcache_cs);
930 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
931 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
938 heap_free(ad->password);
939 heap_free(ad->domain);
941 ad = heap_alloc(sizeof(authorizationData));
943 LeaveCriticalSection(&authcache_cs);
947 ad->host = heap_strdupW(host);
948 ad->scheme = heap_strdupW(scheme);
949 list_add_head(&authorizationCache, &ad->entry);
952 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
953 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
954 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
955 ad->user_len = nt_auth_identity->UserLength;
956 ad->password_len = nt_auth_identity->PasswordLength;
957 ad->domain_len = nt_auth_identity->DomainLength;
959 if(!ad->host || !ad->scheme || !ad->user || !ad->password
960 || (nt_auth_identity->Domain && !ad->domain)) {
962 heap_free(ad->scheme);
964 heap_free(ad->password);
965 heap_free(ad->domain);
966 list_remove(&ad->entry);
970 LeaveCriticalSection(&authcache_cs);
973 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
974 struct HttpAuthInfo **ppAuthInfo,
975 LPWSTR domain_and_username, LPWSTR password,
978 SECURITY_STATUS sec_status;
979 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
981 LPWSTR szRealm = NULL;
983 TRACE("%s\n", debugstr_w(pszAuthValue));
990 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
994 SecInvalidateHandle(&pAuthInfo->cred);
995 SecInvalidateHandle(&pAuthInfo->ctx);
996 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
998 pAuthInfo->auth_data = NULL;
999 pAuthInfo->auth_data_len = 0;
1000 pAuthInfo->finished = FALSE;
1002 if (is_basic_auth_value(pszAuthValue,NULL))
1004 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1005 pAuthInfo->scheme = heap_strdupW(szBasic);
1006 if (!pAuthInfo->scheme)
1008 heap_free(pAuthInfo);
1015 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1017 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1018 if (!pAuthInfo->scheme)
1020 heap_free(pAuthInfo);
1024 if (domain_and_username)
1026 WCHAR *user = strchrW(domain_and_username, '\\');
1027 WCHAR *domain = domain_and_username;
1029 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1031 pAuthData = &nt_auth_identity;
1036 user = domain_and_username;
1040 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1041 nt_auth_identity.User = user;
1042 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1043 nt_auth_identity.Domain = domain;
1044 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1045 nt_auth_identity.Password = password;
1046 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1048 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1050 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1051 pAuthData = &nt_auth_identity;
1053 /* use default credentials */
1056 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1057 SECPKG_CRED_OUTBOUND, NULL,
1059 NULL, &pAuthInfo->cred,
1062 if(pAuthData && !domain_and_username) {
1063 heap_free(nt_auth_identity.User);
1064 heap_free(nt_auth_identity.Domain);
1065 heap_free(nt_auth_identity.Password);
1068 if (sec_status == SEC_E_OK)
1070 PSecPkgInfoW sec_pkg_info;
1071 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1072 if (sec_status == SEC_E_OK)
1074 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1075 FreeContextBuffer(sec_pkg_info);
1078 if (sec_status != SEC_E_OK)
1080 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1081 debugstr_w(pAuthInfo->scheme), sec_status);
1082 heap_free(pAuthInfo->scheme);
1083 heap_free(pAuthInfo);
1087 *ppAuthInfo = pAuthInfo;
1089 else if (pAuthInfo->finished)
1092 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1093 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1095 ERR("authentication scheme changed from %s to %s\n",
1096 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1100 if (is_basic_auth_value(pszAuthValue,&szRealm))
1104 char *auth_data = NULL;
1105 UINT auth_data_len = 0;
1107 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1109 if (!domain_and_username)
1111 if (host && szRealm)
1112 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1113 if (auth_data_len == 0)
1121 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1122 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1124 /* length includes a nul terminator, which will be re-used for the ':' */
1125 auth_data = heap_alloc(userlen + 1 + passlen);
1132 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1133 auth_data[userlen] = ':';
1134 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1135 auth_data_len = userlen + 1 + passlen;
1136 if (host && szRealm)
1137 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1140 pAuthInfo->auth_data = auth_data;
1141 pAuthInfo->auth_data_len = auth_data_len;
1142 pAuthInfo->finished = TRUE;
1148 LPCWSTR pszAuthData;
1149 SecBufferDesc out_desc, in_desc;
1151 unsigned char *buffer;
1152 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1153 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1155 in.BufferType = SECBUFFER_TOKEN;
1159 in_desc.ulVersion = 0;
1160 in_desc.cBuffers = 1;
1161 in_desc.pBuffers = ∈
1163 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1164 if (*pszAuthData == ' ')
1167 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1168 in.pvBuffer = heap_alloc(in.cbBuffer);
1169 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1172 buffer = heap_alloc(pAuthInfo->max_token);
1174 out.BufferType = SECBUFFER_TOKEN;
1175 out.cbBuffer = pAuthInfo->max_token;
1176 out.pvBuffer = buffer;
1178 out_desc.ulVersion = 0;
1179 out_desc.cBuffers = 1;
1180 out_desc.pBuffers = &out;
1182 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1183 first ? NULL : &pAuthInfo->ctx,
1184 first ? request->server->name : NULL,
1185 context_req, 0, SECURITY_NETWORK_DREP,
1186 in.pvBuffer ? &in_desc : NULL,
1187 0, &pAuthInfo->ctx, &out_desc,
1188 &pAuthInfo->attr, &pAuthInfo->exp);
1189 if (sec_status == SEC_E_OK)
1191 pAuthInfo->finished = TRUE;
1192 pAuthInfo->auth_data = out.pvBuffer;
1193 pAuthInfo->auth_data_len = out.cbBuffer;
1194 TRACE("sending last auth packet\n");
1196 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1198 pAuthInfo->auth_data = out.pvBuffer;
1199 pAuthInfo->auth_data_len = out.cbBuffer;
1200 TRACE("sending next auth packet\n");
1204 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1205 heap_free(out.pvBuffer);
1206 destroy_authinfo(pAuthInfo);
1215 /***********************************************************************
1216 * HTTP_HttpAddRequestHeadersW (internal)
1218 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1219 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1224 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1226 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1228 if( dwHeaderLength == ~0U )
1229 len = strlenW(lpszHeader);
1231 len = dwHeaderLength;
1232 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1233 lstrcpynW( buffer, lpszHeader, len + 1);
1239 LPWSTR * pFieldAndValue;
1241 lpszEnd = lpszStart;
1243 while (*lpszEnd != '\0')
1245 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1250 if (*lpszStart == '\0')
1253 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1256 lpszEnd++; /* Jump over newline */
1258 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1259 if (*lpszStart == '\0')
1261 /* Skip 0-length headers */
1262 lpszStart = lpszEnd;
1263 res = ERROR_SUCCESS;
1266 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1269 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1270 if (res == ERROR_SUCCESS)
1271 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1272 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1273 HTTP_FreeTokens(pFieldAndValue);
1276 lpszStart = lpszEnd;
1277 } while (res == ERROR_SUCCESS);
1283 /***********************************************************************
1284 * HttpAddRequestHeadersW (WININET.@)
1286 * Adds one or more HTTP header to the request handler
1289 * On Windows if dwHeaderLength includes the trailing '\0', then
1290 * HttpAddRequestHeadersW() adds it too. However this results in an
1291 * invalid HTTP header which is rejected by some servers so we probably
1292 * don't need to match Windows on that point.
1299 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1300 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1302 http_request_t *request;
1303 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1305 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1310 request = (http_request_t*) get_handle_object( hHttpRequest );
1311 if (request && request->hdr.htype == WH_HHTTPREQ)
1312 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1314 WININET_Release( &request->hdr );
1316 if(res != ERROR_SUCCESS)
1318 return res == ERROR_SUCCESS;
1321 /***********************************************************************
1322 * HttpAddRequestHeadersA (WININET.@)
1324 * Adds one or more HTTP header to the request handler
1331 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1332 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1338 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1340 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1341 hdr = heap_alloc(len*sizeof(WCHAR));
1342 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1343 if( dwHeaderLength != ~0U )
1344 dwHeaderLength = len;
1346 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1352 static void free_accept_types( WCHAR **accept_types )
1354 WCHAR *ptr, **types = accept_types;
1357 while ((ptr = *types))
1362 heap_free( accept_types );
1365 static WCHAR **convert_accept_types( const char **accept_types )
1368 const char **types = accept_types;
1370 BOOL invalid_pointer = FALSE;
1372 if (!types) return NULL;
1378 /* find out how many there are */
1379 if (*types && **types)
1381 TRACE("accept type: %s\n", debugstr_a(*types));
1387 WARN("invalid accept type pointer\n");
1388 invalid_pointer = TRUE;
1393 if (invalid_pointer) return NULL;
1394 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1396 types = accept_types;
1399 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1402 typesW[count] = NULL;
1406 /***********************************************************************
1407 * HttpOpenRequestA (WININET.@)
1409 * Open a HTTP request handle
1412 * HINTERNET a HTTP request handle on success
1416 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1417 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1418 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1419 DWORD dwFlags, DWORD_PTR dwContext)
1421 LPWSTR szVerb = NULL, szObjectName = NULL;
1422 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1423 HINTERNET rc = FALSE;
1425 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1426 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1427 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1428 dwFlags, dwContext);
1432 szVerb = heap_strdupAtoW(lpszVerb);
1439 szObjectName = heap_strdupAtoW(lpszObjectName);
1440 if ( !szObjectName )
1446 szVersion = heap_strdupAtoW(lpszVersion);
1453 szReferrer = heap_strdupAtoW(lpszReferrer);
1458 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1459 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1460 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1463 free_accept_types(szAcceptTypes);
1464 heap_free(szReferrer);
1465 heap_free(szVersion);
1466 heap_free(szObjectName);
1471 /***********************************************************************
1474 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1477 static const CHAR HTTP_Base64Enc[] =
1478 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1482 /* first 6 bits, all from bin[0] */
1483 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1484 x = (bin[0] & 3) << 4;
1486 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1489 base64[n++] = HTTP_Base64Enc[x];
1494 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1495 x = ( bin[1] & 0x0f ) << 2;
1497 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1500 base64[n++] = HTTP_Base64Enc[x];
1504 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1506 /* last 6 bits, all from bin [2] */
1507 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1515 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1516 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1517 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1518 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1519 static const signed char HTTP_Base64Dec[256] =
1521 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1522 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1523 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1524 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1525 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1526 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1527 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1528 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1529 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1530 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1531 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1532 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1533 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1534 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1535 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1536 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1537 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1538 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1539 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1540 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1541 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1542 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1543 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1544 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1545 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1546 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1550 /***********************************************************************
1553 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1561 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1562 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1563 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1564 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1566 WARN("invalid base64: %s\n", debugstr_w(base64));
1570 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1573 if ((base64[2] == '=') && (base64[3] == '='))
1575 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1576 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1578 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1582 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1585 if (base64[3] == '=')
1587 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1588 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1590 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1594 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1603 /***********************************************************************
1604 * HTTP_InsertAuthorization
1606 * Insert or delete the authorization field in the request header.
1608 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1612 static const WCHAR wszSpace[] = {' ',0};
1613 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1615 WCHAR *authorization = NULL;
1617 if (pAuthInfo->auth_data_len)
1619 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1620 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1621 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1625 strcpyW(authorization, pAuthInfo->scheme);
1626 strcatW(authorization, wszSpace);
1627 HTTP_EncodeBase64(pAuthInfo->auth_data,
1628 pAuthInfo->auth_data_len,
1629 authorization+strlenW(authorization));
1631 /* clear the data as it isn't valid now that it has been sent to the
1632 * server, unless it's Basic authentication which doesn't do
1633 * connection tracking */
1634 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1636 heap_free(pAuthInfo->auth_data);
1637 pAuthInfo->auth_data = NULL;
1638 pAuthInfo->auth_data_len = 0;
1642 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1644 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1645 heap_free(authorization);
1650 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1652 static const WCHAR slash[] = { '/',0 };
1653 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1654 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1655 http_session_t *session = req->session;
1656 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1659 size = sizeof(new_location);
1660 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1662 URL_COMPONENTSW UrlComponents;
1664 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1665 strcpyW( url, new_location );
1667 ZeroMemory(&UrlComponents,sizeof(URL_COMPONENTSW));
1668 if(InternetCrackUrlW(url, 0, 0, &UrlComponents)) goto done;
1672 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1673 size += strlenW( session->hostName ) + strlenW( req->path );
1675 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1677 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1678 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1680 sprintfW( url, format, session->hostName, session->hostPort );
1681 if (req->path[0] != '/') strcatW( url, slash );
1682 strcatW( url, req->path );
1685 TRACE("url=%s\n", debugstr_w(url));
1689 /***********************************************************************
1690 * HTTP_DealWithProxy
1692 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1694 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1695 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1696 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1697 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1698 static WCHAR szNul[] = { 0 };
1699 URL_COMPONENTSW UrlComponents;
1700 server_t *new_server;
1701 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1702 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1703 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1705 memset( &UrlComponents, 0, sizeof UrlComponents );
1706 UrlComponents.dwStructSize = sizeof UrlComponents;
1707 UrlComponents.lpszHostName = buf;
1708 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1710 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1712 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1713 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1714 sprintfW(proxy, szFormat, protoProxy);
1716 strcpyW(proxy, protoProxy);
1717 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1719 if( UrlComponents.dwHostNameLength == 0 )
1722 if( !request->path )
1723 request->path = szNul;
1725 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1726 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1728 new_server = get_server(UrlComponents.lpszHostName, UrlComponents.nPort, TRUE);
1732 server_release(request->server);
1733 request->server = 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->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 heap_free(request->path);
1834 heap_free(request->verb);
1835 heap_free(request->rawHeaders);
1836 heap_free(request->version);
1837 heap_free(request->statusText);
1839 for (i = 0; i < request->nCustHeaders; i++)
1841 heap_free(request->custHeaders[i].lpszField);
1842 heap_free(request->custHeaders[i].lpszValue);
1844 destroy_data_stream(request->data_stream);
1845 heap_free(request->custHeaders);
1848 static void http_release_netconn(http_request_t *req, BOOL reuse)
1850 TRACE("%p %p\n",req, req->netconn);
1855 if(reuse && req->netconn->keep_alive) {
1858 EnterCriticalSection(&connection_pool_cs);
1860 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1861 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1862 req->netconn = NULL;
1864 run_collector = !collector_running;
1865 collector_running = TRUE;
1867 LeaveCriticalSection(&connection_pool_cs);
1870 HANDLE thread = NULL;
1873 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1875 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1877 EnterCriticalSection(&connection_pool_cs);
1878 collector_running = FALSE;
1879 LeaveCriticalSection(&connection_pool_cs);
1882 FreeLibrary(module);
1885 CloseHandle(thread);
1890 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1891 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1893 free_netconn(req->netconn);
1894 req->netconn = NULL;
1896 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1897 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1900 static BOOL HTTP_KeepAlive(http_request_t *request)
1902 WCHAR szVersion[10];
1903 WCHAR szConnectionResponse[20];
1904 DWORD dwBufferSize = sizeof(szVersion);
1905 BOOL keepalive = FALSE;
1907 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1908 * the connection is keep-alive by default */
1909 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1910 && !strcmpiW(szVersion, g_szHttp1_1))
1915 dwBufferSize = sizeof(szConnectionResponse);
1916 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1917 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1919 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1925 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1927 http_request_t *req = (http_request_t*)hdr;
1929 http_release_netconn(req, drain_content(req, FALSE));
1932 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1934 http_request_t *req = (http_request_t*)hdr;
1937 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1939 http_session_t *session = req->session;
1940 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1942 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1944 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1945 return ERROR_INSUFFICIENT_BUFFER;
1946 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1947 /* FIXME: can't get a SOCKET from our connection since we don't use
1951 /* FIXME: get source port from req->netConnection */
1952 info->SourcePort = 0;
1953 info->DestPort = session->hostPort;
1955 if (HTTP_KeepAlive(req))
1956 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1957 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1958 info->Flags |= IDSI_FLAG_PROXY;
1959 if (req->netconn->useSSL)
1960 info->Flags |= IDSI_FLAG_SECURE;
1962 return ERROR_SUCCESS;
1966 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1968 case INTERNET_OPTION_SECURITY_FLAGS:
1972 if (*size < sizeof(ULONG))
1973 return ERROR_INSUFFICIENT_BUFFER;
1975 *size = sizeof(DWORD);
1976 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
1977 *(DWORD *)buffer = flags;
1979 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
1980 return ERROR_SUCCESS;
1983 case INTERNET_OPTION_HANDLE_TYPE:
1984 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1986 if (*size < sizeof(ULONG))
1987 return ERROR_INSUFFICIENT_BUFFER;
1989 *size = sizeof(DWORD);
1990 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1991 return ERROR_SUCCESS;
1993 case INTERNET_OPTION_URL: {
1994 WCHAR url[INTERNET_MAX_URL_LENGTH];
1999 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2001 TRACE("INTERNET_OPTION_URL\n");
2003 host = HTTP_GetHeader(req, hostW);
2004 strcpyW(url, httpW);
2005 strcatW(url, host->lpszValue);
2006 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2008 strcatW(url, req->path);
2010 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2013 len = (strlenW(url)+1) * sizeof(WCHAR);
2015 return ERROR_INSUFFICIENT_BUFFER;
2018 strcpyW(buffer, url);
2019 return ERROR_SUCCESS;
2021 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2023 return ERROR_INSUFFICIENT_BUFFER;
2026 return ERROR_SUCCESS;
2030 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2031 INTERNET_CACHE_ENTRY_INFOW *info;
2032 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2033 WCHAR url[INTERNET_MAX_URL_LENGTH];
2034 DWORD nbytes, error;
2037 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2039 if (*size < sizeof(*ts))
2041 *size = sizeof(*ts);
2042 return ERROR_INSUFFICIENT_BUFFER;
2045 HTTP_GetRequestURL(req, url);
2046 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2047 error = GetLastError();
2048 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2050 if (!(info = heap_alloc(nbytes)))
2051 return ERROR_OUTOFMEMORY;
2053 GetUrlCacheEntryInfoW(url, info, &nbytes);
2055 ts->ftExpires = info->ExpireTime;
2056 ts->ftLastModified = info->LastModifiedTime;
2059 *size = sizeof(*ts);
2060 return ERROR_SUCCESS;
2065 case INTERNET_OPTION_DATAFILE_NAME: {
2068 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2070 if(!req->cacheFile) {
2072 return ERROR_INTERNET_ITEM_NOT_FOUND;
2076 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2077 if(*size < req_size)
2078 return ERROR_INSUFFICIENT_BUFFER;
2081 memcpy(buffer, req->cacheFile, *size);
2082 return ERROR_SUCCESS;
2084 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2085 if (req_size > *size)
2086 return ERROR_INSUFFICIENT_BUFFER;
2088 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2089 -1, buffer, *size, NULL, NULL);
2090 return ERROR_SUCCESS;
2094 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2095 PCCERT_CONTEXT context;
2097 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2098 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2099 return ERROR_INSUFFICIENT_BUFFER;
2102 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2104 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2107 memset(info, 0, sizeof(*info));
2108 info->ftExpiry = context->pCertInfo->NotAfter;
2109 info->ftStart = context->pCertInfo->NotBefore;
2110 len = CertNameToStrA(context->dwCertEncodingType,
2111 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2112 info->lpszSubjectInfo = LocalAlloc(0, len);
2113 if(info->lpszSubjectInfo)
2114 CertNameToStrA(context->dwCertEncodingType,
2115 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2116 info->lpszSubjectInfo, len);
2117 len = CertNameToStrA(context->dwCertEncodingType,
2118 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2119 info->lpszIssuerInfo = LocalAlloc(0, len);
2120 if(info->lpszIssuerInfo)
2121 CertNameToStrA(context->dwCertEncodingType,
2122 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2123 info->lpszIssuerInfo, len);
2124 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2125 CertFreeCertificateContext(context);
2126 return ERROR_SUCCESS;
2128 return ERROR_NOT_SUPPORTED;
2130 case INTERNET_OPTION_CONNECT_TIMEOUT:
2131 if (*size < sizeof(DWORD))
2132 return ERROR_INSUFFICIENT_BUFFER;
2134 *size = sizeof(DWORD);
2135 *(DWORD *)buffer = req->connect_timeout;
2136 return ERROR_SUCCESS;
2137 case INTERNET_OPTION_REQUEST_FLAGS: {
2140 if (*size < sizeof(DWORD))
2141 return ERROR_INSUFFICIENT_BUFFER;
2143 /* FIXME: Add support for:
2144 * INTERNET_REQFLAG_FROM_CACHE
2145 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2148 if(req->session->appInfo->proxy)
2149 flags |= INTERNET_REQFLAG_VIA_PROXY;
2150 if(!req->rawHeaders)
2151 flags |= INTERNET_REQFLAG_NO_HEADERS;
2153 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2155 *size = sizeof(DWORD);
2156 *(DWORD*)buffer = flags;
2157 return ERROR_SUCCESS;
2161 return INET_QueryOption(hdr, option, buffer, size, unicode);
2164 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2166 http_request_t *req = (http_request_t*)hdr;
2169 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2170 TRACE("Undocumented option 99\n");
2172 if (!buffer || size != sizeof(DWORD))
2173 return ERROR_INVALID_PARAMETER;
2174 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2175 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2178 case INTERNET_OPTION_SECURITY_FLAGS:
2182 if (!buffer || size != sizeof(DWORD))
2183 return ERROR_INVALID_PARAMETER;
2184 flags = *(DWORD *)buffer;
2185 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2186 flags &= SECURITY_SET_MASK;
2187 req->security_flags |= flags;
2189 req->netconn->security_flags |= flags;
2190 return ERROR_SUCCESS;
2192 case INTERNET_OPTION_CONNECT_TIMEOUT:
2193 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2194 req->connect_timeout = *(DWORD *)buffer;
2195 return ERROR_SUCCESS;
2197 case INTERNET_OPTION_SEND_TIMEOUT:
2198 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2199 req->send_timeout = *(DWORD *)buffer;
2200 return ERROR_SUCCESS;
2202 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2203 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2204 req->receive_timeout = *(DWORD *)buffer;
2205 return ERROR_SUCCESS;
2207 case INTERNET_OPTION_USERNAME:
2208 heap_free(req->session->userName);
2209 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2210 return ERROR_SUCCESS;
2212 case INTERNET_OPTION_PASSWORD:
2213 heap_free(req->session->password);
2214 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2215 return ERROR_SUCCESS;
2216 case INTERNET_OPTION_HTTP_DECODING:
2217 if(size != sizeof(BOOL))
2218 return ERROR_INVALID_PARAMETER;
2219 req->decoding = *(BOOL*)buffer;
2220 return ERROR_SUCCESS;
2223 return INET_SetOption(hdr, option, buffer, size);
2226 static void commit_cache_entry(http_request_t *req)
2228 WCHAR url[INTERNET_MAX_URL_LENGTH];
2232 CloseHandle(req->hCacheFile);
2233 req->hCacheFile = NULL;
2235 if(HTTP_GetRequestURL(req, url)) {
2238 headersLen = req->rawHeaders ? strlenW(req->rawHeaders) : 0;
2239 CommitUrlCacheEntryW(url, req->cacheFile, req->expires,
2240 req->last_modified, NORMAL_CACHE_ENTRY,
2241 req->rawHeaders, headersLen, NULL, 0);
2245 static void create_cache_entry(http_request_t *req)
2247 WCHAR url[INTERNET_MAX_URL_LENGTH];
2248 WCHAR file_name[MAX_PATH+1];
2251 /* FIXME: We should free previous cache file earlier */
2252 heap_free(req->cacheFile);
2253 CloseHandle(req->hCacheFile);
2254 req->hCacheFile = NULL;
2256 b = HTTP_GetRequestURL(req, url);
2258 WARN("Could not get URL\n");
2262 b = CreateUrlCacheEntryW(url, req->contentLength, NULL, file_name, 0);
2264 WARN("Could not create cache entry: %08x\n", GetLastError());
2268 req->cacheFile = heap_strdupW(file_name);
2269 req->hCacheFile = CreateFileW(req->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2270 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2271 if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2272 WARN("Could not create file: %u\n", GetLastError());
2273 req->hCacheFile = NULL;
2277 if(req->read_size) {
2280 b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2282 FIXME("WriteFile failed: %u\n", GetLastError());
2284 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2285 commit_cache_entry(req);
2289 /* read some more data into the read buffer (the read section must be held) */
2290 static DWORD read_more_data( http_request_t *req, int maxlen )
2297 /* move existing data to the start of the buffer */
2299 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2303 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2305 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2306 maxlen - req->read_size, 0, &len );
2307 if(res == ERROR_SUCCESS)
2308 req->read_size += len;
2313 /* remove some amount of data from the read buffer (the read section must be held) */
2314 static void remove_data( http_request_t *req, int count )
2316 if (!(req->read_size -= count)) req->read_pos = 0;
2317 else req->read_pos += count;
2320 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2322 int count, bytes_read, pos = 0;
2325 EnterCriticalSection( &req->read_section );
2328 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2332 count = eol - (req->read_buf + req->read_pos);
2333 bytes_read = count + 1;
2335 else count = bytes_read = req->read_size;
2337 count = min( count, *len - pos );
2338 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2340 remove_data( req, bytes_read );
2343 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2346 TRACE( "returning empty string %u\n", res);
2347 LeaveCriticalSection( &req->read_section );
2348 INTERNET_SetLastError(res);
2352 LeaveCriticalSection( &req->read_section );
2356 if (pos && buffer[pos - 1] == '\r') pos--;
2359 buffer[*len - 1] = 0;
2360 TRACE( "returning %s\n", debugstr_a(buffer));
2364 /* check if we have reached the end of the data to read (the read section must be held) */
2365 static BOOL end_of_read_data( http_request_t *req )
2367 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2370 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
2374 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
2375 assert(*read <= size);
2377 if(req->hCacheFile) {
2382 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2384 FIXME("WriteFile failed: %u\n", GetLastError());
2387 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2388 commit_cache_entry(req);
2394 /* fetch some more data into the read buffer (the read section must be held) */
2395 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2399 if(req->read_size == sizeof(req->read_buf))
2400 return ERROR_SUCCESS;
2404 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2408 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2410 req->read_size += read;
2412 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2418 /* return the size of data available to be read immediately (the read section must be held) */
2419 static DWORD get_avail_data( http_request_t *req )
2421 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2424 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2426 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2430 NETCON_query_data_available(req->netconn, &avail);
2431 return netconn_stream->content_length == ~0u
2433 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2436 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2438 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2439 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2442 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2443 DWORD *read, read_mode_t read_mode)
2445 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2448 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2450 if(read_mode == READMODE_NOBLOCK) {
2451 DWORD avail = netconn_get_avail_data(stream, req);
2456 if(size && req->netconn) {
2457 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2460 netconn_stream->content_length = netconn_stream->content_read;
2463 netconn_stream->content_read += *read = len;
2464 TRACE("read %u bytes\n", len);
2465 return ERROR_SUCCESS;
2468 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2470 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2475 if(netconn_end_of_data(stream, req))
2479 avail = netconn_get_avail_data(stream, req);
2483 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2486 netconn_stream->content_read += len;
2487 }while(netconn_stream->content_read < netconn_stream->content_length);
2492 static void netconn_destroy(data_stream_t *stream)
2496 static const data_stream_vtbl_t netconn_stream_vtbl = {
2497 netconn_get_avail_data,
2498 netconn_end_of_data,
2500 netconn_drain_content,
2504 /* read some more data into the read buffer (the read section must be held) */
2505 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2510 if (stream->buf_pos)
2512 /* move existing data to the start of the buffer */
2513 if(stream->buf_size)
2514 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2515 stream->buf_pos = 0;
2518 if (maxlen == -1) maxlen = sizeof(stream->buf);
2520 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2521 maxlen - stream->buf_size, 0, &len );
2522 if(res == ERROR_SUCCESS)
2523 stream->buf_size += len;
2528 /* remove some amount of data from the read buffer (the read section must be held) */
2529 static void remove_chunked_data(chunked_stream_t *stream, int count)
2531 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2532 else stream->buf_pos += count;
2535 /* discard data contents until we reach end of line (the read section must be held) */
2536 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2542 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2545 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2548 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2549 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2550 } while (stream->buf_size);
2551 return ERROR_SUCCESS;
2554 /* read the size of the next chunk (the read section must be held) */
2555 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2558 DWORD chunk_size = 0, res;
2560 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2565 while (stream->buf_size)
2567 char ch = stream->buf[stream->buf_pos];
2568 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2569 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2570 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2571 else if (ch == ';' || ch == '\r' || ch == '\n')
2573 TRACE( "reading %u byte chunk\n", chunk_size );
2574 stream->chunk_size = chunk_size;
2575 req->contentLength += chunk_size;
2576 return discard_chunked_eol(stream, req);
2578 remove_chunked_data(stream, 1);
2580 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2581 if (!stream->buf_size)
2583 stream->chunk_size = 0;
2584 return ERROR_SUCCESS;
2589 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2591 /* Allow reading only from read buffer */
2595 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2597 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2598 return !chunked_stream->chunk_size;
2601 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2602 DWORD *read, read_mode_t read_mode)
2604 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2605 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2607 if(chunked_stream->chunk_size == ~0u) {
2608 res = start_next_chunk(chunked_stream, req);
2609 if(res != ERROR_SUCCESS)
2613 while(size && chunked_stream->chunk_size) {
2614 if(chunked_stream->buf_size) {
2615 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2617 /* this could block */
2618 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2621 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2622 remove_chunked_data(chunked_stream, read_bytes);
2624 read_bytes = min(size, chunked_stream->chunk_size);
2626 if(read_mode == READMODE_NOBLOCK) {
2629 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2631 if(read_bytes > avail)
2634 /* this could block */
2635 if(read_bytes == chunked_stream->chunk_size)
2639 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2640 if(res != ERROR_SUCCESS)
2644 chunked_stream->chunk_size -= read_bytes;
2646 ret_read += read_bytes;
2647 if(!chunked_stream->chunk_size) {
2648 assert(read_mode != READMODE_NOBLOCK);
2649 res = start_next_chunk(chunked_stream, req);
2650 if(res != ERROR_SUCCESS)
2654 if(read_mode == READMODE_ASYNC)
2655 read_mode = READMODE_NOBLOCK;
2658 TRACE("read %u bytes\n", ret_read);
2663 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2665 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2667 /* FIXME: we can do better */
2668 return !chunked_stream->chunk_size;
2671 static void chunked_destroy(data_stream_t *stream)
2673 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2674 heap_free(chunked_stream);
2677 static const data_stream_vtbl_t chunked_stream_vtbl = {
2678 chunked_get_avail_data,
2679 chunked_end_of_data,
2681 chunked_drain_content,
2685 /* set the request content length based on the headers */
2686 static DWORD set_content_length(http_request_t *request)
2688 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2692 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2693 request->contentLength = request->netconn_stream.content_length = 0;
2694 return ERROR_SUCCESS;
2697 size = sizeof(request->contentLength);
2698 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2699 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2700 request->contentLength = ~0u;
2701 request->netconn_stream.content_length = request->contentLength;
2702 request->netconn_stream.content_read = request->read_size;
2704 size = sizeof(encoding);
2705 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2706 !strcmpiW(encoding, szChunked))
2708 chunked_stream_t *chunked_stream;
2710 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2712 return ERROR_OUTOFMEMORY;
2714 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2715 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2716 chunked_stream->chunk_size = ~0u;
2718 if(request->read_size) {
2719 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2720 chunked_stream->buf_size = request->read_size;
2721 request->read_size = request->read_pos = 0;
2724 request->data_stream = &chunked_stream->data_stream;
2725 request->contentLength = ~0u;
2726 request->read_chunked = TRUE;
2729 if(request->decoding) {
2732 static const WCHAR gzipW[] = {'g','z','i','p',0};
2734 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2735 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2736 return init_gzip_stream(request);
2739 return ERROR_SUCCESS;
2742 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2744 INTERNET_ASYNC_RESULT iar;
2746 iar.dwResult = result;
2747 iar.dwError = error;
2749 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2750 sizeof(INTERNET_ASYNC_RESULT));
2753 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2755 DWORD res, read = 0, avail = 0;
2760 EnterCriticalSection( &req->read_section );
2762 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2763 res = refill_read_buffer(req, mode, &read);
2764 if(res == ERROR_SUCCESS && !first_notif)
2765 avail = get_avail_data(req);
2767 LeaveCriticalSection( &req->read_section );
2769 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2770 WARN("res %u read %u, closing connection\n", res, read);
2771 http_release_netconn(req, FALSE);
2774 if(res == ERROR_SUCCESS)
2775 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2777 send_request_complete(req, 0, res);
2780 /* read data from the http connection (the read section must be held) */
2781 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2783 DWORD current_read = 0, ret_read = 0;
2784 read_mode_t read_mode;
2785 DWORD res = ERROR_SUCCESS;
2787 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2789 EnterCriticalSection( &req->read_section );
2791 if(req->read_size) {
2792 ret_read = min(size, req->read_size);
2793 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2794 req->read_size -= ret_read;
2795 req->read_pos += ret_read;
2796 if(read_mode == READMODE_ASYNC)
2797 read_mode = READMODE_NOBLOCK;
2800 if(ret_read < size) {
2801 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2802 ret_read += current_read;
2805 LeaveCriticalSection( &req->read_section );
2808 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2810 if(size && !ret_read)
2811 http_release_netconn(req, res == ERROR_SUCCESS);
2816 static BOOL drain_content(http_request_t *req, BOOL blocking)
2820 if(!req->netconn || req->contentLength == -1)
2823 if(!strcmpW(req->verb, szHEAD))
2827 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2829 EnterCriticalSection( &req->read_section );
2832 DWORD bytes_read, res;
2835 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2836 if(res != ERROR_SUCCESS) {
2846 LeaveCriticalSection( &req->read_section );
2850 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2852 http_request_t *req = (http_request_t*)hdr;
2855 EnterCriticalSection( &req->read_section );
2856 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2857 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2859 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2860 if(res == ERROR_SUCCESS)
2862 LeaveCriticalSection( &req->read_section );
2867 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2869 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2870 http_request_t *req = (http_request_t*)workRequest->hdr;
2873 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2875 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2876 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2878 send_request_complete(req, res == ERROR_SUCCESS, res);
2881 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2882 DWORD flags, DWORD_PTR context)
2884 http_request_t *req = (http_request_t*)hdr;
2885 DWORD res, size, read, error = ERROR_SUCCESS;
2887 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2888 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2890 if (buffers->dwStructSize != sizeof(*buffers))
2891 return ERROR_INVALID_PARAMETER;
2893 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2895 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2897 WORKREQUEST workRequest;
2899 if (TryEnterCriticalSection( &req->read_section ))
2901 if (get_avail_data(req))
2903 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2904 &buffers->dwBufferLength, FALSE);
2905 size = buffers->dwBufferLength;
2906 LeaveCriticalSection( &req->read_section );
2909 LeaveCriticalSection( &req->read_section );
2912 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2913 workRequest.hdr = WININET_AddRef(&req->hdr);
2914 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2916 INTERNET_AsyncCall(&workRequest);
2918 return ERROR_IO_PENDING;
2922 size = buffers->dwBufferLength;
2924 EnterCriticalSection( &req->read_section );
2925 if(hdr->dwError == ERROR_SUCCESS)
2926 hdr->dwError = INTERNET_HANDLE_IN_USE;
2927 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2928 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2931 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2932 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2933 if(res != ERROR_SUCCESS)
2936 read += buffers->dwBufferLength;
2937 if(read == size || end_of_read_data(req))
2940 LeaveCriticalSection( &req->read_section );
2942 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2943 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2944 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2945 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2947 EnterCriticalSection( &req->read_section );
2950 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2951 hdr->dwError = ERROR_SUCCESS;
2953 error = hdr->dwError;
2955 LeaveCriticalSection( &req->read_section );
2956 size = buffers->dwBufferLength;
2957 buffers->dwBufferLength = read;
2960 if (res == ERROR_SUCCESS) {
2961 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2962 &size, sizeof(size));
2965 return res==ERROR_SUCCESS ? error : res;
2968 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2970 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2971 http_request_t *req = (http_request_t*)workRequest->hdr;
2974 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2976 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2977 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2979 send_request_complete(req, res == ERROR_SUCCESS, res);
2982 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2983 DWORD flags, DWORD_PTR context)
2986 http_request_t *req = (http_request_t*)hdr;
2987 DWORD res, size, read, error = ERROR_SUCCESS;
2989 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2990 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2992 if (buffers->dwStructSize != sizeof(*buffers))
2993 return ERROR_INVALID_PARAMETER;
2995 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2997 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2999 WORKREQUEST workRequest;
3001 if (TryEnterCriticalSection( &req->read_section ))
3003 if (get_avail_data(req))
3005 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
3006 &buffers->dwBufferLength, FALSE);
3007 size = buffers->dwBufferLength;
3008 LeaveCriticalSection( &req->read_section );
3011 LeaveCriticalSection( &req->read_section );
3014 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
3015 workRequest.hdr = WININET_AddRef(&req->hdr);
3016 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
3018 INTERNET_AsyncCall(&workRequest);
3020 return ERROR_IO_PENDING;
3024 size = buffers->dwBufferLength;
3026 EnterCriticalSection( &req->read_section );
3027 if(hdr->dwError == ERROR_SUCCESS)
3028 hdr->dwError = INTERNET_HANDLE_IN_USE;
3029 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3030 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3033 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
3034 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
3035 if(res != ERROR_SUCCESS)
3038 read += buffers->dwBufferLength;
3039 if(read == size || end_of_read_data(req))
3042 LeaveCriticalSection( &req->read_section );
3044 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3045 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
3046 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
3047 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3049 EnterCriticalSection( &req->read_section );
3052 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3053 hdr->dwError = ERROR_SUCCESS;
3055 error = hdr->dwError;
3057 LeaveCriticalSection( &req->read_section );
3058 size = buffers->dwBufferLength;
3059 buffers->dwBufferLength = read;
3062 if (res == ERROR_SUCCESS) {
3063 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3064 &size, sizeof(size));
3067 return res==ERROR_SUCCESS ? error : res;
3070 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3073 http_request_t *request = (http_request_t*)hdr;
3075 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3078 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3079 if (res == ERROR_SUCCESS)
3080 request->bytesWritten += *written;
3082 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3086 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
3088 http_request_t *req = (http_request_t*)workRequest->hdr;
3090 HTTP_ReceiveRequestData(req, FALSE);
3093 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3095 http_request_t *req = (http_request_t*)hdr;
3097 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3099 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3101 WORKREQUEST workRequest;
3103 /* never wait, if we can't enter the section we queue an async request right away */
3104 if (TryEnterCriticalSection( &req->read_section ))
3106 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3107 if ((*available = get_avail_data( req ))) goto done;
3108 if (end_of_read_data( req )) goto done;
3109 LeaveCriticalSection( &req->read_section );
3112 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3113 workRequest.hdr = WININET_AddRef( &req->hdr );
3115 INTERNET_AsyncCall(&workRequest);
3117 return ERROR_IO_PENDING;
3120 EnterCriticalSection( &req->read_section );
3122 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3124 refill_read_buffer( req, READMODE_ASYNC, NULL );
3125 *available = get_avail_data( req );
3129 LeaveCriticalSection( &req->read_section );
3131 TRACE( "returning %u\n", *available );
3132 return ERROR_SUCCESS;
3135 static const object_vtbl_t HTTPREQVtbl = {
3137 HTTPREQ_CloseConnection,
3138 HTTPREQ_QueryOption,
3141 HTTPREQ_ReadFileExA,
3142 HTTPREQ_ReadFileExW,
3144 HTTPREQ_QueryDataAvailable,
3148 /***********************************************************************
3149 * HTTP_HttpOpenRequestW (internal)
3151 * Open a HTTP request handle
3154 * HINTERNET a HTTP request handle on success
3158 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3159 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3160 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3161 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3163 appinfo_t *hIC = session->appInfo;
3164 http_request_t *request;
3166 DWORD len, res = ERROR_SUCCESS;
3170 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3172 return ERROR_OUTOFMEMORY;
3174 request->hdr.htype = WH_HHTTPREQ;
3175 request->hdr.dwFlags = dwFlags;
3176 request->hdr.dwContext = dwContext;
3177 request->contentLength = ~0u;
3179 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3180 request->data_stream = &request->netconn_stream.data_stream;
3181 request->connect_timeout = session->connect_timeout;
3182 request->send_timeout = session->send_timeout;
3183 request->receive_timeout = session->receive_timeout;
3185 InitializeCriticalSection( &request->read_section );
3186 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3188 WININET_AddRef( &session->hdr );
3189 request->session = session;
3190 list_add_head( &session->hdr.children, &request->hdr.entry );
3192 port = session->hostPort;
3193 if(port == INTERNET_INVALID_PORT_NUMBER)
3194 port = dwFlags & INTERNET_FLAG_SECURE ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3196 request->server = get_server(session->hostName, port, TRUE);
3197 if(!request->server) {
3198 WININET_Release(&request->hdr);
3199 return ERROR_OUTOFMEMORY;
3202 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3203 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3204 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3205 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3207 if (lpszObjectName && *lpszObjectName) {
3211 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3212 if (rc != E_POINTER)
3213 len = strlenW(lpszObjectName)+1;
3214 request->path = heap_alloc(len*sizeof(WCHAR));
3215 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3216 URL_ESCAPE_SPACES_ONLY);
3219 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3220 strcpyW(request->path,lpszObjectName);
3223 static const WCHAR slashW[] = {'/',0};
3225 request->path = heap_strdupW(slashW);
3228 if (lpszReferrer && *lpszReferrer)
3229 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3231 if (lpszAcceptTypes)
3234 for (i = 0; lpszAcceptTypes[i]; i++)
3236 if (!*lpszAcceptTypes[i]) continue;
3237 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3238 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3239 HTTP_ADDHDR_FLAG_REQ |
3240 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3244 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3245 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3247 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3248 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3249 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3253 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3255 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3257 res = ERROR_OUTOFMEMORY;
3261 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3262 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3263 heap_free(host_name);
3266 HTTP_ProcessHeader(request, hostW, session->hostName,
3267 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3269 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3270 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3271 INTERNET_DEFAULT_HTTPS_PORT :
3272 INTERNET_DEFAULT_HTTP_PORT);
3274 if (hIC->proxy && hIC->proxy[0])
3275 HTTP_DealWithProxy( hIC, session, request );
3277 INTERNET_SendCallback(&session->hdr, dwContext,
3278 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3282 TRACE("<-- %u (%p)\n", res, request);
3284 if(res != ERROR_SUCCESS) {
3285 WININET_Release( &request->hdr );
3290 *ret = request->hdr.hInternet;
3291 return ERROR_SUCCESS;
3294 /***********************************************************************
3295 * HttpOpenRequestW (WININET.@)
3297 * Open a HTTP request handle
3300 * HINTERNET a HTTP request handle on success
3304 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3305 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3306 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3307 DWORD dwFlags, DWORD_PTR dwContext)
3309 http_session_t *session;
3310 HINTERNET handle = NULL;
3313 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3314 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3315 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3316 dwFlags, dwContext);
3317 if(lpszAcceptTypes!=NULL)
3320 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3321 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3324 session = (http_session_t*) get_handle_object( hHttpSession );
3325 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3327 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3332 * My tests seem to show that the windows version does not
3333 * become asynchronous until after this point. And anyhow
3334 * if this call was asynchronous then how would you get the
3335 * necessary HINTERNET pointer returned by this function.
3338 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3339 lpszVersion, lpszReferrer, lpszAcceptTypes,
3340 dwFlags, dwContext, &handle);
3343 WININET_Release( &session->hdr );
3344 TRACE("returning %p\n", handle);
3345 if(res != ERROR_SUCCESS)
3350 static const LPCWSTR header_lookup[] = {
3351 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3352 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3353 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3354 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3355 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3356 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3357 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3358 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3359 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3360 szDate, /* HTTP_QUERY_DATE = 9 */
3361 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3362 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3363 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3364 szURI, /* HTTP_QUERY_URI = 13 */
3365 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3366 NULL, /* HTTP_QUERY_COST = 15 */
3367 NULL, /* HTTP_QUERY_LINK = 16 */
3368 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3369 NULL, /* HTTP_QUERY_VERSION = 18 */
3370 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3371 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3372 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3373 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3374 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3375 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3376 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3377 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3378 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3379 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3380 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3381 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3382 NULL, /* HTTP_QUERY_FROM = 31 */
3383 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3384 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3385 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3386 szReferer, /* HTTP_QUERY_REFERER = 35 */
3387 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3388 szServer, /* HTTP_QUERY_SERVER = 37 */
3389 NULL, /* HTTP_TITLE = 38 */
3390 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3391 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3392 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3393 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3394 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3395 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3396 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3397 NULL, /* HTTP_QUERY_REFRESH = 46 */
3398 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3399 szAge, /* HTTP_QUERY_AGE = 48 */
3400 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3401 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3402 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3403 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3404 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3405 szETag, /* HTTP_QUERY_ETAG = 54 */
3406 hostW, /* HTTP_QUERY_HOST = 55 */
3407 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3408 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3409 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3410 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3411 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3412 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3413 szRange, /* HTTP_QUERY_RANGE = 62 */
3414 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3415 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3416 szVary, /* HTTP_QUERY_VARY = 65 */
3417 szVia, /* HTTP_QUERY_VIA = 66 */
3418 szWarning, /* HTTP_QUERY_WARNING = 67 */
3419 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3420 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3421 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3424 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3426 /***********************************************************************
3427 * HTTP_HttpQueryInfoW (internal)
3429 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3430 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3432 LPHTTPHEADERW lphttpHdr = NULL;
3433 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3434 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3435 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3438 /* Find requested header structure */
3441 case HTTP_QUERY_CUSTOM:
3442 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3443 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3445 case HTTP_QUERY_RAW_HEADERS_CRLF:
3449 DWORD res = ERROR_INVALID_PARAMETER;
3452 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3454 headers = request->rawHeaders;
3457 len = strlenW(headers) * sizeof(WCHAR);
3459 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3461 len += sizeof(WCHAR);
3462 res = ERROR_INSUFFICIENT_BUFFER;
3467 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3470 len = strlenW(szCrLf) * sizeof(WCHAR);
3471 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3473 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3474 res = ERROR_SUCCESS;
3476 *lpdwBufferLength = len;
3478 if (request_only) heap_free(headers);
3481 case HTTP_QUERY_RAW_HEADERS:
3483 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3485 LPWSTR pszString = lpBuffer;
3487 for (i = 0; ppszRawHeaderLines[i]; i++)
3488 size += strlenW(ppszRawHeaderLines[i]) + 1;
3490 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3492 HTTP_FreeTokens(ppszRawHeaderLines);
3493 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3494 return ERROR_INSUFFICIENT_BUFFER;
3498 for (i = 0; ppszRawHeaderLines[i]; i++)
3500 DWORD len = strlenW(ppszRawHeaderLines[i]);
3501 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3505 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3507 *lpdwBufferLength = size * sizeof(WCHAR);
3508 HTTP_FreeTokens(ppszRawHeaderLines);
3510 return ERROR_SUCCESS;
3512 case HTTP_QUERY_STATUS_TEXT:
3513 if (request->statusText)
3515 DWORD len = strlenW(request->statusText);
3516 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3518 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3519 return ERROR_INSUFFICIENT_BUFFER;
3523 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3524 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3526 *lpdwBufferLength = len * sizeof(WCHAR);
3527 return ERROR_SUCCESS;
3530 case HTTP_QUERY_VERSION:
3531 if (request->version)
3533 DWORD len = strlenW(request->version);
3534 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3536 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3537 return ERROR_INSUFFICIENT_BUFFER;
3541 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3542 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3544 *lpdwBufferLength = len * sizeof(WCHAR);
3545 return ERROR_SUCCESS;
3548 case HTTP_QUERY_CONTENT_ENCODING:
3549 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3550 requested_index,request_only);
3552 case HTTP_QUERY_STATUS_CODE: {
3553 DWORD res = ERROR_SUCCESS;
3556 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3561 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3562 if(*lpdwBufferLength >= sizeof(DWORD))
3563 *(DWORD*)lpBuffer = request->status_code;
3565 res = ERROR_INSUFFICIENT_BUFFER;
3566 *lpdwBufferLength = sizeof(DWORD);
3570 static const WCHAR formatW[] = {'%','u',0};
3572 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3574 if(size <= *lpdwBufferLength) {
3575 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3577 size += sizeof(WCHAR);
3578 res = ERROR_INSUFFICIENT_BUFFER;
3581 *lpdwBufferLength = size;
3586 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3588 if (level < LAST_TABLE_HEADER && header_lookup[level])
3589 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3590 requested_index,request_only);
3594 lphttpHdr = &request->custHeaders[index];
3596 /* Ensure header satisfies requested attributes */
3598 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3599 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3601 return ERROR_HTTP_HEADER_NOT_FOUND;
3604 if (lpdwIndex) (*lpdwIndex)++;
3606 /* coalesce value to requested type */
3607 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3609 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3610 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3612 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3618 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3620 tmpTM = *gmtime(&tmpTime);
3621 STHook = (SYSTEMTIME *)lpBuffer;
3622 STHook->wDay = tmpTM.tm_mday;
3623 STHook->wHour = tmpTM.tm_hour;
3624 STHook->wMilliseconds = 0;
3625 STHook->wMinute = tmpTM.tm_min;
3626 STHook->wDayOfWeek = tmpTM.tm_wday;
3627 STHook->wMonth = tmpTM.tm_mon + 1;
3628 STHook->wSecond = tmpTM.tm_sec;
3629 STHook->wYear = tmpTM.tm_year;
3631 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3632 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3633 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3635 else if (lphttpHdr->lpszValue)
3637 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3639 if (len > *lpdwBufferLength)
3641 *lpdwBufferLength = len;
3642 return ERROR_INSUFFICIENT_BUFFER;
3646 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3647 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3649 *lpdwBufferLength = len - sizeof(WCHAR);
3651 return ERROR_SUCCESS;
3654 /***********************************************************************
3655 * HttpQueryInfoW (WININET.@)
3657 * Queries for information about an HTTP request
3664 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3665 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3667 http_request_t *request;
3670 if (TRACE_ON(wininet)) {
3671 #define FE(x) { x, #x }
3672 static const wininet_flag_info query_flags[] = {
3673 FE(HTTP_QUERY_MIME_VERSION),
3674 FE(HTTP_QUERY_CONTENT_TYPE),
3675 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3676 FE(HTTP_QUERY_CONTENT_ID),
3677 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3678 FE(HTTP_QUERY_CONTENT_LENGTH),
3679 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3680 FE(HTTP_QUERY_ALLOW),
3681 FE(HTTP_QUERY_PUBLIC),
3682 FE(HTTP_QUERY_DATE),
3683 FE(HTTP_QUERY_EXPIRES),
3684 FE(HTTP_QUERY_LAST_MODIFIED),
3685 FE(HTTP_QUERY_MESSAGE_ID),
3687 FE(HTTP_QUERY_DERIVED_FROM),
3688 FE(HTTP_QUERY_COST),
3689 FE(HTTP_QUERY_LINK),
3690 FE(HTTP_QUERY_PRAGMA),
3691 FE(HTTP_QUERY_VERSION),
3692 FE(HTTP_QUERY_STATUS_CODE),
3693 FE(HTTP_QUERY_STATUS_TEXT),
3694 FE(HTTP_QUERY_RAW_HEADERS),
3695 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3696 FE(HTTP_QUERY_CONNECTION),
3697 FE(HTTP_QUERY_ACCEPT),
3698 FE(HTTP_QUERY_ACCEPT_CHARSET),
3699 FE(HTTP_QUERY_ACCEPT_ENCODING),
3700 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3701 FE(HTTP_QUERY_AUTHORIZATION),
3702 FE(HTTP_QUERY_CONTENT_ENCODING),
3703 FE(HTTP_QUERY_FORWARDED),
3704 FE(HTTP_QUERY_FROM),
3705 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3706 FE(HTTP_QUERY_LOCATION),
3707 FE(HTTP_QUERY_ORIG_URI),
3708 FE(HTTP_QUERY_REFERER),
3709 FE(HTTP_QUERY_RETRY_AFTER),
3710 FE(HTTP_QUERY_SERVER),
3711 FE(HTTP_QUERY_TITLE),
3712 FE(HTTP_QUERY_USER_AGENT),
3713 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3714 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3715 FE(HTTP_QUERY_ACCEPT_RANGES),
3716 FE(HTTP_QUERY_SET_COOKIE),
3717 FE(HTTP_QUERY_COOKIE),
3718 FE(HTTP_QUERY_REQUEST_METHOD),
3719 FE(HTTP_QUERY_REFRESH),
3720 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3722 FE(HTTP_QUERY_CACHE_CONTROL),
3723 FE(HTTP_QUERY_CONTENT_BASE),
3724 FE(HTTP_QUERY_CONTENT_LOCATION),
3725 FE(HTTP_QUERY_CONTENT_MD5),
3726 FE(HTTP_QUERY_CONTENT_RANGE),
3727 FE(HTTP_QUERY_ETAG),
3728 FE(HTTP_QUERY_HOST),
3729 FE(HTTP_QUERY_IF_MATCH),
3730 FE(HTTP_QUERY_IF_NONE_MATCH),
3731 FE(HTTP_QUERY_IF_RANGE),
3732 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3733 FE(HTTP_QUERY_MAX_FORWARDS),
3734 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3735 FE(HTTP_QUERY_RANGE),
3736 FE(HTTP_QUERY_TRANSFER_ENCODING),
3737 FE(HTTP_QUERY_UPGRADE),
3738 FE(HTTP_QUERY_VARY),
3740 FE(HTTP_QUERY_WARNING),
3741 FE(HTTP_QUERY_CUSTOM)
3743 static const wininet_flag_info modifier_flags[] = {
3744 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3745 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3746 FE(HTTP_QUERY_FLAG_NUMBER),
3747 FE(HTTP_QUERY_FLAG_COALESCE)
3750 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3751 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3754 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3755 TRACE(" Attribute:");
3756 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3757 if (query_flags[i].val == info) {
3758 TRACE(" %s", query_flags[i].name);
3762 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3763 TRACE(" Unknown (%08x)", info);
3766 TRACE(" Modifier:");
3767 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3768 if (modifier_flags[i].val & info_mod) {
3769 TRACE(" %s", modifier_flags[i].name);
3770 info_mod &= ~ modifier_flags[i].val;
3775 TRACE(" Unknown (%08x)", info_mod);
3780 request = (http_request_t*) get_handle_object( hHttpRequest );
3781 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3783 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3787 if (lpBuffer == NULL)
3788 *lpdwBufferLength = 0;
3789 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3790 lpBuffer, lpdwBufferLength, lpdwIndex);
3794 WININET_Release( &request->hdr );
3796 TRACE("%u <--\n", res);
3797 if(res != ERROR_SUCCESS)
3799 return res == ERROR_SUCCESS;
3802 /***********************************************************************
3803 * HttpQueryInfoA (WININET.@)
3805 * Queries for information about an HTTP request
3812 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3813 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3819 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3820 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3822 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3823 lpdwBufferLength, lpdwIndex );
3829 len = (*lpdwBufferLength)*sizeof(WCHAR);
3830 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3832 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3838 bufferW = heap_alloc(alloclen);
3839 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3840 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3841 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3848 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3852 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3853 lpBuffer, *lpdwBufferLength, NULL, NULL );
3854 *lpdwBufferLength = len - 1;
3856 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3859 /* since the strings being returned from HttpQueryInfoW should be
3860 * only ASCII characters, it is reasonable to assume that all of
3861 * the Unicode characters can be reduced to a single byte */
3862 *lpdwBufferLength = len / sizeof(WCHAR);
3864 heap_free( bufferW );
3868 /***********************************************************************
3869 * HTTP_GetRedirectURL (internal)
3871 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3873 static WCHAR szHttp[] = {'h','t','t','p',0};
3874 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3875 http_session_t *session = request->session;
3876 URL_COMPONENTSW urlComponents;
3877 DWORD url_length = 0;
3879 LPWSTR combined_url;
3881 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3882 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3883 urlComponents.dwSchemeLength = 0;
3884 urlComponents.lpszHostName = session->hostName;
3885 urlComponents.dwHostNameLength = 0;
3886 urlComponents.nPort = session->hostPort;
3887 urlComponents.lpszUserName = session->userName;
3888 urlComponents.dwUserNameLength = 0;
3889 urlComponents.lpszPassword = NULL;
3890 urlComponents.dwPasswordLength = 0;
3891 urlComponents.lpszUrlPath = request->path;
3892 urlComponents.dwUrlPathLength = 0;
3893 urlComponents.lpszExtraInfo = NULL;
3894 urlComponents.dwExtraInfoLength = 0;
3896 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3897 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3900 orig_url = heap_alloc(url_length);
3902 /* convert from bytes to characters */
3903 url_length = url_length / sizeof(WCHAR) - 1;
3904 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3906 heap_free(orig_url);
3911 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3912 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3914 heap_free(orig_url);
3917 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3919 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3921 heap_free(orig_url);
3922 heap_free(combined_url);
3925 heap_free(orig_url);
3926 return combined_url;
3930 /***********************************************************************
3931 * HTTP_HandleRedirect (internal)
3933 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3935 http_session_t *session = request->session;
3936 appinfo_t *hIC = session->appInfo;
3937 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
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(!using_proxy && (strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort)) {
4024 server_t *new_server;
4026 new_server = get_server(hostName, urlComponents.nPort, 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)
4088 LPWSTR requestString;
4094 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
4095 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
4096 http_session_t *session = request->session;
4100 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
4101 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
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 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4716 INTERNET_STATUS_CONNECTING_TO_SERVER,
4717 request->server->addr_str,
4718 strlen(request->server->addr_str)+1);
4720 res = create_netconn(is_https, request->server, request->security_flags,
4721 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4722 request->connect_timeout, &netconn);
4723 if(res != ERROR_SUCCESS) {
4724 ERR("create_netconn failed: %u\n", res);
4728 request->netconn = netconn;
4730 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4731 INTERNET_STATUS_CONNECTED_TO_SERVER,
4732 request->server->addr_str, strlen(request->server->addr_str)+1);
4735 /* Note: we differ from Microsoft's WinINet here. they seem to have
4736 * a bug that causes no status callbacks to be sent when starting
4737 * a tunnel to a proxy server using the CONNECT verb. i believe our
4738 * behaviour to be more correct and to not cause any incompatibilities
4739 * because using a secure connection through a proxy server is a rare
4740 * case that would be hard for anyone to depend on */
4741 if(request->session->appInfo->proxy)
4742 res = HTTP_SecureProxyConnect(request);
4743 if(res == ERROR_SUCCESS)
4744 res = NETCON_secure_connect(request->netconn);
4747 if(res != ERROR_SUCCESS) {
4748 http_release_netconn(request, FALSE);
4753 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4754 return ERROR_SUCCESS;
4757 /***********************************************************************
4758 * HTTP_HttpSendRequestW (internal)
4760 * Sends the specified request to the HTTP server
4763 * ERROR_SUCCESS on success
4764 * win32 error code on failure
4767 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4768 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4769 DWORD dwContentLength, BOOL bEndRequest)
4772 BOOL redirected = FALSE;
4773 LPWSTR requestString = NULL;
4776 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4777 static const WCHAR szContentLength[] =
4778 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4779 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4782 TRACE("--> %p\n", request);
4784 assert(request->hdr.htype == WH_HHTTPREQ);
4786 /* if the verb is NULL default to GET */
4788 request->verb = heap_strdupW(szGET);
4790 if (dwContentLength || strcmpW(request->verb, szGET))
4792 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4793 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4794 request->bytesToWrite = dwContentLength;
4796 if (request->session->appInfo->agent)
4798 WCHAR *agent_header;
4799 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4802 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4803 agent_header = heap_alloc(len * sizeof(WCHAR));
4804 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4806 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4807 heap_free(agent_header);
4809 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4811 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4812 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4814 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4816 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4817 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4818 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4821 /* add the headers the caller supplied */
4822 if( lpszHeaders && dwHeaderLength )
4823 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4828 BOOL reusing_connection;
4832 reusing_connection = request->netconn != NULL;
4835 request->contentLength = ~0u;
4836 request->bytesToWrite = 0;
4839 if (TRACE_ON(wininet))
4841 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4842 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4845 HTTP_FixURL(request);
4846 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4848 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4850 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4851 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4853 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4854 HTTP_InsertCookies(request);
4856 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4858 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4859 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4863 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4866 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4868 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4871 /* send the request as ASCII, tack on the optional data */
4872 if (!lpOptional || redirected)
4873 dwOptionalLength = 0;
4874 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4875 NULL, 0, NULL, NULL );
4876 ascii_req = heap_alloc(len + dwOptionalLength);
4877 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4878 ascii_req, len, NULL, NULL );
4880 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4881 len = (len + dwOptionalLength - 1);
4883 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4885 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4886 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4888 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4889 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4890 heap_free( ascii_req );
4891 if(res != ERROR_SUCCESS) {
4892 TRACE("send failed: %u\n", res);
4893 if(!reusing_connection)
4895 http_release_netconn(request, FALSE);
4900 request->bytesWritten = dwOptionalLength;
4902 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4903 INTERNET_STATUS_REQUEST_SENT,
4904 &len, sizeof(DWORD));
4910 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4911 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4913 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4914 /* FIXME: We should know that connection is closed before sending
4915 * headers. Otherwise wrong callbacks are executed */
4916 if(!responseLen && reusing_connection) {
4917 TRACE("Connection closed by server, reconnecting\n");
4918 http_release_netconn(request, FALSE);
4923 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4924 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4927 http_process_keep_alive(request);
4928 HTTP_ProcessCookies(request);
4929 HTTP_ProcessExpires(request);
4930 HTTP_ProcessLastModified(request);
4932 res = set_content_length(request);
4933 if(res != ERROR_SUCCESS)
4935 if(!request->contentLength)
4936 http_release_netconn(request, TRUE);
4938 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4940 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4941 dwBufferSize=sizeof(szNewLocation);
4942 switch(request->status_code) {
4943 case HTTP_STATUS_REDIRECT:
4944 case HTTP_STATUS_MOVED:
4945 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4946 case HTTP_STATUS_REDIRECT_METHOD:
4947 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4950 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4951 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4953 heap_free(request->verb);
4954 request->verb = heap_strdupW(szGET);
4956 http_release_netconn(request, drain_content(request, FALSE));
4957 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4959 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4960 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4961 res = HTTP_HandleRedirect(request, new_url);
4962 if (res == ERROR_SUCCESS)
4964 heap_free(requestString);
4967 heap_free( new_url );
4972 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4974 WCHAR szAuthValue[2048];
4976 if (request->status_code == HTTP_STATUS_DENIED)
4978 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4980 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4982 if (HTTP_DoAuthorization(request, szAuthValue,
4984 request->session->userName,
4985 request->session->password,
4988 heap_free(requestString);
4989 if(!drain_content(request, TRUE)) {
4990 FIXME("Could not drain content\n");
4991 http_release_netconn(request, FALSE);
4999 TRACE("Cleaning wrong authorization data\n");
5000 destroy_authinfo(request->authInfo);
5001 request->authInfo = NULL;
5004 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
5007 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
5009 if (HTTP_DoAuthorization(request, szAuthValue,
5010 &request->proxyAuthInfo,
5011 request->session->appInfo->proxyUsername,
5012 request->session->appInfo->proxyPassword,
5015 if(!drain_content(request, TRUE)) {
5016 FIXME("Could not drain content\n");
5017 http_release_netconn(request, FALSE);
5025 TRACE("Cleaning wrong proxy authorization data\n");
5026 destroy_authinfo(request->proxyAuthInfo);
5027 request->proxyAuthInfo = NULL;
5033 res = ERROR_SUCCESS;
5038 heap_free(requestString);
5040 /* TODO: send notification for P3P header */
5042 if(res == ERROR_SUCCESS)
5043 create_cache_entry(request);
5045 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5047 if (res == ERROR_SUCCESS) {
5048 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
5049 HTTP_ReceiveRequestData(request, TRUE);
5051 send_request_complete(request,
5052 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
5054 send_request_complete(request, 0, res);
5062 /***********************************************************************
5064 * Helper functions for the HttpSendRequest(Ex) functions
5067 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
5069 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
5070 http_request_t *request = (http_request_t*) workRequest->hdr;
5072 TRACE("%p\n", request);
5074 HTTP_HttpSendRequestW(request, req->lpszHeader,
5075 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5076 req->dwContentLength, req->bEndRequest);
5078 heap_free(req->lpszHeader);
5082 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5086 DWORD res = ERROR_SUCCESS;
5088 if(!request->netconn) {
5089 WARN("Not connected\n");
5090 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5091 return ERROR_INTERNET_OPERATION_CANCELLED;
5094 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5095 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5097 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5099 res = ERROR_HTTP_HEADER_NOT_FOUND;
5101 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5102 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5104 /* process cookies here. Is this right? */
5105 http_process_keep_alive(request);
5106 HTTP_ProcessCookies(request);
5107 HTTP_ProcessExpires(request);
5108 HTTP_ProcessLastModified(request);
5110 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5111 if(!request->contentLength)
5112 http_release_netconn(request, TRUE);
5115 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5117 switch(request->status_code) {
5118 case HTTP_STATUS_REDIRECT:
5119 case HTTP_STATUS_MOVED:
5120 case HTTP_STATUS_REDIRECT_METHOD:
5121 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5122 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5123 dwBufferSize=sizeof(szNewLocation);
5124 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5127 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5128 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5130 heap_free(request->verb);
5131 request->verb = heap_strdupW(szGET);
5133 http_release_netconn(request, drain_content(request, FALSE));
5134 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5136 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5137 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5138 res = HTTP_HandleRedirect(request, new_url);
5139 if (res == ERROR_SUCCESS)
5140 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5141 heap_free( new_url );
5147 if(res == ERROR_SUCCESS)
5148 create_cache_entry(request);
5150 if (res == ERROR_SUCCESS && request->contentLength)
5151 HTTP_ReceiveRequestData(request, TRUE);
5153 send_request_complete(request, res == ERROR_SUCCESS, res);
5158 /***********************************************************************
5159 * HttpEndRequestA (WININET.@)
5161 * Ends an HTTP request that was started by HttpSendRequestEx
5164 * TRUE if successful
5168 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5169 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5171 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5175 SetLastError(ERROR_INVALID_PARAMETER);
5179 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5182 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5184 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5185 http_request_t *request = (http_request_t*)work->hdr;
5187 TRACE("%p\n", request);
5189 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5192 /***********************************************************************
5193 * HttpEndRequestW (WININET.@)
5195 * Ends an HTTP request that was started by HttpSendRequestEx
5198 * TRUE if successful
5202 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5203 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5205 http_request_t *request;
5208 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5212 SetLastError(ERROR_INVALID_PARAMETER);
5216 request = (http_request_t*) get_handle_object( hRequest );
5218 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5220 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5222 WININET_Release( &request->hdr );
5225 request->hdr.dwFlags |= dwFlags;
5227 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5230 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5232 work.asyncproc = AsyncHttpEndRequestProc;
5233 work.hdr = WININET_AddRef( &request->hdr );
5235 work_endrequest = &work.u.HttpEndRequestW;
5236 work_endrequest->dwFlags = dwFlags;
5237 work_endrequest->dwContext = dwContext;
5239 INTERNET_AsyncCall(&work);
5240 res = ERROR_IO_PENDING;
5243 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5245 WININET_Release( &request->hdr );
5246 TRACE("%u <--\n", res);
5247 if(res != ERROR_SUCCESS)
5249 return res == ERROR_SUCCESS;
5252 /***********************************************************************
5253 * HttpSendRequestExA (WININET.@)
5255 * Sends the specified request to the HTTP server and allows chunked
5260 * Failure: FALSE, call GetLastError() for more information.
5262 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5263 LPINTERNET_BUFFERSA lpBuffersIn,
5264 LPINTERNET_BUFFERSA lpBuffersOut,
5265 DWORD dwFlags, DWORD_PTR dwContext)
5267 INTERNET_BUFFERSW BuffersInW;
5270 LPWSTR header = NULL;
5272 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5273 lpBuffersOut, dwFlags, dwContext);
5277 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5278 if (lpBuffersIn->lpcszHeader)
5280 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5281 lpBuffersIn->dwHeadersLength,0,0);
5282 header = heap_alloc(headerlen*sizeof(WCHAR));
5283 if (!(BuffersInW.lpcszHeader = header))
5285 SetLastError(ERROR_OUTOFMEMORY);
5288 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5289 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5293 BuffersInW.lpcszHeader = NULL;
5294 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5295 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5296 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5297 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5298 BuffersInW.Next = NULL;
5301 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5307 /***********************************************************************
5308 * HttpSendRequestExW (WININET.@)
5310 * Sends the specified request to the HTTP server and allows chunked
5315 * Failure: FALSE, call GetLastError() for more information.
5317 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5318 LPINTERNET_BUFFERSW lpBuffersIn,
5319 LPINTERNET_BUFFERSW lpBuffersOut,
5320 DWORD dwFlags, DWORD_PTR dwContext)
5322 http_request_t *request;
5323 http_session_t *session;
5327 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5328 lpBuffersOut, dwFlags, dwContext);
5330 request = (http_request_t*) get_handle_object( hRequest );
5332 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5334 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5338 session = request->session;
5339 assert(session->hdr.htype == WH_HHTTPSESSION);
5340 hIC = session->appInfo;
5341 assert(hIC->hdr.htype == WH_HINIT);
5343 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5345 WORKREQUEST workRequest;
5346 struct WORKREQ_HTTPSENDREQUESTW *req;
5348 workRequest.asyncproc = AsyncHttpSendRequestProc;
5349 workRequest.hdr = WININET_AddRef( &request->hdr );
5350 req = &workRequest.u.HttpSendRequestW;
5355 if (lpBuffersIn->lpcszHeader)
5357 if (lpBuffersIn->dwHeadersLength == ~0u)
5358 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5360 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5362 req->lpszHeader = heap_alloc(size);
5363 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5365 else req->lpszHeader = NULL;
5367 req->dwHeaderLength = size / sizeof(WCHAR);
5368 req->lpOptional = lpBuffersIn->lpvBuffer;
5369 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5370 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5374 req->lpszHeader = NULL;
5375 req->dwHeaderLength = 0;
5376 req->lpOptional = NULL;
5377 req->dwOptionalLength = 0;
5378 req->dwContentLength = 0;
5381 req->bEndRequest = FALSE;
5383 INTERNET_AsyncCall(&workRequest);
5385 * This is from windows.
5387 res = ERROR_IO_PENDING;
5392 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5393 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5394 lpBuffersIn->dwBufferTotal, FALSE);
5396 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5401 WININET_Release( &request->hdr );
5405 return res == ERROR_SUCCESS;
5408 /***********************************************************************
5409 * HttpSendRequestW (WININET.@)
5411 * Sends the specified request to the HTTP server
5418 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5419 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5421 http_request_t *request;
5422 http_session_t *session = NULL;
5423 appinfo_t *hIC = NULL;
5424 DWORD res = ERROR_SUCCESS;
5426 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5427 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5429 request = (http_request_t*) get_handle_object( hHttpRequest );
5430 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5432 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5436 session = request->session;
5437 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5439 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5443 hIC = session->appInfo;
5444 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5446 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5450 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5452 WORKREQUEST workRequest;
5453 struct WORKREQ_HTTPSENDREQUESTW *req;
5455 workRequest.asyncproc = AsyncHttpSendRequestProc;
5456 workRequest.hdr = WININET_AddRef( &request->hdr );
5457 req = &workRequest.u.HttpSendRequestW;
5462 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5463 else size = dwHeaderLength * sizeof(WCHAR);
5465 req->lpszHeader = heap_alloc(size);
5466 memcpy(req->lpszHeader, lpszHeaders, size);
5469 req->lpszHeader = 0;
5470 req->dwHeaderLength = dwHeaderLength;
5471 req->lpOptional = lpOptional;
5472 req->dwOptionalLength = dwOptionalLength;
5473 req->dwContentLength = dwOptionalLength;
5474 req->bEndRequest = TRUE;
5476 INTERNET_AsyncCall(&workRequest);
5478 * This is from windows.
5480 res = ERROR_IO_PENDING;
5484 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5485 dwHeaderLength, lpOptional, dwOptionalLength,
5486 dwOptionalLength, TRUE);
5490 WININET_Release( &request->hdr );
5493 return res == ERROR_SUCCESS;
5496 /***********************************************************************
5497 * HttpSendRequestA (WININET.@)
5499 * Sends the specified request to the HTTP server
5506 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5507 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5510 LPWSTR szHeaders=NULL;
5511 DWORD nLen=dwHeaderLength;
5512 if(lpszHeaders!=NULL)
5514 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5515 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5516 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5518 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5519 heap_free(szHeaders);
5523 /***********************************************************************
5524 * HTTPSESSION_Destroy (internal)
5526 * Deallocate session handle
5529 static void HTTPSESSION_Destroy(object_header_t *hdr)
5531 http_session_t *session = (http_session_t*) hdr;
5533 TRACE("%p\n", session);
5535 WININET_Release(&session->appInfo->hdr);
5537 heap_free(session->hostName);
5538 heap_free(session->password);
5539 heap_free(session->userName);
5542 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5544 http_session_t *ses = (http_session_t *)hdr;
5547 case INTERNET_OPTION_HANDLE_TYPE:
5548 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5550 if (*size < sizeof(ULONG))
5551 return ERROR_INSUFFICIENT_BUFFER;
5553 *size = sizeof(DWORD);
5554 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5555 return ERROR_SUCCESS;
5556 case INTERNET_OPTION_CONNECT_TIMEOUT:
5557 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5559 if (*size < sizeof(DWORD))
5560 return ERROR_INSUFFICIENT_BUFFER;
5562 *size = sizeof(DWORD);
5563 *(DWORD *)buffer = ses->connect_timeout;
5564 return ERROR_SUCCESS;
5566 case INTERNET_OPTION_SEND_TIMEOUT:
5567 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5569 if (*size < sizeof(DWORD))
5570 return ERROR_INSUFFICIENT_BUFFER;
5572 *size = sizeof(DWORD);
5573 *(DWORD *)buffer = ses->send_timeout;
5574 return ERROR_SUCCESS;
5576 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5577 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5579 if (*size < sizeof(DWORD))
5580 return ERROR_INSUFFICIENT_BUFFER;
5582 *size = sizeof(DWORD);
5583 *(DWORD *)buffer = ses->receive_timeout;
5584 return ERROR_SUCCESS;
5587 return INET_QueryOption(hdr, option, buffer, size, unicode);
5590 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5592 http_session_t *ses = (http_session_t*)hdr;
5595 case INTERNET_OPTION_USERNAME:
5597 heap_free(ses->userName);
5598 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5599 return ERROR_SUCCESS;
5601 case INTERNET_OPTION_PASSWORD:
5603 heap_free(ses->password);
5604 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5605 return ERROR_SUCCESS;
5607 case INTERNET_OPTION_CONNECT_TIMEOUT:
5609 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5610 ses->connect_timeout = *(DWORD *)buffer;
5611 return ERROR_SUCCESS;
5613 case INTERNET_OPTION_SEND_TIMEOUT:
5615 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5616 ses->send_timeout = *(DWORD *)buffer;
5617 return ERROR_SUCCESS;
5619 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5621 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5622 ses->receive_timeout = *(DWORD *)buffer;
5623 return ERROR_SUCCESS;
5628 return INET_SetOption(hdr, option, buffer, size);
5631 static const object_vtbl_t HTTPSESSIONVtbl = {
5632 HTTPSESSION_Destroy,
5634 HTTPSESSION_QueryOption,
5635 HTTPSESSION_SetOption,
5644 /***********************************************************************
5645 * HTTP_Connect (internal)
5647 * Create http session handle
5650 * HINTERNET a session handle on success
5654 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5655 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5656 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5657 DWORD dwInternalFlags, HINTERNET *ret)
5659 http_session_t *session = NULL;
5663 if (!lpszServerName || !lpszServerName[0])
5664 return ERROR_INVALID_PARAMETER;
5666 assert( hIC->hdr.htype == WH_HINIT );
5668 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5670 return ERROR_OUTOFMEMORY;
5673 * According to my tests. The name is not resolved until a request is sent
5676 session->hdr.htype = WH_HHTTPSESSION;
5677 session->hdr.dwFlags = dwFlags;
5678 session->hdr.dwContext = dwContext;
5679 session->hdr.dwInternalFlags |= dwInternalFlags;
5681 WININET_AddRef( &hIC->hdr );
5682 session->appInfo = hIC;
5683 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5685 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5686 if(hIC->proxyBypass)
5687 FIXME("Proxy bypass is ignored.\n");
5689 session->hostName = heap_strdupW(lpszServerName);
5690 if (lpszUserName && lpszUserName[0])
5691 session->userName = heap_strdupW(lpszUserName);
5692 if (lpszPassword && lpszPassword[0])
5693 session->password = heap_strdupW(lpszPassword);
5694 session->hostPort = serverPort;
5695 session->connect_timeout = hIC->connect_timeout;
5696 session->send_timeout = INFINITE;
5697 session->receive_timeout = INFINITE;
5699 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5700 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5702 INTERNET_SendCallback(&hIC->hdr, dwContext,
5703 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5708 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5712 TRACE("%p --> %p\n", hIC, session);
5714 *ret = session->hdr.hInternet;
5715 return ERROR_SUCCESS;
5718 /***********************************************************************
5719 * HTTP_clear_response_headers (internal)
5721 * clear out any old response headers
5723 static void HTTP_clear_response_headers( http_request_t *request )
5727 for( i=0; i<request->nCustHeaders; i++)
5729 if( !request->custHeaders[i].lpszField )
5731 if( !request->custHeaders[i].lpszValue )
5733 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5735 HTTP_DeleteCustomHeader( request, i );
5740 /***********************************************************************
5741 * HTTP_GetResponseHeaders (internal)
5743 * Read server response
5750 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5753 WCHAR buffer[MAX_REPLY_LEN];
5754 DWORD buflen = MAX_REPLY_LEN;
5755 BOOL bSuccess = FALSE;
5757 char bufferA[MAX_REPLY_LEN];
5758 LPWSTR status_code = NULL, status_text = NULL;
5759 DWORD cchMaxRawHeaders = 1024;
5760 LPWSTR lpszRawHeaders = NULL;
5762 DWORD cchRawHeaders = 0;
5763 BOOL codeHundred = FALSE;
5767 if(!request->netconn)
5770 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5772 static const WCHAR szHundred[] = {'1','0','0',0};
5774 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5776 buflen = MAX_REPLY_LEN;
5777 if (!read_line(request, bufferA, &buflen))
5780 /* clear old response headers (eg. from a redirect response) */
5782 HTTP_clear_response_headers( request );
5787 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5788 /* check is this a status code line? */
5789 if (!strncmpW(buffer, g_szHttp1_0, 4))
5791 /* split the version from the status code */
5792 status_code = strchrW( buffer, ' ' );
5797 /* split the status code from the status text */
5798 status_text = strchrW( status_code, ' ' );
5803 request->status_code = atoiW(status_code);
5805 TRACE("version [%s] status code [%s] status text [%s]\n",
5806 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5808 codeHundred = (!strcmpW(status_code, szHundred));
5810 else if (!codeHundred)
5812 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5814 heap_free(request->version);
5815 heap_free(request->statusText);
5817 request->status_code = HTTP_STATUS_OK;
5818 request->version = heap_strdupW(g_szHttp1_0);
5819 request->statusText = heap_strdupW(szOK);
5821 heap_free(request->rawHeaders);
5822 request->rawHeaders = heap_strdupW(szDefaultHeader);
5827 } while (codeHundred);
5829 /* Add status code */
5830 HTTP_ProcessHeader(request, szStatus, status_code,
5831 HTTP_ADDHDR_FLAG_REPLACE);
5833 heap_free(request->version);
5834 heap_free(request->statusText);
5836 request->version = heap_strdupW(buffer);
5837 request->statusText = heap_strdupW(status_text);
5839 /* Restore the spaces */
5840 *(status_code-1) = ' ';
5841 *(status_text-1) = ' ';
5843 /* regenerate raw headers */
5844 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5845 if (!lpszRawHeaders) goto lend;
5847 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5848 cchMaxRawHeaders *= 2;
5849 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5850 if (temp == NULL) goto lend;
5851 lpszRawHeaders = temp;
5852 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5853 cchRawHeaders += (buflen-1);
5854 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5855 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5856 lpszRawHeaders[cchRawHeaders] = '\0';
5858 /* Parse each response line */
5861 buflen = MAX_REPLY_LEN;
5862 if (read_line(request, bufferA, &buflen))
5864 LPWSTR * pFieldAndValue;
5866 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5868 if (!bufferA[0]) break;
5869 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5871 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5874 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5875 cchMaxRawHeaders *= 2;
5876 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5877 if (temp == NULL) goto lend;
5878 lpszRawHeaders = temp;
5879 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5880 cchRawHeaders += (buflen-1);
5881 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5882 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5883 lpszRawHeaders[cchRawHeaders] = '\0';
5885 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5886 HTTP_ADDREQ_FLAG_ADD );
5888 HTTP_FreeTokens(pFieldAndValue);
5899 /* make sure the response header is terminated with an empty line. Some apps really
5900 truly care about that empty line being there for some reason. Just add it to the
5902 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5904 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5905 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5906 if (temp == NULL) goto lend;
5907 lpszRawHeaders = temp;
5910 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5912 heap_free(request->rawHeaders);
5913 request->rawHeaders = lpszRawHeaders;
5914 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5924 heap_free(lpszRawHeaders);
5929 /***********************************************************************
5930 * HTTP_InterpretHttpHeader (internal)
5932 * Parse server response
5936 * Pointer to array of field, value, NULL on success.
5939 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5941 LPWSTR * pTokenPair;
5945 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5947 pszColon = strchrW(buffer, ':');
5948 /* must have two tokens */
5951 HTTP_FreeTokens(pTokenPair);
5953 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5957 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5960 HTTP_FreeTokens(pTokenPair);
5963 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5964 pTokenPair[0][pszColon - buffer] = '\0';
5968 len = strlenW(pszColon);
5969 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5972 HTTP_FreeTokens(pTokenPair);
5975 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5977 strip_spaces(pTokenPair[0]);
5978 strip_spaces(pTokenPair[1]);
5980 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5984 /***********************************************************************
5985 * HTTP_ProcessHeader (internal)
5987 * Stuff header into header tables according to <dwModifier>
5991 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5993 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5995 LPHTTPHEADERW lphttpHdr = NULL;
5997 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5998 DWORD res = ERROR_HTTP_INVALID_HEADER;
6000 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
6002 /* REPLACE wins out over ADD */
6003 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6004 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
6006 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
6009 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
6013 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
6014 return ERROR_HTTP_INVALID_HEADER;
6015 lphttpHdr = &request->custHeaders[index];
6021 hdr.lpszField = (LPWSTR)field;
6022 hdr.lpszValue = (LPWSTR)value;
6023 hdr.wFlags = hdr.wCount = 0;
6025 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6026 hdr.wFlags |= HDR_ISREQUEST;
6028 return HTTP_InsertCustomHeader(request, &hdr);
6030 /* no value to delete */
6031 else return ERROR_SUCCESS;
6033 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6034 lphttpHdr->wFlags |= HDR_ISREQUEST;
6036 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
6038 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6040 HTTP_DeleteCustomHeader( request, index );
6046 hdr.lpszField = (LPWSTR)field;
6047 hdr.lpszValue = (LPWSTR)value;
6048 hdr.wFlags = hdr.wCount = 0;
6050 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6051 hdr.wFlags |= HDR_ISREQUEST;
6053 return HTTP_InsertCustomHeader(request, &hdr);
6056 return ERROR_SUCCESS;
6058 else if (dwModifier & COALESCEFLAGS)
6063 INT origlen = strlenW(lphttpHdr->lpszValue);
6064 INT valuelen = strlenW(value);
6066 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6069 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6071 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6074 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6077 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6079 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6082 lphttpHdr->lpszValue = lpsztmp;
6083 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6086 lphttpHdr->lpszValue[origlen] = ch;
6088 lphttpHdr->lpszValue[origlen] = ' ';
6092 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6093 lphttpHdr->lpszValue[len] = '\0';
6094 res = ERROR_SUCCESS;
6098 WARN("heap_realloc (%d bytes) failed\n",len+1);
6099 res = ERROR_OUTOFMEMORY;
6102 TRACE("<-- %d\n", res);
6106 /***********************************************************************
6107 * HTTP_GetCustomHeaderIndex (internal)
6109 * Return index of custom header from header array
6112 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6113 int requested_index, BOOL request_only)
6117 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6119 for (index = 0; index < request->nCustHeaders; index++)
6121 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6124 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6127 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6130 if (requested_index == 0)
6135 if (index >= request->nCustHeaders)
6138 TRACE("Return: %d\n", index);
6143 /***********************************************************************
6144 * HTTP_InsertCustomHeader (internal)
6146 * Insert header into array
6149 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6152 LPHTTPHEADERW lph = NULL;
6154 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6155 count = request->nCustHeaders + 1;
6157 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6159 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6162 return ERROR_OUTOFMEMORY;
6164 request->custHeaders = lph;
6165 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6166 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6167 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6168 request->custHeaders[count-1].wCount= lpHdr->wCount;
6169 request->nCustHeaders++;
6171 return ERROR_SUCCESS;
6175 /***********************************************************************
6176 * HTTP_DeleteCustomHeader (internal)
6178 * Delete header from array
6179 * If this function is called, the indexs may change.
6181 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6183 if( request->nCustHeaders <= 0 )
6185 if( index >= request->nCustHeaders )
6187 request->nCustHeaders--;
6189 heap_free(request->custHeaders[index].lpszField);
6190 heap_free(request->custHeaders[index].lpszValue);
6192 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6193 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6194 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6200 /***********************************************************************
6201 * HTTP_VerifyValidHeader (internal)
6203 * Verify the given header is not invalid for the given http request
6206 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6208 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6209 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6210 return ERROR_HTTP_INVALID_HEADER;
6212 return ERROR_SUCCESS;
6215 /***********************************************************************
6216 * IsHostInProxyBypassList (@)
6221 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6223 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6227 /***********************************************************************
6228 * InternetShowSecurityInfoByURLA (@)
6230 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6232 FIXME("stub: %s %p\n", url, window);
6236 /***********************************************************************
6237 * InternetShowSecurityInfoByURLW (@)
6239 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6241 FIXME("stub: %s %p\n", debugstr_w(url), window);
6245 /***********************************************************************
6246 * ShowX509EncodedCertificate (@)
6248 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6250 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6256 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6258 memset(&view, 0, sizeof(view));
6259 view.hwndParent = parent;
6260 view.pCertContext = certContext;
6261 if (CryptUIDlgViewCertificateW(&view, NULL))
6262 ret = ERROR_SUCCESS;
6264 ret = GetLastError();
6265 CertFreeCertificateContext(certContext);
6268 ret = GetLastError();