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_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
102 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
103 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
104 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
105 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
106 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
107 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
108 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 };
109 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
110 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
111 static const WCHAR szDate[] = { 'D','a','t','e',0 };
112 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
113 static const WCHAR szETag[] = { 'E','T','a','g',0 };
114 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
115 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
116 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
118 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
119 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
120 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
121 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
122 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
123 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
124 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
125 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
126 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
127 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
128 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
129 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
130 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
131 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
132 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
133 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
134 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
135 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 };
136 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
137 static const WCHAR szURI[] = { 'U','R','I',0 };
138 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
139 static const WCHAR szVary[] = { 'V','a','r','y',0 };
140 static const WCHAR szVia[] = { 'V','i','a',0 };
141 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
142 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define COLLECT_TIME 60000
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
169 unsigned int auth_data_len;
170 BOOL finished; /* finished authenticating */
174 typedef struct _basicAuthorizationData
181 UINT authorizationLen;
182 } basicAuthorizationData;
184 typedef struct _authorizationData
198 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
199 static struct list authorizationCache = LIST_INIT(authorizationCache);
201 static CRITICAL_SECTION authcache_cs;
202 static CRITICAL_SECTION_DEBUG critsect_debug =
205 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
206 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
208 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
210 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
211 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
212 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
213 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
214 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
215 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
216 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
217 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
218 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
219 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
220 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
222 static CRITICAL_SECTION connection_pool_cs;
223 static CRITICAL_SECTION_DEBUG connection_pool_debug =
225 0, 0, &connection_pool_cs,
226 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
227 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
229 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
231 static struct list connection_pool = LIST_INIT(connection_pool);
232 static BOOL collector_running;
234 void server_addref(server_t *server)
236 InterlockedIncrement(&server->ref);
239 void server_release(server_t *server)
241 if(InterlockedDecrement(&server->ref))
245 server->keep_until = GetTickCount64() + COLLECT_TIME;
248 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
250 server_t *iter, *server = NULL;
252 EnterCriticalSection(&connection_pool_cs);
254 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
255 if(iter->port == port && !strcmpW(iter->name, name)) {
257 server_addref(server);
263 server = heap_alloc(sizeof(*server));
265 server->addr_len = 0;
268 list_init(&server->conn_pool);
269 server->name = heap_strdupW(name);
271 list_add_head(&connection_pool, &server->entry);
279 LeaveCriticalSection(&connection_pool_cs);
284 BOOL collect_connections(BOOL collect_all)
286 netconn_t *netconn, *netconn_safe;
287 server_t *server, *server_safe;
288 BOOL remaining = FALSE;
291 now = GetTickCount64();
293 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
294 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
295 if(collect_all || netconn->keep_until < now) {
296 TRACE("freeing %p\n", netconn);
297 list_remove(&netconn->pool_entry);
298 free_netconn(netconn);
305 if(collect_all || server->keep_until < now) {
306 list_remove(&server->entry);
308 heap_free(server->name);
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(FALSE);
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_SYSTEM_DEFAULT, 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->session->serverName : 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 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1701 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1702 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1704 memset( &UrlComponents, 0, sizeof UrlComponents );
1705 UrlComponents.dwStructSize = sizeof UrlComponents;
1706 UrlComponents.lpszHostName = buf;
1707 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1709 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1711 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1712 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1713 sprintfW(proxy, szFormat, protoProxy);
1715 strcpyW(proxy, protoProxy);
1716 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1718 if( UrlComponents.dwHostNameLength == 0 )
1721 if( !request->path )
1722 request->path = szNul;
1724 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1725 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1727 heap_free(session->serverName);
1728 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1729 session->serverPort = UrlComponents.nPort;
1731 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1735 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
1740 if(server->addr_len)
1741 return ERROR_SUCCESS;
1743 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1744 INTERNET_STATUS_RESOLVING_NAME,
1746 (strlenW(server->name)+1) * sizeof(WCHAR));
1748 addr_len = sizeof(server->addr);
1749 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1750 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1752 switch(server->addr.ss_family) {
1754 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1757 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1760 WARN("unsupported family %d\n", server->addr.ss_family);
1761 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1764 server->addr_len = addr_len;
1765 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1766 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1767 INTERNET_STATUS_NAME_RESOLVED,
1768 server->addr_str, strlen(server->addr_str)+1);
1770 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1771 return ERROR_SUCCESS;
1774 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1776 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1777 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1778 static const WCHAR slash[] = { '/',0 };
1779 LPHTTPHEADERW host_header;
1782 host_header = HTTP_GetHeader(req, hostW);
1786 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1790 strcpyW(buf, scheme);
1791 strcatW(buf, host_header->lpszValue);
1792 if (req->path[0] != '/')
1793 strcatW(buf, slash);
1794 strcatW(buf, req->path);
1799 /***********************************************************************
1800 * HTTPREQ_Destroy (internal)
1802 * Deallocate request handle
1805 static void HTTPREQ_Destroy(object_header_t *hdr)
1807 http_request_t *request = (http_request_t*) hdr;
1812 if(request->hCacheFile) {
1813 WCHAR url[INTERNET_MAX_URL_LENGTH];
1815 CloseHandle(request->hCacheFile);
1817 if(HTTP_GetRequestURL(request, url)) {
1820 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1821 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1822 request->last_modified, NORMAL_CACHE_ENTRY,
1823 request->rawHeaders, headersLen, NULL, 0);
1826 heap_free(request->cacheFile);
1828 DeleteCriticalSection( &request->read_section );
1829 WININET_Release(&request->session->hdr);
1831 destroy_authinfo(request->authInfo);
1832 destroy_authinfo(request->proxyAuthInfo);
1834 heap_free(request->path);
1835 heap_free(request->verb);
1836 heap_free(request->rawHeaders);
1837 heap_free(request->version);
1838 heap_free(request->statusText);
1840 for (i = 0; i < request->nCustHeaders; i++)
1842 heap_free(request->custHeaders[i].lpszField);
1843 heap_free(request->custHeaders[i].lpszValue);
1845 destroy_data_stream(request->data_stream);
1846 heap_free(request->custHeaders);
1849 static void http_release_netconn(http_request_t *req, BOOL reuse)
1851 TRACE("%p %p\n",req, req->netconn);
1856 if(reuse && req->netconn->keep_alive) {
1859 EnterCriticalSection(&connection_pool_cs);
1861 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1862 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1863 req->netconn = NULL;
1865 run_collector = !collector_running;
1866 collector_running = TRUE;
1868 LeaveCriticalSection(&connection_pool_cs);
1871 HANDLE thread = NULL;
1874 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1876 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1878 EnterCriticalSection(&connection_pool_cs);
1879 collector_running = FALSE;
1880 LeaveCriticalSection(&connection_pool_cs);
1883 FreeLibrary(module);
1889 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1890 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1892 free_netconn(req->netconn);
1893 req->netconn = NULL;
1895 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1896 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1899 static void drain_content(http_request_t *req)
1903 if (!req->netconn) return;
1905 if (req->contentLength == -1)
1907 else if(!strcmpW(req->verb, szHEAD))
1910 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1912 http_release_netconn(req, try_reuse);
1915 static BOOL HTTP_KeepAlive(http_request_t *request)
1917 WCHAR szVersion[10];
1918 WCHAR szConnectionResponse[20];
1919 DWORD dwBufferSize = sizeof(szVersion);
1920 BOOL keepalive = FALSE;
1922 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1923 * the connection is keep-alive by default */
1924 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1925 && !strcmpiW(szVersion, g_szHttp1_1))
1930 dwBufferSize = sizeof(szConnectionResponse);
1931 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1932 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1934 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1940 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1942 http_request_t *req = (http_request_t*)hdr;
1947 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1949 http_request_t *req = (http_request_t*)hdr;
1952 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1954 http_session_t *session = req->session;
1955 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1957 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1959 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1960 return ERROR_INSUFFICIENT_BUFFER;
1961 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1962 /* FIXME: can't get a SOCKET from our connection since we don't use
1966 /* FIXME: get source port from req->netConnection */
1967 info->SourcePort = 0;
1968 info->DestPort = session->hostPort;
1970 if (HTTP_KeepAlive(req))
1971 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1972 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1973 info->Flags |= IDSI_FLAG_PROXY;
1974 if (req->netconn->useSSL)
1975 info->Flags |= IDSI_FLAG_SECURE;
1977 return ERROR_SUCCESS;
1980 case INTERNET_OPTION_SECURITY_FLAGS:
1984 if (*size < sizeof(ULONG))
1985 return ERROR_INSUFFICIENT_BUFFER;
1987 *size = sizeof(DWORD);
1989 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1990 flags |= SECURITY_FLAG_SECURE;
1991 flags |= req->security_flags;
1993 int bits = NETCON_GetCipherStrength(req->netconn);
1995 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1996 else if (bits >= 56)
1997 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1999 flags |= SECURITY_FLAG_STRENGTH_WEAK;
2001 *(DWORD *)buffer = flags;
2002 return ERROR_SUCCESS;
2005 case INTERNET_OPTION_HANDLE_TYPE:
2006 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2008 if (*size < sizeof(ULONG))
2009 return ERROR_INSUFFICIENT_BUFFER;
2011 *size = sizeof(DWORD);
2012 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2013 return ERROR_SUCCESS;
2015 case INTERNET_OPTION_URL: {
2016 WCHAR url[INTERNET_MAX_URL_LENGTH];
2021 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2023 TRACE("INTERNET_OPTION_URL\n");
2025 host = HTTP_GetHeader(req, hostW);
2026 strcpyW(url, httpW);
2027 strcatW(url, host->lpszValue);
2028 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2030 strcatW(url, req->path);
2032 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2035 len = (strlenW(url)+1) * sizeof(WCHAR);
2037 return ERROR_INSUFFICIENT_BUFFER;
2040 strcpyW(buffer, url);
2041 return ERROR_SUCCESS;
2043 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2045 return ERROR_INSUFFICIENT_BUFFER;
2048 return ERROR_SUCCESS;
2052 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2053 INTERNET_CACHE_ENTRY_INFOW *info;
2054 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2055 WCHAR url[INTERNET_MAX_URL_LENGTH];
2056 DWORD nbytes, error;
2059 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2061 if (*size < sizeof(*ts))
2063 *size = sizeof(*ts);
2064 return ERROR_INSUFFICIENT_BUFFER;
2067 HTTP_GetRequestURL(req, url);
2068 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2069 error = GetLastError();
2070 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2072 if (!(info = heap_alloc(nbytes)))
2073 return ERROR_OUTOFMEMORY;
2075 GetUrlCacheEntryInfoW(url, info, &nbytes);
2077 ts->ftExpires = info->ExpireTime;
2078 ts->ftLastModified = info->LastModifiedTime;
2081 *size = sizeof(*ts);
2082 return ERROR_SUCCESS;
2087 case INTERNET_OPTION_DATAFILE_NAME: {
2090 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2092 if(!req->cacheFile) {
2094 return ERROR_INTERNET_ITEM_NOT_FOUND;
2098 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2099 if(*size < req_size)
2100 return ERROR_INSUFFICIENT_BUFFER;
2103 memcpy(buffer, req->cacheFile, *size);
2104 return ERROR_SUCCESS;
2106 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2107 if (req_size > *size)
2108 return ERROR_INSUFFICIENT_BUFFER;
2110 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2111 -1, buffer, *size, NULL, NULL);
2112 return ERROR_SUCCESS;
2116 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2117 PCCERT_CONTEXT context;
2119 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2120 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2121 return ERROR_INSUFFICIENT_BUFFER;
2124 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2126 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2129 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2130 info->ftExpiry = context->pCertInfo->NotAfter;
2131 info->ftStart = context->pCertInfo->NotBefore;
2132 len = CertNameToStrA(context->dwCertEncodingType,
2133 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2134 info->lpszSubjectInfo = LocalAlloc(0, len);
2135 if(info->lpszSubjectInfo)
2136 CertNameToStrA(context->dwCertEncodingType,
2137 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2138 info->lpszSubjectInfo, len);
2139 len = CertNameToStrA(context->dwCertEncodingType,
2140 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2141 info->lpszIssuerInfo = LocalAlloc(0, len);
2142 if(info->lpszIssuerInfo)
2143 CertNameToStrA(context->dwCertEncodingType,
2144 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2145 info->lpszIssuerInfo, len);
2146 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2147 CertFreeCertificateContext(context);
2148 return ERROR_SUCCESS;
2153 return INET_QueryOption(hdr, option, buffer, size, unicode);
2156 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2158 http_request_t *req = (http_request_t*)hdr;
2161 case INTERNET_OPTION_SECURITY_FLAGS:
2165 if (!buffer || size != sizeof(DWORD))
2166 return ERROR_INVALID_PARAMETER;
2167 flags = *(DWORD *)buffer;
2168 TRACE("%08x\n", flags);
2169 req->security_flags = flags;
2171 req->netconn->security_flags = flags;
2172 return ERROR_SUCCESS;
2174 case INTERNET_OPTION_SEND_TIMEOUT:
2175 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2176 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2178 if (size != sizeof(DWORD))
2179 return ERROR_INVALID_PARAMETER;
2182 FIXME("unsupported without active connection\n");
2183 return ERROR_SUCCESS;
2186 return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
2189 case INTERNET_OPTION_USERNAME:
2190 heap_free(req->session->userName);
2191 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2192 return ERROR_SUCCESS;
2194 case INTERNET_OPTION_PASSWORD:
2195 heap_free(req->session->password);
2196 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2197 return ERROR_SUCCESS;
2198 case INTERNET_OPTION_HTTP_DECODING:
2199 if(size != sizeof(BOOL))
2200 return ERROR_INVALID_PARAMETER;
2201 req->decoding = *(BOOL*)buffer;
2202 return ERROR_SUCCESS;
2205 return ERROR_INTERNET_INVALID_OPTION;
2208 /* read some more data into the read buffer (the read section must be held) */
2209 static DWORD read_more_data( http_request_t *req, int maxlen )
2216 /* move existing data to the start of the buffer */
2218 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2222 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2224 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2225 maxlen - req->read_size, 0, &len );
2226 if(res == ERROR_SUCCESS)
2227 req->read_size += len;
2232 /* remove some amount of data from the read buffer (the read section must be held) */
2233 static void remove_data( http_request_t *req, int count )
2235 if (!(req->read_size -= count)) req->read_pos = 0;
2236 else req->read_pos += count;
2239 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2241 int count, bytes_read, pos = 0;
2244 EnterCriticalSection( &req->read_section );
2247 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2251 count = eol - (req->read_buf + req->read_pos);
2252 bytes_read = count + 1;
2254 else count = bytes_read = req->read_size;
2256 count = min( count, *len - pos );
2257 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2259 remove_data( req, bytes_read );
2262 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2265 TRACE( "returning empty string %u\n", res);
2266 LeaveCriticalSection( &req->read_section );
2267 INTERNET_SetLastError(res);
2271 LeaveCriticalSection( &req->read_section );
2275 if (pos && buffer[pos - 1] == '\r') pos--;
2278 buffer[*len - 1] = 0;
2279 TRACE( "returning %s\n", debugstr_a(buffer));
2283 /* check if we have reached the end of the data to read (the read section must be held) */
2284 static BOOL end_of_read_data( http_request_t *req )
2286 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2289 /* fetch some more data into the read buffer (the read section must be held) */
2290 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2294 if(req->read_size == sizeof(req->read_buf))
2295 return ERROR_SUCCESS;
2299 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2303 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2304 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2305 req->read_size += read;
2307 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2313 /* return the size of data available to be read immediately (the read section must be held) */
2314 static DWORD get_avail_data( http_request_t *req )
2316 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2319 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2321 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2325 NETCON_query_data_available(req->netconn, &avail);
2326 return netconn_stream->content_length == ~0u
2328 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2331 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2333 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2334 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2337 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2338 DWORD *read, read_mode_t read_mode)
2340 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2343 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2345 if(read_mode == READMODE_NOBLOCK)
2346 size = min(size, netconn_get_avail_data(stream, req));
2348 if(size && req->netconn) {
2349 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2352 netconn_stream->content_length = netconn_stream->content_read;
2355 netconn_stream->content_read += *read = len;
2356 TRACE("read %u bytes\n", len);
2357 return ERROR_SUCCESS;
2360 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2362 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2367 if(netconn_end_of_data(stream, req))
2371 avail = netconn_get_avail_data(stream, req);
2375 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2378 netconn_stream->content_read += len;
2379 }while(netconn_stream->content_read < netconn_stream->content_length);
2384 static void netconn_destroy(data_stream_t *stream)
2388 static const data_stream_vtbl_t netconn_stream_vtbl = {
2389 netconn_get_avail_data,
2390 netconn_end_of_data,
2392 netconn_drain_content,
2396 /* read some more data into the read buffer (the read section must be held) */
2397 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2402 if (stream->buf_pos)
2404 /* move existing data to the start of the buffer */
2405 if(stream->buf_size)
2406 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2407 stream->buf_pos = 0;
2410 if (maxlen == -1) maxlen = sizeof(stream->buf);
2412 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2413 maxlen - stream->buf_size, 0, &len );
2414 if(res == ERROR_SUCCESS)
2415 stream->buf_size += len;
2420 /* remove some amount of data from the read buffer (the read section must be held) */
2421 static void remove_chunked_data(chunked_stream_t *stream, int count)
2423 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2424 else stream->buf_pos += count;
2427 /* discard data contents until we reach end of line (the read section must be held) */
2428 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2434 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2437 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2440 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2441 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2442 } while (stream->buf_size);
2443 return ERROR_SUCCESS;
2446 /* read the size of the next chunk (the read section must be held) */
2447 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2450 DWORD chunk_size = 0, res;
2452 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2457 while (stream->buf_size)
2459 char ch = stream->buf[stream->buf_pos];
2460 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2461 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2462 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2463 else if (ch == ';' || ch == '\r' || ch == '\n')
2465 TRACE( "reading %u byte chunk\n", chunk_size );
2466 stream->chunk_size = chunk_size;
2467 req->contentLength += chunk_size;
2468 return discard_chunked_eol(stream, req);
2470 remove_chunked_data(stream, 1);
2472 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2473 if (!stream->buf_size)
2475 stream->chunk_size = 0;
2476 return ERROR_SUCCESS;
2481 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2483 /* Allow reading only from read buffer */
2487 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2489 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2490 return !chunked_stream->chunk_size;
2493 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2494 DWORD *read, read_mode_t read_mode)
2496 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2497 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2499 if(chunked_stream->chunk_size == ~0u) {
2500 res = start_next_chunk(chunked_stream, req);
2501 if(res != ERROR_SUCCESS)
2505 while(size && chunked_stream->chunk_size) {
2506 if(chunked_stream->buf_size) {
2507 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2509 /* this could block */
2510 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2513 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2514 remove_chunked_data(chunked_stream, read_bytes);
2516 read_bytes = min(size, chunked_stream->chunk_size);
2518 if(read_mode == READMODE_NOBLOCK) {
2521 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2523 if(read_bytes > avail)
2526 /* this could block */
2527 if(read_bytes == chunked_stream->chunk_size)
2531 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2532 if(res != ERROR_SUCCESS)
2536 chunked_stream->chunk_size -= read_bytes;
2538 ret_read += read_bytes;
2539 if(!chunked_stream->chunk_size) {
2540 assert(read_mode != READMODE_NOBLOCK);
2541 res = start_next_chunk(chunked_stream, req);
2542 if(res != ERROR_SUCCESS)
2546 if(read_mode == READMODE_ASYNC)
2547 read_mode = READMODE_NOBLOCK;
2550 TRACE("read %u bytes\n", ret_read);
2555 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2557 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2559 /* FIXME: we can do better */
2560 return !chunked_stream->chunk_size;
2563 static void chunked_destroy(data_stream_t *stream)
2565 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2566 heap_free(chunked_stream);
2569 static const data_stream_vtbl_t chunked_stream_vtbl = {
2570 chunked_get_avail_data,
2571 chunked_end_of_data,
2573 chunked_drain_content,
2577 /* set the request content length based on the headers */
2578 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2580 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2584 if(status_code == HTTP_STATUS_NO_CONTENT) {
2585 request->contentLength = request->netconn_stream.content_length = 0;
2586 return ERROR_SUCCESS;
2589 size = sizeof(request->contentLength);
2590 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2591 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2592 request->contentLength = ~0u;
2593 request->netconn_stream.content_length = request->contentLength;
2594 request->netconn_stream.content_read = request->read_size;
2596 size = sizeof(encoding);
2597 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2598 !strcmpiW(encoding, szChunked))
2600 chunked_stream_t *chunked_stream;
2602 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2604 return ERROR_OUTOFMEMORY;
2606 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2607 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2608 chunked_stream->chunk_size = ~0u;
2610 if(request->read_size) {
2611 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2612 chunked_stream->buf_size = request->read_size;
2613 request->read_size = request->read_pos = 0;
2616 request->data_stream = &chunked_stream->data_stream;
2617 request->contentLength = ~0u;
2618 request->read_chunked = TRUE;
2621 if(request->decoding) {
2624 static const WCHAR gzipW[] = {'g','z','i','p',0};
2626 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2627 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2628 return init_gzip_stream(request);
2631 return ERROR_SUCCESS;
2634 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2636 INTERNET_ASYNC_RESULT iar;
2638 iar.dwResult = result;
2639 iar.dwError = error;
2641 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2642 sizeof(INTERNET_ASYNC_RESULT));
2645 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2647 DWORD res, read = 0, avail = 0;
2652 EnterCriticalSection( &req->read_section );
2654 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2655 res = refill_read_buffer(req, mode, &read);
2656 if(res == ERROR_SUCCESS && !first_notif)
2657 avail = get_avail_data(req);
2659 LeaveCriticalSection( &req->read_section );
2661 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2662 WARN("res %u read %u, closing connection\n", res, read);
2663 http_release_netconn(req, FALSE);
2666 if(res == ERROR_SUCCESS)
2667 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2669 send_request_complete(req, 0, res);
2672 /* read data from the http connection (the read section must be held) */
2673 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2675 DWORD current_read = 0, ret_read = 0;
2676 read_mode_t read_mode;
2677 DWORD res = ERROR_SUCCESS;
2679 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2681 EnterCriticalSection( &req->read_section );
2683 if(req->read_size) {
2684 ret_read = min(size, req->read_size);
2685 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2686 req->read_size -= ret_read;
2687 req->read_pos += ret_read;
2688 if(read_mode == READMODE_ASYNC)
2689 read_mode = READMODE_NOBLOCK;
2692 if(ret_read < size) {
2693 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2694 ret_read += current_read;
2697 LeaveCriticalSection( &req->read_section );
2700 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2702 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2706 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2708 WARN("WriteFile failed: %u\n", GetLastError());
2711 if(size && !ret_read)
2712 http_release_netconn(req, res == ERROR_SUCCESS);
2718 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2720 http_request_t *req = (http_request_t*)hdr;
2723 EnterCriticalSection( &req->read_section );
2724 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2725 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2727 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2728 if(res == ERROR_SUCCESS)
2730 LeaveCriticalSection( &req->read_section );
2735 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2737 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2738 http_request_t *req = (http_request_t*)workRequest->hdr;
2741 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2743 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2744 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2746 send_request_complete(req, res == ERROR_SUCCESS, res);
2749 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2750 DWORD flags, DWORD_PTR context)
2752 http_request_t *req = (http_request_t*)hdr;
2753 DWORD res, size, read, error = ERROR_SUCCESS;
2755 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2756 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2758 if (buffers->dwStructSize != sizeof(*buffers))
2759 return ERROR_INVALID_PARAMETER;
2761 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2763 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2765 WORKREQUEST workRequest;
2767 if (TryEnterCriticalSection( &req->read_section ))
2769 if (get_avail_data(req))
2771 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2772 &buffers->dwBufferLength, FALSE);
2773 size = buffers->dwBufferLength;
2774 LeaveCriticalSection( &req->read_section );
2777 LeaveCriticalSection( &req->read_section );
2780 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2781 workRequest.hdr = WININET_AddRef(&req->hdr);
2782 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2784 INTERNET_AsyncCall(&workRequest);
2786 return ERROR_IO_PENDING;
2790 size = buffers->dwBufferLength;
2792 EnterCriticalSection( &req->read_section );
2793 if(hdr->dwError == ERROR_SUCCESS)
2794 hdr->dwError = INTERNET_HANDLE_IN_USE;
2795 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2796 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2799 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2800 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2801 if(res != ERROR_SUCCESS)
2804 read += buffers->dwBufferLength;
2805 if(read == size || end_of_read_data(req))
2808 LeaveCriticalSection( &req->read_section );
2810 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2811 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2812 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2813 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2815 EnterCriticalSection( &req->read_section );
2818 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2819 hdr->dwError = ERROR_SUCCESS;
2821 error = hdr->dwError;
2823 LeaveCriticalSection( &req->read_section );
2824 size = buffers->dwBufferLength;
2825 buffers->dwBufferLength = read;
2828 if (res == ERROR_SUCCESS) {
2829 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2830 &size, sizeof(size));
2833 return res==ERROR_SUCCESS ? error : res;
2836 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2838 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2839 http_request_t *req = (http_request_t*)workRequest->hdr;
2842 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2844 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2845 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2847 send_request_complete(req, res == ERROR_SUCCESS, res);
2850 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2851 DWORD flags, DWORD_PTR context)
2854 http_request_t *req = (http_request_t*)hdr;
2855 DWORD res, size, read, error = ERROR_SUCCESS;
2857 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2858 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2860 if (buffers->dwStructSize != sizeof(*buffers))
2861 return ERROR_INVALID_PARAMETER;
2863 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2865 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2867 WORKREQUEST workRequest;
2869 if (TryEnterCriticalSection( &req->read_section ))
2871 if (get_avail_data(req))
2873 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2874 &buffers->dwBufferLength, FALSE);
2875 size = buffers->dwBufferLength;
2876 LeaveCriticalSection( &req->read_section );
2879 LeaveCriticalSection( &req->read_section );
2882 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2883 workRequest.hdr = WININET_AddRef(&req->hdr);
2884 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2886 INTERNET_AsyncCall(&workRequest);
2888 return ERROR_IO_PENDING;
2892 size = buffers->dwBufferLength;
2894 EnterCriticalSection( &req->read_section );
2895 if(hdr->dwError == ERROR_SUCCESS)
2896 hdr->dwError = INTERNET_HANDLE_IN_USE;
2897 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2898 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2901 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2902 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2903 if(res != ERROR_SUCCESS)
2906 read += buffers->dwBufferLength;
2907 if(read == size || end_of_read_data(req))
2910 LeaveCriticalSection( &req->read_section );
2912 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2913 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2914 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2915 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2917 EnterCriticalSection( &req->read_section );
2920 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2921 hdr->dwError = ERROR_SUCCESS;
2923 error = hdr->dwError;
2925 LeaveCriticalSection( &req->read_section );
2926 size = buffers->dwBufferLength;
2927 buffers->dwBufferLength = read;
2930 if (res == ERROR_SUCCESS) {
2931 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2932 &size, sizeof(size));
2935 return res==ERROR_SUCCESS ? error : res;
2938 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2941 http_request_t *request = (http_request_t*)hdr;
2943 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2946 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2947 if (res == ERROR_SUCCESS)
2948 request->bytesWritten += *written;
2950 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2954 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2956 http_request_t *req = (http_request_t*)workRequest->hdr;
2958 HTTP_ReceiveRequestData(req, FALSE);
2961 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2963 http_request_t *req = (http_request_t*)hdr;
2965 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2967 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2969 WORKREQUEST workRequest;
2971 /* never wait, if we can't enter the section we queue an async request right away */
2972 if (TryEnterCriticalSection( &req->read_section ))
2974 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2975 if ((*available = get_avail_data( req ))) goto done;
2976 if (end_of_read_data( req )) goto done;
2977 LeaveCriticalSection( &req->read_section );
2980 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2981 workRequest.hdr = WININET_AddRef( &req->hdr );
2983 INTERNET_AsyncCall(&workRequest);
2985 return ERROR_IO_PENDING;
2988 EnterCriticalSection( &req->read_section );
2990 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2992 refill_read_buffer( req, READMODE_ASYNC, NULL );
2993 *available = get_avail_data( req );
2997 LeaveCriticalSection( &req->read_section );
2999 TRACE( "returning %u\n", *available );
3000 return ERROR_SUCCESS;
3003 static const object_vtbl_t HTTPREQVtbl = {
3005 HTTPREQ_CloseConnection,
3006 HTTPREQ_QueryOption,
3009 HTTPREQ_ReadFileExA,
3010 HTTPREQ_ReadFileExW,
3012 HTTPREQ_QueryDataAvailable,
3016 /***********************************************************************
3017 * HTTP_HttpOpenRequestW (internal)
3019 * Open a HTTP request handle
3022 * HINTERNET a HTTP request handle on success
3026 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3027 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3028 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3029 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3031 appinfo_t *hIC = session->appInfo;
3032 http_request_t *request;
3033 DWORD len, res = ERROR_SUCCESS;
3037 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3039 return ERROR_OUTOFMEMORY;
3041 request->hdr.htype = WH_HHTTPREQ;
3042 request->hdr.dwFlags = dwFlags;
3043 request->hdr.dwContext = dwContext;
3044 request->contentLength = ~0u;
3046 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3047 request->data_stream = &request->netconn_stream.data_stream;
3049 InitializeCriticalSection( &request->read_section );
3051 WININET_AddRef( &session->hdr );
3052 request->session = session;
3053 list_add_head( &session->hdr.children, &request->hdr.entry );
3055 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3056 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3057 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3058 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3060 if (lpszObjectName && *lpszObjectName) {
3064 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3065 if (rc != E_POINTER)
3066 len = strlenW(lpszObjectName)+1;
3067 request->path = heap_alloc(len*sizeof(WCHAR));
3068 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3069 URL_ESCAPE_SPACES_ONLY);
3072 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3073 strcpyW(request->path,lpszObjectName);
3076 static const WCHAR slashW[] = {'/',0};
3078 request->path = heap_strdupW(slashW);
3081 if (lpszReferrer && *lpszReferrer)
3082 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3084 if (lpszAcceptTypes)
3087 for (i = 0; lpszAcceptTypes[i]; i++)
3089 if (!*lpszAcceptTypes[i]) continue;
3090 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3091 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3092 HTTP_ADDHDR_FLAG_REQ |
3093 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3097 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3098 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3100 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3101 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3102 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3106 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3108 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3110 res = ERROR_OUTOFMEMORY;
3114 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3115 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3116 heap_free(host_name);
3119 HTTP_ProcessHeader(request, hostW, session->hostName,
3120 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3122 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3123 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3124 INTERNET_DEFAULT_HTTPS_PORT :
3125 INTERNET_DEFAULT_HTTP_PORT);
3127 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3128 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3129 INTERNET_DEFAULT_HTTPS_PORT :
3130 INTERNET_DEFAULT_HTTP_PORT);
3132 if (hIC->proxy && hIC->proxy[0])
3133 HTTP_DealWithProxy( hIC, session, request );
3135 INTERNET_SendCallback(&session->hdr, dwContext,
3136 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3140 TRACE("<-- %u (%p)\n", res, request);
3142 if(res != ERROR_SUCCESS) {
3143 WININET_Release( &request->hdr );
3148 *ret = request->hdr.hInternet;
3149 return ERROR_SUCCESS;
3152 /***********************************************************************
3153 * HttpOpenRequestW (WININET.@)
3155 * Open a HTTP request handle
3158 * HINTERNET a HTTP request handle on success
3162 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3163 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3164 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3165 DWORD dwFlags, DWORD_PTR dwContext)
3167 http_session_t *session;
3168 HINTERNET handle = NULL;
3171 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3172 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3173 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3174 dwFlags, dwContext);
3175 if(lpszAcceptTypes!=NULL)
3178 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3179 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3182 session = (http_session_t*) get_handle_object( hHttpSession );
3183 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3185 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3190 * My tests seem to show that the windows version does not
3191 * become asynchronous until after this point. And anyhow
3192 * if this call was asynchronous then how would you get the
3193 * necessary HINTERNET pointer returned by this function.
3196 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3197 lpszVersion, lpszReferrer, lpszAcceptTypes,
3198 dwFlags, dwContext, &handle);
3201 WININET_Release( &session->hdr );
3202 TRACE("returning %p\n", handle);
3203 if(res != ERROR_SUCCESS)
3208 static const LPCWSTR header_lookup[] = {
3209 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3210 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3211 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3212 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3213 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3214 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3215 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3216 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3217 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3218 szDate, /* HTTP_QUERY_DATE = 9 */
3219 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3220 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3221 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3222 szURI, /* HTTP_QUERY_URI = 13 */
3223 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3224 NULL, /* HTTP_QUERY_COST = 15 */
3225 NULL, /* HTTP_QUERY_LINK = 16 */
3226 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3227 NULL, /* HTTP_QUERY_VERSION = 18 */
3228 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3229 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3230 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3231 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3232 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3233 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3234 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3235 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3236 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3237 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3238 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3239 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3240 NULL, /* HTTP_QUERY_FROM = 31 */
3241 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3242 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3243 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3244 szReferer, /* HTTP_QUERY_REFERER = 35 */
3245 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3246 szServer, /* HTTP_QUERY_SERVER = 37 */
3247 NULL, /* HTTP_TITLE = 38 */
3248 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3249 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3250 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3251 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3252 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3253 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3254 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3255 NULL, /* HTTP_QUERY_REFRESH = 46 */
3256 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3257 szAge, /* HTTP_QUERY_AGE = 48 */
3258 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3259 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3260 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3261 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3262 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3263 szETag, /* HTTP_QUERY_ETAG = 54 */
3264 hostW, /* HTTP_QUERY_HOST = 55 */
3265 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3266 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3267 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3268 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3269 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3270 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3271 szRange, /* HTTP_QUERY_RANGE = 62 */
3272 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3273 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3274 szVary, /* HTTP_QUERY_VARY = 65 */
3275 szVia, /* HTTP_QUERY_VIA = 66 */
3276 szWarning, /* HTTP_QUERY_WARNING = 67 */
3277 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3278 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3279 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3282 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3284 /***********************************************************************
3285 * HTTP_HttpQueryInfoW (internal)
3287 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3288 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3290 LPHTTPHEADERW lphttpHdr = NULL;
3291 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3292 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3293 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3296 /* Find requested header structure */
3299 case HTTP_QUERY_CUSTOM:
3300 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3301 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3303 case HTTP_QUERY_RAW_HEADERS_CRLF:
3307 DWORD res = ERROR_INVALID_PARAMETER;
3310 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3312 headers = request->rawHeaders;
3315 len = strlenW(headers) * sizeof(WCHAR);
3317 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3319 len += sizeof(WCHAR);
3320 res = ERROR_INSUFFICIENT_BUFFER;
3325 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3328 len = strlenW(szCrLf) * sizeof(WCHAR);
3329 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3331 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3332 res = ERROR_SUCCESS;
3334 *lpdwBufferLength = len;
3336 if (request_only) heap_free(headers);
3339 case HTTP_QUERY_RAW_HEADERS:
3341 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3343 LPWSTR pszString = lpBuffer;
3345 for (i = 0; ppszRawHeaderLines[i]; i++)
3346 size += strlenW(ppszRawHeaderLines[i]) + 1;
3348 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3350 HTTP_FreeTokens(ppszRawHeaderLines);
3351 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3352 return ERROR_INSUFFICIENT_BUFFER;
3356 for (i = 0; ppszRawHeaderLines[i]; i++)
3358 DWORD len = strlenW(ppszRawHeaderLines[i]);
3359 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3363 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3365 *lpdwBufferLength = size * sizeof(WCHAR);
3366 HTTP_FreeTokens(ppszRawHeaderLines);
3368 return ERROR_SUCCESS;
3370 case HTTP_QUERY_STATUS_TEXT:
3371 if (request->statusText)
3373 DWORD len = strlenW(request->statusText);
3374 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3376 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3377 return ERROR_INSUFFICIENT_BUFFER;
3381 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3382 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3384 *lpdwBufferLength = len * sizeof(WCHAR);
3385 return ERROR_SUCCESS;
3388 case HTTP_QUERY_VERSION:
3389 if (request->version)
3391 DWORD len = strlenW(request->version);
3392 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3394 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3395 return ERROR_INSUFFICIENT_BUFFER;
3399 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3400 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3402 *lpdwBufferLength = len * sizeof(WCHAR);
3403 return ERROR_SUCCESS;
3406 case HTTP_QUERY_CONTENT_ENCODING:
3407 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3408 requested_index,request_only);
3411 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3413 if (level < LAST_TABLE_HEADER && header_lookup[level])
3414 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3415 requested_index,request_only);
3419 lphttpHdr = &request->custHeaders[index];
3421 /* Ensure header satisfies requested attributes */
3423 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3424 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3426 return ERROR_HTTP_HEADER_NOT_FOUND;
3429 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3431 /* coalesce value to requested type */
3432 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3434 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3435 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3437 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3443 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3445 tmpTM = *gmtime(&tmpTime);
3446 STHook = (SYSTEMTIME *)lpBuffer;
3447 STHook->wDay = tmpTM.tm_mday;
3448 STHook->wHour = tmpTM.tm_hour;
3449 STHook->wMilliseconds = 0;
3450 STHook->wMinute = tmpTM.tm_min;
3451 STHook->wDayOfWeek = tmpTM.tm_wday;
3452 STHook->wMonth = tmpTM.tm_mon + 1;
3453 STHook->wSecond = tmpTM.tm_sec;
3454 STHook->wYear = tmpTM.tm_year;
3456 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3457 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3458 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3460 else if (lphttpHdr->lpszValue)
3462 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3464 if (len > *lpdwBufferLength)
3466 *lpdwBufferLength = len;
3467 return ERROR_INSUFFICIENT_BUFFER;
3471 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3472 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3474 *lpdwBufferLength = len - sizeof(WCHAR);
3476 return ERROR_SUCCESS;
3479 /***********************************************************************
3480 * HttpQueryInfoW (WININET.@)
3482 * Queries for information about an HTTP request
3489 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3490 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3492 http_request_t *request;
3495 if (TRACE_ON(wininet)) {
3496 #define FE(x) { x, #x }
3497 static const wininet_flag_info query_flags[] = {
3498 FE(HTTP_QUERY_MIME_VERSION),
3499 FE(HTTP_QUERY_CONTENT_TYPE),
3500 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3501 FE(HTTP_QUERY_CONTENT_ID),
3502 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3503 FE(HTTP_QUERY_CONTENT_LENGTH),
3504 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3505 FE(HTTP_QUERY_ALLOW),
3506 FE(HTTP_QUERY_PUBLIC),
3507 FE(HTTP_QUERY_DATE),
3508 FE(HTTP_QUERY_EXPIRES),
3509 FE(HTTP_QUERY_LAST_MODIFIED),
3510 FE(HTTP_QUERY_MESSAGE_ID),
3512 FE(HTTP_QUERY_DERIVED_FROM),
3513 FE(HTTP_QUERY_COST),
3514 FE(HTTP_QUERY_LINK),
3515 FE(HTTP_QUERY_PRAGMA),
3516 FE(HTTP_QUERY_VERSION),
3517 FE(HTTP_QUERY_STATUS_CODE),
3518 FE(HTTP_QUERY_STATUS_TEXT),
3519 FE(HTTP_QUERY_RAW_HEADERS),
3520 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3521 FE(HTTP_QUERY_CONNECTION),
3522 FE(HTTP_QUERY_ACCEPT),
3523 FE(HTTP_QUERY_ACCEPT_CHARSET),
3524 FE(HTTP_QUERY_ACCEPT_ENCODING),
3525 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3526 FE(HTTP_QUERY_AUTHORIZATION),
3527 FE(HTTP_QUERY_CONTENT_ENCODING),
3528 FE(HTTP_QUERY_FORWARDED),
3529 FE(HTTP_QUERY_FROM),
3530 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3531 FE(HTTP_QUERY_LOCATION),
3532 FE(HTTP_QUERY_ORIG_URI),
3533 FE(HTTP_QUERY_REFERER),
3534 FE(HTTP_QUERY_RETRY_AFTER),
3535 FE(HTTP_QUERY_SERVER),
3536 FE(HTTP_QUERY_TITLE),
3537 FE(HTTP_QUERY_USER_AGENT),
3538 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3539 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3540 FE(HTTP_QUERY_ACCEPT_RANGES),
3541 FE(HTTP_QUERY_SET_COOKIE),
3542 FE(HTTP_QUERY_COOKIE),
3543 FE(HTTP_QUERY_REQUEST_METHOD),
3544 FE(HTTP_QUERY_REFRESH),
3545 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3547 FE(HTTP_QUERY_CACHE_CONTROL),
3548 FE(HTTP_QUERY_CONTENT_BASE),
3549 FE(HTTP_QUERY_CONTENT_LOCATION),
3550 FE(HTTP_QUERY_CONTENT_MD5),
3551 FE(HTTP_QUERY_CONTENT_RANGE),
3552 FE(HTTP_QUERY_ETAG),
3553 FE(HTTP_QUERY_HOST),
3554 FE(HTTP_QUERY_IF_MATCH),
3555 FE(HTTP_QUERY_IF_NONE_MATCH),
3556 FE(HTTP_QUERY_IF_RANGE),
3557 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3558 FE(HTTP_QUERY_MAX_FORWARDS),
3559 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3560 FE(HTTP_QUERY_RANGE),
3561 FE(HTTP_QUERY_TRANSFER_ENCODING),
3562 FE(HTTP_QUERY_UPGRADE),
3563 FE(HTTP_QUERY_VARY),
3565 FE(HTTP_QUERY_WARNING),
3566 FE(HTTP_QUERY_CUSTOM)
3568 static const wininet_flag_info modifier_flags[] = {
3569 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3570 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3571 FE(HTTP_QUERY_FLAG_NUMBER),
3572 FE(HTTP_QUERY_FLAG_COALESCE)
3575 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3576 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3579 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3580 TRACE(" Attribute:");
3581 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3582 if (query_flags[i].val == info) {
3583 TRACE(" %s", query_flags[i].name);
3587 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3588 TRACE(" Unknown (%08x)", info);
3591 TRACE(" Modifier:");
3592 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3593 if (modifier_flags[i].val & info_mod) {
3594 TRACE(" %s", modifier_flags[i].name);
3595 info_mod &= ~ modifier_flags[i].val;
3600 TRACE(" Unknown (%08x)", info_mod);
3605 request = (http_request_t*) get_handle_object( hHttpRequest );
3606 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3608 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3612 if (lpBuffer == NULL)
3613 *lpdwBufferLength = 0;
3614 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3615 lpBuffer, lpdwBufferLength, lpdwIndex);
3619 WININET_Release( &request->hdr );
3621 TRACE("%u <--\n", res);
3622 if(res != ERROR_SUCCESS)
3624 return res == ERROR_SUCCESS;
3627 /***********************************************************************
3628 * HttpQueryInfoA (WININET.@)
3630 * Queries for information about an HTTP request
3637 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3638 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3644 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3645 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3647 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3648 lpdwBufferLength, lpdwIndex );
3654 len = (*lpdwBufferLength)*sizeof(WCHAR);
3655 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3657 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3663 bufferW = heap_alloc(alloclen);
3664 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3665 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3666 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3673 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3677 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3678 lpBuffer, *lpdwBufferLength, NULL, NULL );
3679 *lpdwBufferLength = len - 1;
3681 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3684 /* since the strings being returned from HttpQueryInfoW should be
3685 * only ASCII characters, it is reasonable to assume that all of
3686 * the Unicode characters can be reduced to a single byte */
3687 *lpdwBufferLength = len / sizeof(WCHAR);
3689 heap_free( bufferW );
3693 /***********************************************************************
3694 * HTTP_GetRedirectURL (internal)
3696 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3698 static WCHAR szHttp[] = {'h','t','t','p',0};
3699 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3700 http_session_t *session = request->session;
3701 URL_COMPONENTSW urlComponents;
3702 DWORD url_length = 0;
3704 LPWSTR combined_url;
3706 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3707 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3708 urlComponents.dwSchemeLength = 0;
3709 urlComponents.lpszHostName = session->hostName;
3710 urlComponents.dwHostNameLength = 0;
3711 urlComponents.nPort = session->hostPort;
3712 urlComponents.lpszUserName = session->userName;
3713 urlComponents.dwUserNameLength = 0;
3714 urlComponents.lpszPassword = NULL;
3715 urlComponents.dwPasswordLength = 0;
3716 urlComponents.lpszUrlPath = request->path;
3717 urlComponents.dwUrlPathLength = 0;
3718 urlComponents.lpszExtraInfo = NULL;
3719 urlComponents.dwExtraInfoLength = 0;
3721 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3722 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3725 orig_url = heap_alloc(url_length);
3727 /* convert from bytes to characters */
3728 url_length = url_length / sizeof(WCHAR) - 1;
3729 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3731 heap_free(orig_url);
3736 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3737 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3739 heap_free(orig_url);
3742 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3744 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3746 heap_free(orig_url);
3747 heap_free(combined_url);
3750 heap_free(orig_url);
3751 return combined_url;
3755 /***********************************************************************
3756 * HTTP_HandleRedirect (internal)
3758 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3760 http_session_t *session = request->session;
3761 appinfo_t *hIC = session->appInfo;
3762 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3763 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3768 /* if it's an absolute path, keep the same session info */
3769 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3773 URL_COMPONENTSW urlComponents;
3774 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3775 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3776 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3777 static WCHAR szHttp[] = {'h','t','t','p',0};
3778 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3784 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3785 urlComponents.lpszScheme = protocol;
3786 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3787 urlComponents.lpszHostName = hostName;
3788 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3789 urlComponents.lpszUserName = userName;
3790 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3791 urlComponents.lpszPassword = NULL;
3792 urlComponents.dwPasswordLength = 0;
3793 urlComponents.lpszUrlPath = path;
3794 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3795 urlComponents.lpszExtraInfo = NULL;
3796 urlComponents.dwExtraInfoLength = 0;
3797 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3798 return INTERNET_GetLastError();
3800 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3801 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3803 TRACE("redirect from secure page to non-secure page\n");
3804 /* FIXME: warn about from secure redirect to non-secure page */
3805 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3807 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3808 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3810 TRACE("redirect from non-secure page to secure page\n");
3811 /* FIXME: notify about redirect to secure page */
3812 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3815 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3817 if (lstrlenW(protocol)>4) /*https*/
3818 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3820 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3825 * This upsets redirects to binary files on sourceforge.net
3826 * and gives an html page instead of the target file
3827 * Examination of the HTTP request sent by native wininet.dll
3828 * reveals that it doesn't send a referrer in that case.
3829 * Maybe there's a flag that enables this, or maybe a referrer
3830 * shouldn't be added in case of a redirect.
3833 /* consider the current host as the referrer */
3834 if (session->lpszServerName && *session->lpszServerName)
3835 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3836 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3837 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3840 heap_free(session->hostName);
3841 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3842 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3845 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3846 len = lstrlenW(hostName);
3847 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3848 session->hostName = heap_alloc(len*sizeof(WCHAR));
3849 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3852 session->hostName = heap_strdupW(hostName);
3854 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3856 heap_free(session->userName);
3857 session->userName = NULL;
3859 session->userName = heap_strdupW(userName);
3861 reset_data_stream(request);
3864 if(strcmpiW(session->serverName, hostName)) {
3865 heap_free(session->serverName);
3866 session->serverName = heap_strdupW(hostName);
3868 session->serverPort = urlComponents.nPort;
3871 heap_free(request->path);
3878 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3879 if (rc != E_POINTER)
3880 needed = strlenW(path)+1;
3881 request->path = heap_alloc(needed*sizeof(WCHAR));
3882 rc = UrlEscapeW(path, request->path, &needed,
3883 URL_ESCAPE_SPACES_ONLY);
3886 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3887 strcpyW(request->path,path);
3891 /* Remove custom content-type/length headers on redirects. */
3892 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3894 HTTP_DeleteCustomHeader(request, index);
3895 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3897 HTTP_DeleteCustomHeader(request, index);
3899 return ERROR_SUCCESS;
3902 /***********************************************************************
3903 * HTTP_build_req (internal)
3905 * concatenate all the strings in the request together
3907 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3912 for( t = list; *t ; t++ )
3913 len += strlenW( *t );
3916 str = heap_alloc(len*sizeof(WCHAR));
3919 for( t = list; *t ; t++ )
3925 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3928 LPWSTR requestString;
3934 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3935 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3936 http_session_t *session = request->session;
3940 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3941 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3942 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3943 heap_free( lpszPath );
3945 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3946 NULL, 0, NULL, NULL );
3947 len--; /* the nul terminator isn't needed */
3948 ascii_req = heap_alloc(len);
3949 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3950 heap_free( requestString );
3952 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3954 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3955 heap_free( ascii_req );
3956 if (res != ERROR_SUCCESS)
3959 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3961 return ERROR_HTTP_INVALID_HEADER;
3963 return ERROR_SUCCESS;
3966 static void HTTP_InsertCookies(http_request_t *request)
3968 DWORD cookie_size, size, cnt = 0;
3972 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3974 host = HTTP_GetHeader(request, hostW);
3978 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3981 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
3982 if(!(cookies = heap_alloc(size)))
3985 cnt += sprintfW(cookies, cookieW);
3986 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
3987 strcatW(cookies, szCrLf);
3989 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
3994 static WORD HTTP_ParseWkday(LPCWSTR day)
3996 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4004 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4005 if (!strcmpiW(day, days[i]))
4012 static WORD HTTP_ParseMonth(LPCWSTR month)
4014 static const WCHAR jan[] = { 'j','a','n',0 };
4015 static const WCHAR feb[] = { 'f','e','b',0 };
4016 static const WCHAR mar[] = { 'm','a','r',0 };
4017 static const WCHAR apr[] = { 'a','p','r',0 };
4018 static const WCHAR may[] = { 'm','a','y',0 };
4019 static const WCHAR jun[] = { 'j','u','n',0 };
4020 static const WCHAR jul[] = { 'j','u','l',0 };
4021 static const WCHAR aug[] = { 'a','u','g',0 };
4022 static const WCHAR sep[] = { 's','e','p',0 };
4023 static const WCHAR oct[] = { 'o','c','t',0 };
4024 static const WCHAR nov[] = { 'n','o','v',0 };
4025 static const WCHAR dec[] = { 'd','e','c',0 };
4027 if (!strcmpiW(month, jan)) return 1;
4028 if (!strcmpiW(month, feb)) return 2;
4029 if (!strcmpiW(month, mar)) return 3;
4030 if (!strcmpiW(month, apr)) return 4;
4031 if (!strcmpiW(month, may)) return 5;
4032 if (!strcmpiW(month, jun)) return 6;
4033 if (!strcmpiW(month, jul)) return 7;
4034 if (!strcmpiW(month, aug)) return 8;
4035 if (!strcmpiW(month, sep)) return 9;
4036 if (!strcmpiW(month, oct)) return 10;
4037 if (!strcmpiW(month, nov)) return 11;
4038 if (!strcmpiW(month, dec)) return 12;
4043 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4044 * optionally preceded by whitespace.
4045 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4046 * st, and sets *str to the first character after the time format.
4048 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4054 while (isspaceW(*ptr))
4057 num = strtoulW(ptr, &nextPtr, 10);
4058 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4060 ERR("unexpected time format %s\n", debugstr_w(ptr));
4065 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4069 st->wHour = (WORD)num;
4070 num = strtoulW(ptr, &nextPtr, 10);
4071 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4073 ERR("unexpected time format %s\n", debugstr_w(ptr));
4078 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4082 st->wMinute = (WORD)num;
4083 num = strtoulW(ptr, &nextPtr, 10);
4084 if (!nextPtr || nextPtr <= ptr)
4086 ERR("unexpected time format %s\n", debugstr_w(ptr));
4091 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4096 st->wSecond = (WORD)num;
4100 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4102 static const WCHAR gmt[]= { 'G','M','T',0 };
4103 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4105 SYSTEMTIME st = { 0 };
4108 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4109 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4112 st.wDayOfWeek = HTTP_ParseWkday(day);
4113 if (st.wDayOfWeek >= 7)
4115 ERR("unexpected weekday %s\n", debugstr_w(day));
4119 while (isspaceW(*ptr))
4122 for (monthPtr = month; !isspace(*ptr) &&
4123 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4127 st.wMonth = HTTP_ParseMonth(month);
4128 if (!st.wMonth || st.wMonth > 12)
4130 ERR("unexpected month %s\n", debugstr_w(month));
4134 while (isspaceW(*ptr))
4137 num = strtoulW(ptr, &nextPtr, 10);
4138 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4140 ERR("unexpected day %s\n", debugstr_w(ptr));
4144 st.wDay = (WORD)num;
4146 while (isspaceW(*ptr))
4149 if (!HTTP_ParseTime(&st, &ptr))
4152 while (isspaceW(*ptr))
4155 num = strtoulW(ptr, &nextPtr, 10);
4156 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4158 ERR("unexpected year %s\n", debugstr_w(ptr));
4162 st.wYear = (WORD)num;
4164 while (isspaceW(*ptr))
4167 /* asctime() doesn't report a timezone, but some web servers do, so accept
4168 * with or without GMT.
4170 if (*ptr && strcmpW(ptr, gmt))
4172 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4175 return SystemTimeToFileTime(&st, ft);
4178 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4180 static const WCHAR gmt[]= { 'G','M','T',0 };
4181 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4184 SYSTEMTIME st = { 0 };
4186 ptr = strchrW(value, ',');
4189 if (ptr - value != 3)
4191 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4194 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4196 st.wDayOfWeek = HTTP_ParseWkday(day);
4197 if (st.wDayOfWeek > 6)
4199 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4204 while (isspaceW(*ptr))
4207 num = strtoulW(ptr, &nextPtr, 10);
4208 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4210 WARN("unexpected day %s\n", debugstr_w(value));
4214 st.wDay = (WORD)num;
4216 while (isspaceW(*ptr))
4219 for (monthPtr = month; !isspace(*ptr) &&
4220 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4224 st.wMonth = HTTP_ParseMonth(month);
4225 if (!st.wMonth || st.wMonth > 12)
4227 WARN("unexpected month %s\n", debugstr_w(month));
4231 while (isspaceW(*ptr))
4234 num = strtoulW(ptr, &nextPtr, 10);
4235 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4237 ERR("unexpected year %s\n", debugstr_w(value));
4241 st.wYear = (WORD)num;
4243 if (!HTTP_ParseTime(&st, &ptr))
4246 while (isspaceW(*ptr))
4249 if (strcmpW(ptr, gmt))
4251 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4254 return SystemTimeToFileTime(&st, ft);
4257 static WORD HTTP_ParseWeekday(LPCWSTR day)
4259 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4260 { 'm','o','n','d','a','y',0 },
4261 { 't','u','e','s','d','a','y',0 },
4262 { 'w','e','d','n','e','s','d','a','y',0 },
4263 { 't','h','u','r','s','d','a','y',0 },
4264 { 'f','r','i','d','a','y',0 },
4265 { 's','a','t','u','r','d','a','y',0 }};
4267 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4268 if (!strcmpiW(day, days[i]))
4275 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4277 static const WCHAR gmt[]= { 'G','M','T',0 };
4278 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4281 SYSTEMTIME st = { 0 };
4283 ptr = strchrW(value, ',');
4286 if (ptr - value == 3)
4288 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4290 st.wDayOfWeek = HTTP_ParseWkday(day);
4291 if (st.wDayOfWeek > 6)
4293 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4297 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4299 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4300 day[ptr - value + 1] = 0;
4301 st.wDayOfWeek = HTTP_ParseWeekday(day);
4302 if (st.wDayOfWeek > 6)
4304 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4310 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4315 while (isspaceW(*ptr))
4318 num = strtoulW(ptr, &nextPtr, 10);
4319 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4321 ERR("unexpected day %s\n", debugstr_w(value));
4325 st.wDay = (WORD)num;
4329 ERR("unexpected month format %s\n", debugstr_w(ptr));
4334 for (monthPtr = month; *ptr != '-' &&
4335 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4339 st.wMonth = HTTP_ParseMonth(month);
4340 if (!st.wMonth || st.wMonth > 12)
4342 ERR("unexpected month %s\n", debugstr_w(month));
4348 ERR("unexpected year format %s\n", debugstr_w(ptr));
4353 num = strtoulW(ptr, &nextPtr, 10);
4354 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4356 ERR("unexpected year %s\n", debugstr_w(value));
4360 st.wYear = (WORD)num;
4362 if (!HTTP_ParseTime(&st, &ptr))
4365 while (isspaceW(*ptr))
4368 if (strcmpW(ptr, gmt))
4370 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4373 return SystemTimeToFileTime(&st, ft);
4376 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4378 static const WCHAR zero[] = { '0',0 };
4381 if (!strcmpW(value, zero))
4383 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4386 else if (strchrW(value, ','))
4388 ret = HTTP_ParseRfc1123Date(value, ft);
4391 ret = HTTP_ParseRfc850Date(value, ft);
4393 ERR("unexpected date format %s\n", debugstr_w(value));
4398 ret = HTTP_ParseDateAsAsctime(value, ft);
4400 ERR("unexpected date format %s\n", debugstr_w(value));
4405 static void HTTP_ProcessExpires(http_request_t *request)
4407 BOOL expirationFound = FALSE;
4410 /* Look for a Cache-Control header with a max-age directive, as it takes
4411 * precedence over the Expires header.
4413 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4414 if (headerIndex != -1)
4416 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4419 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4421 LPWSTR comma = strchrW(ptr, ','), end, equal;
4426 end = ptr + strlenW(ptr);
4427 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4431 static const WCHAR max_age[] = {
4432 'm','a','x','-','a','g','e',0 };
4434 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4439 age = strtoulW(equal + 1, &nextPtr, 10);
4440 if (nextPtr > equal + 1)
4444 NtQuerySystemTime( &ft );
4445 /* Age is in seconds, FILETIME resolution is in
4446 * 100 nanosecond intervals.
4448 ft.QuadPart += age * (ULONGLONG)1000000;
4449 request->expires.dwLowDateTime = ft.u.LowPart;
4450 request->expires.dwHighDateTime = ft.u.HighPart;
4451 expirationFound = TRUE;
4458 while (isspaceW(*ptr))
4465 if (!expirationFound)
4467 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4468 if (headerIndex != -1)
4470 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4473 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4475 expirationFound = TRUE;
4476 request->expires = ft;
4480 if (!expirationFound)
4484 /* With no known age, default to 10 minutes until expiration. */
4485 NtQuerySystemTime( &t );
4486 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4487 request->expires.dwLowDateTime = t.u.LowPart;
4488 request->expires.dwHighDateTime = t.u.HighPart;
4492 static void HTTP_ProcessLastModified(http_request_t *request)
4496 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4497 if (headerIndex != -1)
4499 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4502 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4503 request->last_modified = ft;
4507 static void http_process_keep_alive(http_request_t *req)
4511 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4513 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4515 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4518 static void HTTP_CacheRequest(http_request_t *request)
4520 WCHAR url[INTERNET_MAX_URL_LENGTH];
4521 WCHAR cacheFileName[MAX_PATH+1];
4524 b = HTTP_GetRequestURL(request, url);
4526 WARN("Could not get URL\n");
4530 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4532 heap_free(request->cacheFile);
4533 CloseHandle(request->hCacheFile);
4535 request->cacheFile = heap_strdupW(cacheFileName);
4536 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4537 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4538 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4539 WARN("Could not create file: %u\n", GetLastError());
4540 request->hCacheFile = NULL;
4543 WARN("Could not create cache entry: %08x\n", GetLastError());
4547 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4549 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4550 http_session_t *session = request->session;
4551 netconn_t *netconn = NULL;
4555 assert(!request->netconn);
4556 reset_data_stream(request);
4558 server = get_server(session->serverName, session->serverPort);
4560 return ERROR_OUTOFMEMORY;
4562 res = HTTP_ResolveName(request, server);
4563 if(res != ERROR_SUCCESS) {
4564 server_release(server);
4568 EnterCriticalSection(&connection_pool_cs);
4570 while(!list_empty(&server->conn_pool)) {
4571 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4572 list_remove(&netconn->pool_entry);
4574 if(NETCON_is_alive(netconn))
4577 TRACE("connection %p closed during idle\n", netconn);
4578 free_netconn(netconn);
4582 LeaveCriticalSection(&connection_pool_cs);
4585 TRACE("<-- reusing %p netconn\n", netconn);
4586 request->netconn = netconn;
4588 return ERROR_SUCCESS;
4591 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4592 INTERNET_STATUS_CONNECTING_TO_SERVER,
4594 strlen(server->addr_str)+1);
4596 res = create_netconn(is_https, server, request->security_flags, &netconn);
4597 server_release(server);
4598 if(res != ERROR_SUCCESS) {
4599 ERR("create_netconn failed: %u\n", res);
4603 request->netconn = netconn;
4605 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4606 INTERNET_STATUS_CONNECTED_TO_SERVER,
4607 server->addr_str, strlen(server->addr_str)+1);
4610 /* Note: we differ from Microsoft's WinINet here. they seem to have
4611 * a bug that causes no status callbacks to be sent when starting
4612 * a tunnel to a proxy server using the CONNECT verb. i believe our
4613 * behaviour to be more correct and to not cause any incompatibilities
4614 * because using a secure connection through a proxy server is a rare
4615 * case that would be hard for anyone to depend on */
4616 if(session->appInfo->proxy)
4617 res = HTTP_SecureProxyConnect(request);
4618 if(res == ERROR_SUCCESS)
4619 res = NETCON_secure_connect(request->netconn, session->hostName);
4620 if(res != ERROR_SUCCESS)
4622 WARN("Couldn't connect securely to host\n");
4624 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4625 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4626 || res == ERROR_INTERNET_INVALID_CA
4627 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4628 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4629 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4630 || res == ERROR_INTERNET_SEC_INVALID_CERT
4631 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4632 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4636 if(res != ERROR_SUCCESS) {
4637 http_release_netconn(request, FALSE);
4642 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4643 return ERROR_SUCCESS;
4646 /***********************************************************************
4647 * HTTP_HttpSendRequestW (internal)
4649 * Sends the specified request to the HTTP server
4652 * ERROR_SUCCESS on success
4653 * win32 error code on failure
4656 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4657 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4658 DWORD dwContentLength, BOOL bEndRequest)
4661 BOOL redirected = FALSE;
4662 LPWSTR requestString = NULL;
4665 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4666 static const WCHAR szContentLength[] =
4667 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4668 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4671 TRACE("--> %p\n", request);
4673 assert(request->hdr.htype == WH_HHTTPREQ);
4675 /* if the verb is NULL default to GET */
4677 request->verb = heap_strdupW(szGET);
4679 if (dwContentLength || strcmpW(request->verb, szGET))
4681 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4682 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4683 request->bytesToWrite = dwContentLength;
4685 if (request->session->appInfo->agent)
4687 WCHAR *agent_header;
4688 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4691 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4692 agent_header = heap_alloc(len * sizeof(WCHAR));
4693 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4695 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4696 heap_free(agent_header);
4698 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4700 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4701 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4703 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4705 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4706 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4707 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4713 BOOL reusing_connection;
4718 /* like native, just in case the caller forgot to call InternetReadFile
4719 * for all the data */
4720 drain_content(request);
4722 request->contentLength = ~0u;
4723 request->bytesToWrite = 0;
4726 if (TRACE_ON(wininet))
4728 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4729 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4732 HTTP_FixURL(request);
4733 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4735 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4737 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4738 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4740 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4741 HTTP_InsertCookies(request);
4743 /* add the headers the caller supplied */
4744 if( lpszHeaders && dwHeaderLength )
4746 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4747 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4750 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4752 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4753 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4757 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4760 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4762 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4765 /* send the request as ASCII, tack on the optional data */
4766 if (!lpOptional || redirected)
4767 dwOptionalLength = 0;
4768 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4769 NULL, 0, NULL, NULL );
4770 ascii_req = heap_alloc(len + dwOptionalLength);
4771 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4772 ascii_req, len, NULL, NULL );
4774 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4775 len = (len + dwOptionalLength - 1);
4777 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4779 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4780 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4782 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4783 heap_free( ascii_req );
4784 if(res != ERROR_SUCCESS) {
4785 TRACE("send failed: %u\n", res);
4786 if(!reusing_connection)
4788 http_release_netconn(request, FALSE);
4793 request->bytesWritten = dwOptionalLength;
4795 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4796 INTERNET_STATUS_REQUEST_SENT,
4797 &len, sizeof(DWORD));
4804 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4805 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4807 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4808 /* FIXME: We should know that connection is closed before sending
4809 * headers. Otherwise wrong callbacks are executed */
4810 if(!responseLen && reusing_connection) {
4811 TRACE("Connection closed by server, reconnecting\n");
4812 http_release_netconn(request, FALSE);
4817 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4818 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4821 http_process_keep_alive(request);
4822 HTTP_ProcessCookies(request);
4823 HTTP_ProcessExpires(request);
4824 HTTP_ProcessLastModified(request);
4826 dwBufferSize = sizeof(dwStatusCode);
4827 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4828 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4831 res = set_content_length(request, dwStatusCode);
4832 if(res != ERROR_SUCCESS)
4834 if(!request->contentLength)
4835 http_release_netconn(request, TRUE);
4837 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4839 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4840 dwBufferSize=sizeof(szNewLocation);
4841 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4842 dwStatusCode == HTTP_STATUS_MOVED ||
4843 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4844 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4845 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4847 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4848 dwStatusCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4850 heap_free(request->verb);
4851 request->verb = heap_strdupW(szGET);
4853 drain_content(request);
4854 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4856 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4857 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4858 res = HTTP_HandleRedirect(request, new_url);
4859 if (res == ERROR_SUCCESS)
4861 heap_free(requestString);
4864 heap_free( new_url );
4869 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4871 WCHAR szAuthValue[2048];
4873 if (dwStatusCode == HTTP_STATUS_DENIED)
4875 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4877 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4879 if (HTTP_DoAuthorization(request, szAuthValue,
4881 request->session->userName,
4882 request->session->password,
4885 heap_free(requestString);
4892 TRACE("Cleaning wrong authorization data\n");
4893 destroy_authinfo(request->authInfo);
4894 request->authInfo = NULL;
4897 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4900 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4902 if (HTTP_DoAuthorization(request, szAuthValue,
4903 &request->proxyAuthInfo,
4904 request->session->appInfo->proxyUsername,
4905 request->session->appInfo->proxyPassword,
4914 TRACE("Cleaning wrong proxy authorization data\n");
4915 destroy_authinfo(request->proxyAuthInfo);
4916 request->proxyAuthInfo = NULL;
4922 res = ERROR_SUCCESS;
4926 if(res == ERROR_SUCCESS)
4927 HTTP_CacheRequest(request);
4930 heap_free(requestString);
4932 /* TODO: send notification for P3P header */
4934 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4936 if (res == ERROR_SUCCESS) {
4937 if(request->contentLength && request->bytesWritten == request->bytesToWrite)
4938 HTTP_ReceiveRequestData(request, TRUE);
4940 send_request_complete(request,
4941 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4943 send_request_complete(request, 0, res);
4951 /***********************************************************************
4953 * Helper functions for the HttpSendRequest(Ex) functions
4956 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4958 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4959 http_request_t *request = (http_request_t*) workRequest->hdr;
4961 TRACE("%p\n", request);
4963 HTTP_HttpSendRequestW(request, req->lpszHeader,
4964 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4965 req->dwContentLength, req->bEndRequest);
4967 heap_free(req->lpszHeader);
4971 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4974 DWORD dwCode, dwCodeLength;
4976 DWORD res = ERROR_SUCCESS;
4978 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4979 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4981 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4983 res = ERROR_HTTP_HEADER_NOT_FOUND;
4985 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4986 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4988 /* process cookies here. Is this right? */
4989 http_process_keep_alive(request);
4990 HTTP_ProcessCookies(request);
4991 HTTP_ProcessExpires(request);
4992 HTTP_ProcessLastModified(request);
4994 dwCodeLength = sizeof(dwCode);
4995 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4996 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
4999 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
5000 if(!request->contentLength)
5001 http_release_netconn(request, TRUE);
5004 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5006 if (dwCode == HTTP_STATUS_REDIRECT ||
5007 dwCode == HTTP_STATUS_MOVED ||
5008 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
5009 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
5011 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5012 dwBufferSize=sizeof(szNewLocation);
5013 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
5015 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5016 dwCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
5018 heap_free(request->verb);
5019 request->verb = heap_strdupW(szGET);
5021 drain_content(request);
5022 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5024 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5025 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5026 res = HTTP_HandleRedirect(request, new_url);
5027 if (res == ERROR_SUCCESS)
5028 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5029 heap_free( new_url );
5035 if (res == ERROR_SUCCESS && request->contentLength)
5036 HTTP_ReceiveRequestData(request, TRUE);
5038 send_request_complete(request, res == ERROR_SUCCESS, res);
5043 /***********************************************************************
5044 * HttpEndRequestA (WININET.@)
5046 * Ends an HTTP request that was started by HttpSendRequestEx
5049 * TRUE if successful
5053 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5054 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5056 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5060 SetLastError(ERROR_INVALID_PARAMETER);
5064 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5067 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5069 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5070 http_request_t *request = (http_request_t*)work->hdr;
5072 TRACE("%p\n", request);
5074 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5077 /***********************************************************************
5078 * HttpEndRequestW (WININET.@)
5080 * Ends an HTTP request that was started by HttpSendRequestEx
5083 * TRUE if successful
5087 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5088 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5090 http_request_t *request;
5093 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5097 SetLastError(ERROR_INVALID_PARAMETER);
5101 request = (http_request_t*) get_handle_object( hRequest );
5103 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5105 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5107 WININET_Release( &request->hdr );
5110 request->hdr.dwFlags |= dwFlags;
5112 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5115 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5117 work.asyncproc = AsyncHttpEndRequestProc;
5118 work.hdr = WININET_AddRef( &request->hdr );
5120 work_endrequest = &work.u.HttpEndRequestW;
5121 work_endrequest->dwFlags = dwFlags;
5122 work_endrequest->dwContext = dwContext;
5124 INTERNET_AsyncCall(&work);
5125 res = ERROR_IO_PENDING;
5128 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5130 WININET_Release( &request->hdr );
5131 TRACE("%u <--\n", res);
5132 if(res != ERROR_SUCCESS)
5134 return res == ERROR_SUCCESS;
5137 /***********************************************************************
5138 * HttpSendRequestExA (WININET.@)
5140 * Sends the specified request to the HTTP server and allows chunked
5145 * Failure: FALSE, call GetLastError() for more information.
5147 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5148 LPINTERNET_BUFFERSA lpBuffersIn,
5149 LPINTERNET_BUFFERSA lpBuffersOut,
5150 DWORD dwFlags, DWORD_PTR dwContext)
5152 INTERNET_BUFFERSW BuffersInW;
5155 LPWSTR header = NULL;
5157 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5158 lpBuffersOut, dwFlags, dwContext);
5162 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5163 if (lpBuffersIn->lpcszHeader)
5165 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5166 lpBuffersIn->dwHeadersLength,0,0);
5167 header = heap_alloc(headerlen*sizeof(WCHAR));
5168 if (!(BuffersInW.lpcszHeader = header))
5170 SetLastError(ERROR_OUTOFMEMORY);
5173 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5174 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5178 BuffersInW.lpcszHeader = NULL;
5179 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5180 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5181 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5182 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5183 BuffersInW.Next = NULL;
5186 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5192 /***********************************************************************
5193 * HttpSendRequestExW (WININET.@)
5195 * Sends the specified request to the HTTP server and allows chunked
5200 * Failure: FALSE, call GetLastError() for more information.
5202 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5203 LPINTERNET_BUFFERSW lpBuffersIn,
5204 LPINTERNET_BUFFERSW lpBuffersOut,
5205 DWORD dwFlags, DWORD_PTR dwContext)
5207 http_request_t *request;
5208 http_session_t *session;
5212 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5213 lpBuffersOut, dwFlags, dwContext);
5215 request = (http_request_t*) get_handle_object( hRequest );
5217 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5219 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5223 session = request->session;
5224 assert(session->hdr.htype == WH_HHTTPSESSION);
5225 hIC = session->appInfo;
5226 assert(hIC->hdr.htype == WH_HINIT);
5228 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5230 WORKREQUEST workRequest;
5231 struct WORKREQ_HTTPSENDREQUESTW *req;
5233 workRequest.asyncproc = AsyncHttpSendRequestProc;
5234 workRequest.hdr = WININET_AddRef( &request->hdr );
5235 req = &workRequest.u.HttpSendRequestW;
5240 if (lpBuffersIn->lpcszHeader)
5242 if (lpBuffersIn->dwHeadersLength == ~0u)
5243 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5245 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5247 req->lpszHeader = heap_alloc(size);
5248 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5250 else req->lpszHeader = NULL;
5252 req->dwHeaderLength = size / sizeof(WCHAR);
5253 req->lpOptional = lpBuffersIn->lpvBuffer;
5254 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5255 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5259 req->lpszHeader = NULL;
5260 req->dwHeaderLength = 0;
5261 req->lpOptional = NULL;
5262 req->dwOptionalLength = 0;
5263 req->dwContentLength = 0;
5266 req->bEndRequest = FALSE;
5268 INTERNET_AsyncCall(&workRequest);
5270 * This is from windows.
5272 res = ERROR_IO_PENDING;
5277 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5278 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5279 lpBuffersIn->dwBufferTotal, FALSE);
5281 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5286 WININET_Release( &request->hdr );
5290 return res == ERROR_SUCCESS;
5293 /***********************************************************************
5294 * HttpSendRequestW (WININET.@)
5296 * Sends the specified request to the HTTP server
5303 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5304 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5306 http_request_t *request;
5307 http_session_t *session = NULL;
5308 appinfo_t *hIC = NULL;
5309 DWORD res = ERROR_SUCCESS;
5311 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5312 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5314 request = (http_request_t*) get_handle_object( hHttpRequest );
5315 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5317 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5321 session = request->session;
5322 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5324 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5328 hIC = session->appInfo;
5329 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5331 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5335 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5337 WORKREQUEST workRequest;
5338 struct WORKREQ_HTTPSENDREQUESTW *req;
5340 workRequest.asyncproc = AsyncHttpSendRequestProc;
5341 workRequest.hdr = WININET_AddRef( &request->hdr );
5342 req = &workRequest.u.HttpSendRequestW;
5347 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5348 else size = dwHeaderLength * sizeof(WCHAR);
5350 req->lpszHeader = heap_alloc(size);
5351 memcpy(req->lpszHeader, lpszHeaders, size);
5354 req->lpszHeader = 0;
5355 req->dwHeaderLength = dwHeaderLength;
5356 req->lpOptional = lpOptional;
5357 req->dwOptionalLength = dwOptionalLength;
5358 req->dwContentLength = dwOptionalLength;
5359 req->bEndRequest = TRUE;
5361 INTERNET_AsyncCall(&workRequest);
5363 * This is from windows.
5365 res = ERROR_IO_PENDING;
5369 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5370 dwHeaderLength, lpOptional, dwOptionalLength,
5371 dwOptionalLength, TRUE);
5375 WININET_Release( &request->hdr );
5378 return res == ERROR_SUCCESS;
5381 /***********************************************************************
5382 * HttpSendRequestA (WININET.@)
5384 * Sends the specified request to the HTTP server
5391 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5392 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5395 LPWSTR szHeaders=NULL;
5396 DWORD nLen=dwHeaderLength;
5397 if(lpszHeaders!=NULL)
5399 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5400 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5401 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5403 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5404 heap_free(szHeaders);
5408 /***********************************************************************
5409 * HTTPSESSION_Destroy (internal)
5411 * Deallocate session handle
5414 static void HTTPSESSION_Destroy(object_header_t *hdr)
5416 http_session_t *session = (http_session_t*) hdr;
5418 TRACE("%p\n", session);
5420 WININET_Release(&session->appInfo->hdr);
5422 heap_free(session->hostName);
5423 heap_free(session->serverName);
5424 heap_free(session->password);
5425 heap_free(session->userName);
5428 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5431 case INTERNET_OPTION_HANDLE_TYPE:
5432 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5434 if (*size < sizeof(ULONG))
5435 return ERROR_INSUFFICIENT_BUFFER;
5437 *size = sizeof(DWORD);
5438 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5439 return ERROR_SUCCESS;
5442 return INET_QueryOption(hdr, option, buffer, size, unicode);
5445 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5447 http_session_t *ses = (http_session_t*)hdr;
5450 case INTERNET_OPTION_USERNAME:
5452 heap_free(ses->userName);
5453 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5454 return ERROR_SUCCESS;
5456 case INTERNET_OPTION_PASSWORD:
5458 heap_free(ses->password);
5459 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5460 return ERROR_SUCCESS;
5465 return ERROR_INTERNET_INVALID_OPTION;
5468 static const object_vtbl_t HTTPSESSIONVtbl = {
5469 HTTPSESSION_Destroy,
5471 HTTPSESSION_QueryOption,
5472 HTTPSESSION_SetOption,
5481 /***********************************************************************
5482 * HTTP_Connect (internal)
5484 * Create http session handle
5487 * HINTERNET a session handle on success
5491 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5492 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5493 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5494 DWORD dwInternalFlags, HINTERNET *ret)
5496 http_session_t *session = NULL;
5500 if (!lpszServerName || !lpszServerName[0])
5501 return ERROR_INVALID_PARAMETER;
5503 assert( hIC->hdr.htype == WH_HINIT );
5505 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5507 return ERROR_OUTOFMEMORY;
5510 * According to my tests. The name is not resolved until a request is sent
5513 session->hdr.htype = WH_HHTTPSESSION;
5514 session->hdr.dwFlags = dwFlags;
5515 session->hdr.dwContext = dwContext;
5516 session->hdr.dwInternalFlags |= dwInternalFlags;
5518 WININET_AddRef( &hIC->hdr );
5519 session->appInfo = hIC;
5520 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5522 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5523 if(hIC->proxyBypass)
5524 FIXME("Proxy bypass is ignored.\n");
5526 session->serverName = heap_strdupW(lpszServerName);
5527 session->hostName = heap_strdupW(lpszServerName);
5528 if (lpszUserName && lpszUserName[0])
5529 session->userName = heap_strdupW(lpszUserName);
5530 if (lpszPassword && lpszPassword[0])
5531 session->password = heap_strdupW(lpszPassword);
5532 session->serverPort = serverPort;
5533 session->hostPort = serverPort;
5535 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5536 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5538 INTERNET_SendCallback(&hIC->hdr, dwContext,
5539 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5544 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5548 TRACE("%p --> %p\n", hIC, session);
5550 *ret = session->hdr.hInternet;
5551 return ERROR_SUCCESS;
5554 /***********************************************************************
5555 * HTTP_clear_response_headers (internal)
5557 * clear out any old response headers
5559 static void HTTP_clear_response_headers( http_request_t *request )
5563 for( i=0; i<request->nCustHeaders; i++)
5565 if( !request->custHeaders[i].lpszField )
5567 if( !request->custHeaders[i].lpszValue )
5569 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5571 HTTP_DeleteCustomHeader( request, i );
5576 /***********************************************************************
5577 * HTTP_GetResponseHeaders (internal)
5579 * Read server response
5586 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5589 WCHAR buffer[MAX_REPLY_LEN];
5590 DWORD buflen = MAX_REPLY_LEN;
5591 BOOL bSuccess = FALSE;
5593 char bufferA[MAX_REPLY_LEN];
5594 LPWSTR status_code = NULL, status_text = NULL;
5595 DWORD cchMaxRawHeaders = 1024;
5596 LPWSTR lpszRawHeaders = NULL;
5598 DWORD cchRawHeaders = 0;
5599 BOOL codeHundred = FALSE;
5603 if(!request->netconn)
5607 static const WCHAR szHundred[] = {'1','0','0',0};
5609 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5611 buflen = MAX_REPLY_LEN;
5612 if (!read_line(request, bufferA, &buflen))
5615 /* clear old response headers (eg. from a redirect response) */
5617 HTTP_clear_response_headers( request );
5622 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5623 /* check is this a status code line? */
5624 if (!strncmpW(buffer, g_szHttp1_0, 4))
5626 /* split the version from the status code */
5627 status_code = strchrW( buffer, ' ' );
5632 /* split the status code from the status text */
5633 status_text = strchrW( status_code, ' ' );
5638 TRACE("version [%s] status code [%s] status text [%s]\n",
5639 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5641 codeHundred = (!strcmpW(status_code, szHundred));
5643 else if (!codeHundred)
5645 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5647 heap_free(request->version);
5648 heap_free(request->statusText);
5650 request->version = heap_strdupW(g_szHttp1_0);
5651 request->statusText = heap_strdupW(szOK);
5653 heap_free(request->rawHeaders);
5654 request->rawHeaders = heap_strdupW(szDefaultHeader);
5659 } while (codeHundred);
5661 /* Add status code */
5662 HTTP_ProcessHeader(request, szStatus, status_code,
5663 HTTP_ADDHDR_FLAG_REPLACE);
5665 heap_free(request->version);
5666 heap_free(request->statusText);
5668 request->version = heap_strdupW(buffer);
5669 request->statusText = heap_strdupW(status_text);
5671 /* Restore the spaces */
5672 *(status_code-1) = ' ';
5673 *(status_text-1) = ' ';
5675 /* regenerate raw headers */
5676 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5677 if (!lpszRawHeaders) goto lend;
5679 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5680 cchMaxRawHeaders *= 2;
5681 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5682 if (temp == NULL) goto lend;
5683 lpszRawHeaders = temp;
5684 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5685 cchRawHeaders += (buflen-1);
5686 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5687 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5688 lpszRawHeaders[cchRawHeaders] = '\0';
5690 /* Parse each response line */
5693 buflen = MAX_REPLY_LEN;
5694 if (read_line(request, bufferA, &buflen))
5696 LPWSTR * pFieldAndValue;
5698 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5700 if (!bufferA[0]) break;
5701 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5703 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5706 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5707 cchMaxRawHeaders *= 2;
5708 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5709 if (temp == NULL) goto lend;
5710 lpszRawHeaders = temp;
5711 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5712 cchRawHeaders += (buflen-1);
5713 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5714 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5715 lpszRawHeaders[cchRawHeaders] = '\0';
5717 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5718 HTTP_ADDREQ_FLAG_ADD );
5720 HTTP_FreeTokens(pFieldAndValue);
5731 /* make sure the response header is terminated with an empty line. Some apps really
5732 truly care about that empty line being there for some reason. Just add it to the
5734 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5736 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5737 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5738 if (temp == NULL) goto lend;
5739 lpszRawHeaders = temp;
5742 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5744 heap_free(request->rawHeaders);
5745 request->rawHeaders = lpszRawHeaders;
5746 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5756 heap_free(lpszRawHeaders);
5761 /***********************************************************************
5762 * HTTP_InterpretHttpHeader (internal)
5764 * Parse server response
5768 * Pointer to array of field, value, NULL on success.
5771 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5773 LPWSTR * pTokenPair;
5777 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5779 pszColon = strchrW(buffer, ':');
5780 /* must have two tokens */
5783 HTTP_FreeTokens(pTokenPair);
5785 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5789 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5792 HTTP_FreeTokens(pTokenPair);
5795 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5796 pTokenPair[0][pszColon - buffer] = '\0';
5800 len = strlenW(pszColon);
5801 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5804 HTTP_FreeTokens(pTokenPair);
5807 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5809 strip_spaces(pTokenPair[0]);
5810 strip_spaces(pTokenPair[1]);
5812 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5816 /***********************************************************************
5817 * HTTP_ProcessHeader (internal)
5819 * Stuff header into header tables according to <dwModifier>
5823 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5825 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5827 LPHTTPHEADERW lphttpHdr = NULL;
5829 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5830 DWORD res = ERROR_HTTP_INVALID_HEADER;
5832 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5834 /* REPLACE wins out over ADD */
5835 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5836 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5838 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5841 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5845 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5846 return ERROR_HTTP_INVALID_HEADER;
5847 lphttpHdr = &request->custHeaders[index];
5853 hdr.lpszField = (LPWSTR)field;
5854 hdr.lpszValue = (LPWSTR)value;
5855 hdr.wFlags = hdr.wCount = 0;
5857 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5858 hdr.wFlags |= HDR_ISREQUEST;
5860 return HTTP_InsertCustomHeader(request, &hdr);
5862 /* no value to delete */
5863 else return ERROR_SUCCESS;
5865 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5866 lphttpHdr->wFlags |= HDR_ISREQUEST;
5868 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5870 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5872 HTTP_DeleteCustomHeader( request, index );
5878 hdr.lpszField = (LPWSTR)field;
5879 hdr.lpszValue = (LPWSTR)value;
5880 hdr.wFlags = hdr.wCount = 0;
5882 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5883 hdr.wFlags |= HDR_ISREQUEST;
5885 return HTTP_InsertCustomHeader(request, &hdr);
5888 return ERROR_SUCCESS;
5890 else if (dwModifier & COALESCEFLAGS)
5895 INT origlen = strlenW(lphttpHdr->lpszValue);
5896 INT valuelen = strlenW(value);
5898 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5901 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5903 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5906 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5909 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5911 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5914 lphttpHdr->lpszValue = lpsztmp;
5915 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5918 lphttpHdr->lpszValue[origlen] = ch;
5920 lphttpHdr->lpszValue[origlen] = ' ';
5924 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5925 lphttpHdr->lpszValue[len] = '\0';
5926 res = ERROR_SUCCESS;
5930 WARN("heap_realloc (%d bytes) failed\n",len+1);
5931 res = ERROR_OUTOFMEMORY;
5934 TRACE("<-- %d\n", res);
5938 /***********************************************************************
5939 * HTTP_GetCustomHeaderIndex (internal)
5941 * Return index of custom header from header array
5944 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5945 int requested_index, BOOL request_only)
5949 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5951 for (index = 0; index < request->nCustHeaders; index++)
5953 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5956 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5959 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5962 if (requested_index == 0)
5967 if (index >= request->nCustHeaders)
5970 TRACE("Return: %d\n", index);
5975 /***********************************************************************
5976 * HTTP_InsertCustomHeader (internal)
5978 * Insert header into array
5981 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5984 LPHTTPHEADERW lph = NULL;
5986 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5987 count = request->nCustHeaders + 1;
5989 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5991 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5994 return ERROR_OUTOFMEMORY;
5996 request->custHeaders = lph;
5997 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5998 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5999 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6000 request->custHeaders[count-1].wCount= lpHdr->wCount;
6001 request->nCustHeaders++;
6003 return ERROR_SUCCESS;
6007 /***********************************************************************
6008 * HTTP_DeleteCustomHeader (internal)
6010 * Delete header from array
6011 * If this function is called, the indexs may change.
6013 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6015 if( request->nCustHeaders <= 0 )
6017 if( index >= request->nCustHeaders )
6019 request->nCustHeaders--;
6021 heap_free(request->custHeaders[index].lpszField);
6022 heap_free(request->custHeaders[index].lpszValue);
6024 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6025 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6026 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6032 /***********************************************************************
6033 * HTTP_VerifyValidHeader (internal)
6035 * Verify the given header is not invalid for the given http request
6038 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6040 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6041 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6042 return ERROR_HTTP_INVALID_HEADER;
6044 return ERROR_SUCCESS;
6047 /***********************************************************************
6048 * IsHostInProxyBypassList (@)
6053 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6055 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6059 /***********************************************************************
6060 * InternetShowSecurityInfoByURLA (@)
6062 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6064 FIXME("stub: %s %p\n", url, window);
6068 /***********************************************************************
6069 * InternetShowSecurityInfoByURLW (@)
6071 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6073 FIXME("stub: %s %p\n", debugstr_w(url), window);
6077 /***********************************************************************
6078 * ShowX509EncodedCertificate (@)
6080 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6082 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6088 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6090 memset(&view, 0, sizeof(view));
6091 view.hwndParent = parent;
6092 view.pCertContext = certContext;
6093 if (CryptUIDlgViewCertificateW(&view, NULL))
6094 ret = ERROR_SUCCESS;
6096 ret = GetLastError();
6097 CertFreeCertificateContext(certContext);
6100 ret = GetLastError();