2 * Wininet - HTTP Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 * Copyright 2011 Jacek Caban for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
69 #include "cryptuiapi.h"
72 #include "wine/debug.h"
73 #include "wine/exception.h"
74 #include "wine/unicode.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
78 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
79 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
80 static const WCHAR szOK[] = {'O','K',0};
81 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
82 static const WCHAR hostW[] = { 'H','o','s','t',0 };
83 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
85 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
86 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
87 static const WCHAR szGET[] = { 'G','E','T', 0 };
88 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
89 static const WCHAR szCrLf[] = {'\r','\n', 0};
91 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
92 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
93 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
94 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
95 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
96 static const WCHAR szAge[] = { 'A','g','e',0 };
97 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
98 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
99 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
100 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
101 static const WCHAR szContent_Disposition[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
102 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
104 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
105 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
106 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
107 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
108 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
109 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
110 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
111 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
112 static const WCHAR szDate[] = { 'D','a','t','e',0 };
113 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
114 static const WCHAR szETag[] = { 'E','T','a','g',0 };
115 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
116 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
117 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
118 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
120 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
121 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
122 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
123 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
124 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
125 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
126 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
127 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
128 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
129 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
130 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
131 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
132 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
133 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
134 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
135 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
136 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
137 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
138 static const WCHAR szURI[] = { 'U','R','I',0 };
139 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
140 static const WCHAR szVary[] = { 'V','a','r','y',0 };
141 static const WCHAR szVia[] = { 'V','i','a',0 };
142 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
143 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
145 #define HTTP_REFERER szReferer
146 #define HTTP_ACCEPT szAccept
147 #define HTTP_USERAGENT szUser_Agent
149 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
150 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
151 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
154 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
155 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157 #define COLLECT_TIME 60000
159 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
170 unsigned int auth_data_len;
171 BOOL finished; /* finished authenticating */
175 typedef struct _basicAuthorizationData
182 UINT authorizationLen;
183 } basicAuthorizationData;
185 typedef struct _authorizationData
199 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
200 static struct list authorizationCache = LIST_INIT(authorizationCache);
202 static CRITICAL_SECTION authcache_cs;
203 static CRITICAL_SECTION_DEBUG critsect_debug =
206 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
207 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
209 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
211 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
212 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
213 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
214 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
215 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
216 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
217 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
218 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
219 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
220 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
221 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
223 static CRITICAL_SECTION connection_pool_cs;
224 static CRITICAL_SECTION_DEBUG connection_pool_debug =
226 0, 0, &connection_pool_cs,
227 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
228 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
230 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
232 static struct list connection_pool = LIST_INIT(connection_pool);
233 static BOOL collector_running;
235 void server_addref(server_t *server)
237 InterlockedIncrement(&server->ref);
240 void server_release(server_t *server)
242 if(InterlockedDecrement(&server->ref))
245 list_remove(&server->entry);
247 heap_free(server->name);
251 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
253 server_t *iter, *server = NULL;
255 EnterCriticalSection(&connection_pool_cs);
257 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
258 if(iter->port == port && !strcmpW(iter->name, name)) {
260 server_addref(server);
266 server = heap_alloc(sizeof(*server));
268 server->addr_len = 0;
269 server->ref = 2; /* list reference and return */
271 server->security_flags = 0;
272 list_init(&server->conn_pool);
273 server->name = heap_strdupW(name);
275 list_add_head(&connection_pool, &server->entry);
283 LeaveCriticalSection(&connection_pool_cs);
288 BOOL collect_connections(collect_type_t collect_type)
290 netconn_t *netconn, *netconn_safe;
291 server_t *server, *server_safe;
292 BOOL remaining = FALSE;
295 now = GetTickCount64();
297 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
298 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
299 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
300 TRACE("freeing %p\n", netconn);
301 list_remove(&netconn->pool_entry);
302 free_netconn(netconn);
308 if(collect_type == COLLECT_CLEANUP) {
309 list_remove(&server->entry);
310 list_init(&server->entry);
311 server_release(server);
318 static DWORD WINAPI collect_connections_proc(void *arg)
320 BOOL remaining_conns;
323 /* FIXME: Use more sophisticated method */
326 EnterCriticalSection(&connection_pool_cs);
328 remaining_conns = collect_connections(COLLECT_TIMEOUT);
330 collector_running = FALSE;
332 LeaveCriticalSection(&connection_pool_cs);
333 }while(remaining_conns);
335 FreeLibraryAndExitThread(WININET_hModule, 0);
338 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
341 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
342 if (HeaderIndex == -1)
345 return &req->custHeaders[HeaderIndex];
354 struct data_stream_vtbl_t {
355 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
356 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
357 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
358 BOOL (*drain_content)(data_stream_t*,http_request_t*);
359 void (*destroy)(data_stream_t*);
363 data_stream_t data_stream;
365 BYTE buf[READ_BUFFER_SIZE];
371 static inline void destroy_data_stream(data_stream_t *stream)
373 stream->vtbl->destroy(stream);
376 static void reset_data_stream(http_request_t *req)
378 destroy_data_stream(req->data_stream);
379 req->data_stream = &req->netconn_stream.data_stream;
380 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
381 req->read_chunked = req->read_gzip = FALSE;
387 data_stream_t stream;
388 data_stream_t *parent_stream;
390 BYTE buf[READ_BUFFER_SIZE];
396 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
398 /* Allow reading only from read buffer */
402 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
404 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
405 return gzip_stream->end_of_data;
408 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
409 DWORD *read, read_mode_t read_mode)
411 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
412 z_stream *zstream = &gzip_stream->zstream;
413 DWORD current_read, ret_read = 0;
416 DWORD res = ERROR_SUCCESS;
418 while(size && !gzip_stream->end_of_data) {
419 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
421 if(gzip_stream->buf_size <= 64 && !end) {
422 if(gzip_stream->buf_pos) {
423 if(gzip_stream->buf_size)
424 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
425 gzip_stream->buf_pos = 0;
427 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
428 sizeof(gzip_stream->buf)-gzip_stream->buf_size, ¤t_read, read_mode);
429 gzip_stream->buf_size += current_read;
430 if(res != ERROR_SUCCESS)
432 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
433 if(!current_read && !end) {
434 if(read_mode != READMODE_NOBLOCK) {
435 WARN("unexpected end of data\n");
436 gzip_stream->end_of_data = TRUE;
440 if(gzip_stream->buf_size <= 64 && !end)
444 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
445 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
446 zstream->next_out = buf+ret_read;
447 zstream->avail_out = size;
448 zres = inflate(&gzip_stream->zstream, 0);
449 current_read = size - zstream->avail_out;
450 size -= current_read;
451 ret_read += current_read;
452 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
453 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
454 if(zres == Z_STREAM_END) {
455 TRACE("end of data\n");
456 gzip_stream->end_of_data = TRUE;
458 }else if(zres != Z_OK) {
459 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
461 res = ERROR_INTERNET_DECODING_FAILED;
465 if(ret_read && read_mode == READMODE_ASYNC)
466 read_mode = READMODE_NOBLOCK;
469 TRACE("read %u bytes\n", ret_read);
474 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
476 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
477 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
480 static void gzip_destroy(data_stream_t *stream)
482 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
484 destroy_data_stream(gzip_stream->parent_stream);
486 if(!gzip_stream->end_of_data)
487 inflateEnd(&gzip_stream->zstream);
488 heap_free(gzip_stream);
491 static const data_stream_vtbl_t gzip_stream_vtbl = {
499 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
501 return heap_alloc(items*size);
504 static void wininet_zfree(voidpf opaque, voidpf address)
509 static DWORD init_gzip_stream(http_request_t *req)
511 gzip_stream_t *gzip_stream;
514 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
516 return ERROR_OUTOFMEMORY;
518 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
519 gzip_stream->zstream.zalloc = wininet_zalloc;
520 gzip_stream->zstream.zfree = wininet_zfree;
522 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
524 ERR("inflateInit failed: %d\n", zres);
525 heap_free(gzip_stream);
526 return ERROR_OUTOFMEMORY;
529 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
531 HTTP_DeleteCustomHeader(req, index);
534 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
535 gzip_stream->buf_size = req->read_size;
536 req->read_pos = req->read_size = 0;
539 req->read_gzip = TRUE;
540 gzip_stream->parent_stream = req->data_stream;
541 req->data_stream = &gzip_stream->stream;
542 return ERROR_SUCCESS;
547 static DWORD init_gzip_stream(http_request_t *req)
549 ERR("gzip stream not supported, missing zlib.\n");
550 return ERROR_SUCCESS;
555 /***********************************************************************
556 * HTTP_Tokenize (internal)
558 * Tokenize a string, allocating memory for the tokens.
560 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
562 LPWSTR * token_array;
569 /* empty string has no tokens */
573 for (i = 0; string[i]; i++)
575 if (!strncmpW(string+i, token_string, strlenW(token_string)))
579 /* we want to skip over separators, but not the null terminator */
580 for (j = 0; j < strlenW(token_string) - 1; j++)
588 /* add 1 for terminating NULL */
589 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
590 token_array[tokens] = NULL;
593 for (i = 0; i < tokens; i++)
596 next_token = strstrW(string, token_string);
597 if (!next_token) next_token = string+strlenW(string);
598 len = next_token - string;
599 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
600 memcpy(token_array[i], string, len*sizeof(WCHAR));
601 token_array[i][len] = '\0';
602 string = next_token+strlenW(token_string);
607 /***********************************************************************
608 * HTTP_FreeTokens (internal)
610 * Frees memory returned from HTTP_Tokenize.
612 static void HTTP_FreeTokens(LPWSTR * token_array)
615 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
616 heap_free(token_array);
619 static void HTTP_FixURL(http_request_t *request)
621 static const WCHAR szSlash[] = { '/',0 };
622 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
624 /* If we don't have a path we set it to root */
625 if (NULL == request->path)
626 request->path = heap_strdupW(szSlash);
627 else /* remove \r and \n*/
629 int nLen = strlenW(request->path);
630 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
633 request->path[nLen]='\0';
635 /* Replace '\' with '/' */
638 if (request->path[nLen] == '\\') request->path[nLen]='/';
642 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
643 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
644 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
646 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
648 strcpyW(fixurl + 1, request->path);
649 heap_free( request->path );
650 request->path = fixurl;
654 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
656 LPWSTR requestString;
662 static const WCHAR szSpace[] = { ' ',0 };
663 static const WCHAR szColon[] = { ':',' ',0 };
664 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
666 /* allocate space for an array of all the string pointers to be added */
667 len = (request->nCustHeaders)*4 + 10;
668 req = heap_alloc(len*sizeof(LPCWSTR));
670 /* add the verb, path and HTTP version string */
678 /* Append custom request headers */
679 for (i = 0; i < request->nCustHeaders; i++)
681 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
684 req[n++] = request->custHeaders[i].lpszField;
686 req[n++] = request->custHeaders[i].lpszValue;
688 TRACE("Adding custom header %s (%s)\n",
689 debugstr_w(request->custHeaders[i].lpszField),
690 debugstr_w(request->custHeaders[i].lpszValue));
695 ERR("oops. buffer overrun\n");
698 requestString = HTTP_build_req( req, 4 );
702 * Set (header) termination string for request
703 * Make sure there's exactly two new lines at the end of the request
705 p = &requestString[strlenW(requestString)-1];
706 while ( (*p == '\n') || (*p == '\r') )
708 strcpyW( p+1, sztwocrlf );
710 return requestString;
713 static void HTTP_ProcessCookies( http_request_t *request )
717 LPHTTPHEADERW setCookieHeader;
719 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
722 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
728 setCookieHeader = &request->custHeaders[HeaderIndex];
730 if (!setCookieHeader->lpszValue)
733 host = HTTP_GetHeader(request, hostW);
737 data = strchrW(setCookieHeader->lpszValue, '=');
741 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
746 set_cookie(host->lpszValue, request->path, name, data);
751 static void strip_spaces(LPWSTR start)
756 while (*str == ' ' && *str != '\0')
760 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
762 end = start + strlenW(start) - 1;
763 while (end >= start && *end == ' ')
770 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
772 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
773 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
775 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
776 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
777 if (is_basic && pszRealm)
780 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
784 token = strchrW(ptr,'=');
788 while (*realm == ' ' && *realm != '\0')
790 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
791 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
794 while (*token == ' ' && *token != '\0')
798 *pszRealm = heap_strdupW(token);
799 strip_spaces(*pszRealm);
806 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
808 if (!authinfo) return;
810 if (SecIsValidHandle(&authinfo->ctx))
811 DeleteSecurityContext(&authinfo->ctx);
812 if (SecIsValidHandle(&authinfo->cred))
813 FreeCredentialsHandle(&authinfo->cred);
815 heap_free(authinfo->auth_data);
816 heap_free(authinfo->scheme);
820 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
822 basicAuthorizationData *ad;
825 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
827 EnterCriticalSection(&authcache_cs);
828 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
830 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
832 TRACE("Authorization found in cache\n");
833 *auth_data = heap_alloc(ad->authorizationLen);
834 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
835 rc = ad->authorizationLen;
839 LeaveCriticalSection(&authcache_cs);
843 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
846 basicAuthorizationData* ad = NULL;
848 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
850 EnterCriticalSection(&authcache_cs);
851 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
853 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
854 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
863 TRACE("Found match in cache, replacing\n");
864 heap_free(ad->authorization);
865 ad->authorization = heap_alloc(auth_data_len);
866 memcpy(ad->authorization, auth_data, auth_data_len);
867 ad->authorizationLen = auth_data_len;
871 ad = heap_alloc(sizeof(basicAuthorizationData));
872 ad->host = heap_strdupW(host);
873 ad->realm = heap_strdupW(realm);
874 ad->authorization = heap_alloc(auth_data_len);
875 memcpy(ad->authorization, auth_data, auth_data_len);
876 ad->authorizationLen = auth_data_len;
877 list_add_head(&basicAuthorizationCache,&ad->entry);
878 TRACE("authorization cached\n");
880 LeaveCriticalSection(&authcache_cs);
883 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
884 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
886 authorizationData *ad;
888 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
890 EnterCriticalSection(&authcache_cs);
891 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
892 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
893 TRACE("Authorization found in cache\n");
895 nt_auth_identity->User = heap_strdupW(ad->user);
896 nt_auth_identity->Password = heap_strdupW(ad->password);
897 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
898 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
899 (!nt_auth_identity->Domain && ad->domain_len)) {
900 heap_free(nt_auth_identity->User);
901 heap_free(nt_auth_identity->Password);
902 heap_free(nt_auth_identity->Domain);
906 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
907 nt_auth_identity->UserLength = ad->user_len;
908 nt_auth_identity->PasswordLength = ad->password_len;
909 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
910 nt_auth_identity->DomainLength = ad->domain_len;
911 LeaveCriticalSection(&authcache_cs);
915 LeaveCriticalSection(&authcache_cs);
920 static void cache_authorization(LPWSTR host, LPWSTR scheme,
921 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
923 authorizationData *ad;
926 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
928 EnterCriticalSection(&authcache_cs);
929 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
930 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
937 heap_free(ad->password);
938 heap_free(ad->domain);
940 ad = heap_alloc(sizeof(authorizationData));
942 LeaveCriticalSection(&authcache_cs);
946 ad->host = heap_strdupW(host);
947 ad->scheme = heap_strdupW(scheme);
948 list_add_head(&authorizationCache, &ad->entry);
951 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
952 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
953 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
954 ad->user_len = nt_auth_identity->UserLength;
955 ad->password_len = nt_auth_identity->PasswordLength;
956 ad->domain_len = nt_auth_identity->DomainLength;
958 if(!ad->host || !ad->scheme || !ad->user || !ad->password
959 || (nt_auth_identity->Domain && !ad->domain)) {
961 heap_free(ad->scheme);
963 heap_free(ad->password);
964 heap_free(ad->domain);
965 list_remove(&ad->entry);
969 LeaveCriticalSection(&authcache_cs);
972 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
973 struct HttpAuthInfo **ppAuthInfo,
974 LPWSTR domain_and_username, LPWSTR password,
977 SECURITY_STATUS sec_status;
978 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
980 LPWSTR szRealm = NULL;
982 TRACE("%s\n", debugstr_w(pszAuthValue));
989 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
993 SecInvalidateHandle(&pAuthInfo->cred);
994 SecInvalidateHandle(&pAuthInfo->ctx);
995 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
997 pAuthInfo->auth_data = NULL;
998 pAuthInfo->auth_data_len = 0;
999 pAuthInfo->finished = FALSE;
1001 if (is_basic_auth_value(pszAuthValue,NULL))
1003 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1004 pAuthInfo->scheme = heap_strdupW(szBasic);
1005 if (!pAuthInfo->scheme)
1007 heap_free(pAuthInfo);
1014 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1016 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1017 if (!pAuthInfo->scheme)
1019 heap_free(pAuthInfo);
1023 if (domain_and_username)
1025 WCHAR *user = strchrW(domain_and_username, '\\');
1026 WCHAR *domain = domain_and_username;
1028 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1030 pAuthData = &nt_auth_identity;
1035 user = domain_and_username;
1039 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1040 nt_auth_identity.User = user;
1041 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1042 nt_auth_identity.Domain = domain;
1043 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1044 nt_auth_identity.Password = password;
1045 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1047 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1049 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1050 pAuthData = &nt_auth_identity;
1052 /* use default credentials */
1055 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1056 SECPKG_CRED_OUTBOUND, NULL,
1058 NULL, &pAuthInfo->cred,
1061 if(pAuthData && !domain_and_username) {
1062 heap_free(nt_auth_identity.User);
1063 heap_free(nt_auth_identity.Domain);
1064 heap_free(nt_auth_identity.Password);
1067 if (sec_status == SEC_E_OK)
1069 PSecPkgInfoW sec_pkg_info;
1070 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1071 if (sec_status == SEC_E_OK)
1073 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1074 FreeContextBuffer(sec_pkg_info);
1077 if (sec_status != SEC_E_OK)
1079 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1080 debugstr_w(pAuthInfo->scheme), sec_status);
1081 heap_free(pAuthInfo->scheme);
1082 heap_free(pAuthInfo);
1086 *ppAuthInfo = pAuthInfo;
1088 else if (pAuthInfo->finished)
1091 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1092 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1094 ERR("authentication scheme changed from %s to %s\n",
1095 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1099 if (is_basic_auth_value(pszAuthValue,&szRealm))
1103 char *auth_data = NULL;
1104 UINT auth_data_len = 0;
1106 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1108 if (!domain_and_username)
1110 if (host && szRealm)
1111 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1112 if (auth_data_len == 0)
1120 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1121 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1123 /* length includes a nul terminator, which will be re-used for the ':' */
1124 auth_data = heap_alloc(userlen + 1 + passlen);
1131 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1132 auth_data[userlen] = ':';
1133 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1134 auth_data_len = userlen + 1 + passlen;
1135 if (host && szRealm)
1136 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1139 pAuthInfo->auth_data = auth_data;
1140 pAuthInfo->auth_data_len = auth_data_len;
1141 pAuthInfo->finished = TRUE;
1147 LPCWSTR pszAuthData;
1148 SecBufferDesc out_desc, in_desc;
1150 unsigned char *buffer;
1151 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1152 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1154 in.BufferType = SECBUFFER_TOKEN;
1158 in_desc.ulVersion = 0;
1159 in_desc.cBuffers = 1;
1160 in_desc.pBuffers = ∈
1162 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1163 if (*pszAuthData == ' ')
1166 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1167 in.pvBuffer = heap_alloc(in.cbBuffer);
1168 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1171 buffer = heap_alloc(pAuthInfo->max_token);
1173 out.BufferType = SECBUFFER_TOKEN;
1174 out.cbBuffer = pAuthInfo->max_token;
1175 out.pvBuffer = buffer;
1177 out_desc.ulVersion = 0;
1178 out_desc.cBuffers = 1;
1179 out_desc.pBuffers = &out;
1181 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1182 first ? NULL : &pAuthInfo->ctx,
1183 first ? request->server->name : NULL,
1184 context_req, 0, SECURITY_NETWORK_DREP,
1185 in.pvBuffer ? &in_desc : NULL,
1186 0, &pAuthInfo->ctx, &out_desc,
1187 &pAuthInfo->attr, &pAuthInfo->exp);
1188 if (sec_status == SEC_E_OK)
1190 pAuthInfo->finished = TRUE;
1191 pAuthInfo->auth_data = out.pvBuffer;
1192 pAuthInfo->auth_data_len = out.cbBuffer;
1193 TRACE("sending last auth packet\n");
1195 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1197 pAuthInfo->auth_data = out.pvBuffer;
1198 pAuthInfo->auth_data_len = out.cbBuffer;
1199 TRACE("sending next auth packet\n");
1203 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1204 heap_free(out.pvBuffer);
1205 destroy_authinfo(pAuthInfo);
1214 /***********************************************************************
1215 * HTTP_HttpAddRequestHeadersW (internal)
1217 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1218 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1223 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1225 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1227 if( dwHeaderLength == ~0U )
1228 len = strlenW(lpszHeader);
1230 len = dwHeaderLength;
1231 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1232 lstrcpynW( buffer, lpszHeader, len + 1);
1238 LPWSTR * pFieldAndValue;
1240 lpszEnd = lpszStart;
1242 while (*lpszEnd != '\0')
1244 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1249 if (*lpszStart == '\0')
1252 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1255 lpszEnd++; /* Jump over newline */
1257 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1258 if (*lpszStart == '\0')
1260 /* Skip 0-length headers */
1261 lpszStart = lpszEnd;
1262 res = ERROR_SUCCESS;
1265 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1268 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1269 if (res == ERROR_SUCCESS)
1270 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1271 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1272 HTTP_FreeTokens(pFieldAndValue);
1275 lpszStart = lpszEnd;
1276 } while (res == ERROR_SUCCESS);
1282 /***********************************************************************
1283 * HttpAddRequestHeadersW (WININET.@)
1285 * Adds one or more HTTP header to the request handler
1288 * On Windows if dwHeaderLength includes the trailing '\0', then
1289 * HttpAddRequestHeadersW() adds it too. However this results in an
1290 * invalid HTTP header which is rejected by some servers so we probably
1291 * don't need to match Windows on that point.
1298 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1299 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1301 http_request_t *request;
1302 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1304 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1309 request = (http_request_t*) get_handle_object( hHttpRequest );
1310 if (request && request->hdr.htype == WH_HHTTPREQ)
1311 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1313 WININET_Release( &request->hdr );
1315 if(res != ERROR_SUCCESS)
1317 return res == ERROR_SUCCESS;
1320 /***********************************************************************
1321 * HttpAddRequestHeadersA (WININET.@)
1323 * Adds one or more HTTP header to the request handler
1330 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1331 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1337 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1339 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1340 hdr = heap_alloc(len*sizeof(WCHAR));
1341 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1342 if( dwHeaderLength != ~0U )
1343 dwHeaderLength = len;
1345 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1351 static void free_accept_types( WCHAR **accept_types )
1353 WCHAR *ptr, **types = accept_types;
1356 while ((ptr = *types))
1361 heap_free( accept_types );
1364 static WCHAR **convert_accept_types( const char **accept_types )
1367 const char **types = accept_types;
1369 BOOL invalid_pointer = FALSE;
1371 if (!types) return NULL;
1377 /* find out how many there are */
1378 if (*types && **types)
1380 TRACE("accept type: %s\n", debugstr_a(*types));
1386 WARN("invalid accept type pointer\n");
1387 invalid_pointer = TRUE;
1392 if (invalid_pointer) return NULL;
1393 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1395 types = accept_types;
1398 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1401 typesW[count] = NULL;
1405 /***********************************************************************
1406 * HttpOpenRequestA (WININET.@)
1408 * Open a HTTP request handle
1411 * HINTERNET a HTTP request handle on success
1415 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1416 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1417 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1418 DWORD dwFlags, DWORD_PTR dwContext)
1420 LPWSTR szVerb = NULL, szObjectName = NULL;
1421 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1422 HINTERNET rc = FALSE;
1424 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1425 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1426 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1427 dwFlags, dwContext);
1431 szVerb = heap_strdupAtoW(lpszVerb);
1438 szObjectName = heap_strdupAtoW(lpszObjectName);
1439 if ( !szObjectName )
1445 szVersion = heap_strdupAtoW(lpszVersion);
1452 szReferrer = heap_strdupAtoW(lpszReferrer);
1457 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1458 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1459 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1462 free_accept_types(szAcceptTypes);
1463 heap_free(szReferrer);
1464 heap_free(szVersion);
1465 heap_free(szObjectName);
1470 /***********************************************************************
1473 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1476 static const CHAR HTTP_Base64Enc[] =
1477 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1481 /* first 6 bits, all from bin[0] */
1482 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1483 x = (bin[0] & 3) << 4;
1485 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1488 base64[n++] = HTTP_Base64Enc[x];
1493 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1494 x = ( bin[1] & 0x0f ) << 2;
1496 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1499 base64[n++] = HTTP_Base64Enc[x];
1503 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1505 /* last 6 bits, all from bin [2] */
1506 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1514 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1515 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1516 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1517 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1518 static const signed char HTTP_Base64Dec[256] =
1520 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1521 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1522 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1523 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1524 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1525 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1526 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1527 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1528 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1529 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1530 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1531 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1532 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1533 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1534 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1535 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1536 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1537 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1538 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1539 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1540 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1541 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1542 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1543 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1544 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1545 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1549 /***********************************************************************
1552 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1560 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1561 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1562 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1563 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1565 WARN("invalid base64: %s\n", debugstr_w(base64));
1569 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1572 if ((base64[2] == '=') && (base64[3] == '='))
1574 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1575 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1577 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1581 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1584 if (base64[3] == '=')
1586 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1587 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1589 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1593 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1602 /***********************************************************************
1603 * HTTP_InsertAuthorization
1605 * Insert or delete the authorization field in the request header.
1607 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1611 static const WCHAR wszSpace[] = {' ',0};
1612 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1614 WCHAR *authorization = NULL;
1616 if (pAuthInfo->auth_data_len)
1618 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1619 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1620 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1624 strcpyW(authorization, pAuthInfo->scheme);
1625 strcatW(authorization, wszSpace);
1626 HTTP_EncodeBase64(pAuthInfo->auth_data,
1627 pAuthInfo->auth_data_len,
1628 authorization+strlenW(authorization));
1630 /* clear the data as it isn't valid now that it has been sent to the
1631 * server, unless it's Basic authentication which doesn't do
1632 * connection tracking */
1633 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1635 heap_free(pAuthInfo->auth_data);
1636 pAuthInfo->auth_data = NULL;
1637 pAuthInfo->auth_data_len = 0;
1641 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1643 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1644 heap_free(authorization);
1649 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1651 static const WCHAR slash[] = { '/',0 };
1652 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1653 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1654 http_session_t *session = req->session;
1655 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1658 size = sizeof(new_location);
1659 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1661 URL_COMPONENTSW UrlComponents;
1663 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1664 strcpyW( url, new_location );
1666 ZeroMemory(&UrlComponents,sizeof(URL_COMPONENTSW));
1667 if(InternetCrackUrlW(url, 0, 0, &UrlComponents)) goto done;
1671 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1672 size += strlenW( session->hostName ) + strlenW( req->path );
1674 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1676 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1677 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1679 sprintfW( url, format, session->hostName, session->hostPort );
1680 if (req->path[0] != '/') strcatW( url, slash );
1681 strcatW( url, req->path );
1684 TRACE("url=%s\n", debugstr_w(url));
1688 /***********************************************************************
1689 * HTTP_DealWithProxy
1691 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1693 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1694 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1695 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1696 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1697 static WCHAR szNul[] = { 0 };
1698 URL_COMPONENTSW UrlComponents;
1699 server_t *new_server;
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 new_server = get_server(UrlComponents.lpszHostName, UrlComponents.nPort);
1731 server_release(request->server);
1732 request->server = new_server;
1734 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1738 static DWORD HTTP_ResolveName(http_request_t *request)
1740 server_t *server = request->server;
1744 if(server->addr_len)
1745 return ERROR_SUCCESS;
1747 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1748 INTERNET_STATUS_RESOLVING_NAME,
1750 (strlenW(server->name)+1) * sizeof(WCHAR));
1752 addr_len = sizeof(server->addr);
1753 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1754 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1756 switch(server->addr.ss_family) {
1758 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1761 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1764 WARN("unsupported family %d\n", server->addr.ss_family);
1765 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1768 server->addr_len = addr_len;
1769 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1770 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1771 INTERNET_STATUS_NAME_RESOLVED,
1772 server->addr_str, strlen(server->addr_str)+1);
1774 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1775 return ERROR_SUCCESS;
1778 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1780 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1781 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1782 static const WCHAR slash[] = { '/',0 };
1783 LPHTTPHEADERW host_header;
1786 host_header = HTTP_GetHeader(req, hostW);
1790 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1794 strcpyW(buf, scheme);
1795 strcatW(buf, host_header->lpszValue);
1796 if (req->path[0] != '/')
1797 strcatW(buf, slash);
1798 strcatW(buf, req->path);
1803 /***********************************************************************
1804 * HTTPREQ_Destroy (internal)
1806 * Deallocate request handle
1809 static void HTTPREQ_Destroy(object_header_t *hdr)
1811 http_request_t *request = (http_request_t*) hdr;
1816 if(request->hCacheFile) {
1817 WCHAR url[INTERNET_MAX_URL_LENGTH];
1819 CloseHandle(request->hCacheFile);
1821 if(HTTP_GetRequestURL(request, url)) {
1824 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1825 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1826 request->last_modified, NORMAL_CACHE_ENTRY,
1827 request->rawHeaders, headersLen, NULL, 0);
1830 heap_free(request->cacheFile);
1832 request->read_section.DebugInfo->Spare[0] = 0;
1833 DeleteCriticalSection( &request->read_section );
1834 WININET_Release(&request->session->hdr);
1836 destroy_authinfo(request->authInfo);
1837 destroy_authinfo(request->proxyAuthInfo);
1840 server_release(request->server);
1842 heap_free(request->path);
1843 heap_free(request->verb);
1844 heap_free(request->rawHeaders);
1845 heap_free(request->version);
1846 heap_free(request->statusText);
1848 for (i = 0; i < request->nCustHeaders; i++)
1850 heap_free(request->custHeaders[i].lpszField);
1851 heap_free(request->custHeaders[i].lpszValue);
1853 destroy_data_stream(request->data_stream);
1854 heap_free(request->custHeaders);
1857 static void http_release_netconn(http_request_t *req, BOOL reuse)
1859 TRACE("%p %p\n",req, req->netconn);
1864 if(reuse && req->netconn->keep_alive) {
1867 EnterCriticalSection(&connection_pool_cs);
1869 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1870 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1871 req->netconn = NULL;
1873 run_collector = !collector_running;
1874 collector_running = TRUE;
1876 LeaveCriticalSection(&connection_pool_cs);
1879 HANDLE thread = NULL;
1882 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1884 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1886 EnterCriticalSection(&connection_pool_cs);
1887 collector_running = FALSE;
1888 LeaveCriticalSection(&connection_pool_cs);
1891 FreeLibrary(module);
1894 CloseHandle(thread);
1899 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1900 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1902 free_netconn(req->netconn);
1903 req->netconn = NULL;
1905 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1906 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1909 static void drain_content(http_request_t *req)
1913 if (!req->netconn) return;
1915 if (req->contentLength == -1)
1917 else if(!strcmpW(req->verb, szHEAD))
1920 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1922 http_release_netconn(req, try_reuse);
1925 static BOOL HTTP_KeepAlive(http_request_t *request)
1927 WCHAR szVersion[10];
1928 WCHAR szConnectionResponse[20];
1929 DWORD dwBufferSize = sizeof(szVersion);
1930 BOOL keepalive = FALSE;
1932 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1933 * the connection is keep-alive by default */
1934 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1935 && !strcmpiW(szVersion, g_szHttp1_1))
1940 dwBufferSize = sizeof(szConnectionResponse);
1941 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1942 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1944 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1950 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1952 http_request_t *req = (http_request_t*)hdr;
1957 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1959 http_request_t *req = (http_request_t*)hdr;
1962 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1964 http_session_t *session = req->session;
1965 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1967 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1969 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1970 return ERROR_INSUFFICIENT_BUFFER;
1971 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1972 /* FIXME: can't get a SOCKET from our connection since we don't use
1976 /* FIXME: get source port from req->netConnection */
1977 info->SourcePort = 0;
1978 info->DestPort = session->hostPort;
1980 if (HTTP_KeepAlive(req))
1981 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1982 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1983 info->Flags |= IDSI_FLAG_PROXY;
1984 if (req->netconn->useSSL)
1985 info->Flags |= IDSI_FLAG_SECURE;
1987 return ERROR_SUCCESS;
1991 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1993 case INTERNET_OPTION_SECURITY_FLAGS:
1997 if (*size < sizeof(ULONG))
1998 return ERROR_INSUFFICIENT_BUFFER;
2000 *size = sizeof(DWORD);
2001 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
2002 *(DWORD *)buffer = flags;
2004 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
2005 return ERROR_SUCCESS;
2008 case INTERNET_OPTION_HANDLE_TYPE:
2009 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2011 if (*size < sizeof(ULONG))
2012 return ERROR_INSUFFICIENT_BUFFER;
2014 *size = sizeof(DWORD);
2015 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2016 return ERROR_SUCCESS;
2018 case INTERNET_OPTION_URL: {
2019 WCHAR url[INTERNET_MAX_URL_LENGTH];
2024 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2026 TRACE("INTERNET_OPTION_URL\n");
2028 host = HTTP_GetHeader(req, hostW);
2029 strcpyW(url, httpW);
2030 strcatW(url, host->lpszValue);
2031 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2033 strcatW(url, req->path);
2035 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2038 len = (strlenW(url)+1) * sizeof(WCHAR);
2040 return ERROR_INSUFFICIENT_BUFFER;
2043 strcpyW(buffer, url);
2044 return ERROR_SUCCESS;
2046 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2048 return ERROR_INSUFFICIENT_BUFFER;
2051 return ERROR_SUCCESS;
2055 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2056 INTERNET_CACHE_ENTRY_INFOW *info;
2057 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2058 WCHAR url[INTERNET_MAX_URL_LENGTH];
2059 DWORD nbytes, error;
2062 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2064 if (*size < sizeof(*ts))
2066 *size = sizeof(*ts);
2067 return ERROR_INSUFFICIENT_BUFFER;
2070 HTTP_GetRequestURL(req, url);
2071 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2072 error = GetLastError();
2073 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2075 if (!(info = heap_alloc(nbytes)))
2076 return ERROR_OUTOFMEMORY;
2078 GetUrlCacheEntryInfoW(url, info, &nbytes);
2080 ts->ftExpires = info->ExpireTime;
2081 ts->ftLastModified = info->LastModifiedTime;
2084 *size = sizeof(*ts);
2085 return ERROR_SUCCESS;
2090 case INTERNET_OPTION_DATAFILE_NAME: {
2093 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2095 if(!req->cacheFile) {
2097 return ERROR_INTERNET_ITEM_NOT_FOUND;
2101 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2102 if(*size < req_size)
2103 return ERROR_INSUFFICIENT_BUFFER;
2106 memcpy(buffer, req->cacheFile, *size);
2107 return ERROR_SUCCESS;
2109 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2110 if (req_size > *size)
2111 return ERROR_INSUFFICIENT_BUFFER;
2113 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2114 -1, buffer, *size, NULL, NULL);
2115 return ERROR_SUCCESS;
2119 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2120 PCCERT_CONTEXT context;
2122 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2123 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2124 return ERROR_INSUFFICIENT_BUFFER;
2127 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2129 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2132 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2133 info->ftExpiry = context->pCertInfo->NotAfter;
2134 info->ftStart = context->pCertInfo->NotBefore;
2135 len = CertNameToStrA(context->dwCertEncodingType,
2136 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2137 info->lpszSubjectInfo = LocalAlloc(0, len);
2138 if(info->lpszSubjectInfo)
2139 CertNameToStrA(context->dwCertEncodingType,
2140 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2141 info->lpszSubjectInfo, len);
2142 len = CertNameToStrA(context->dwCertEncodingType,
2143 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2144 info->lpszIssuerInfo = LocalAlloc(0, len);
2145 if(info->lpszIssuerInfo)
2146 CertNameToStrA(context->dwCertEncodingType,
2147 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2148 info->lpszIssuerInfo, len);
2149 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2150 CertFreeCertificateContext(context);
2151 return ERROR_SUCCESS;
2154 case INTERNET_OPTION_CONNECT_TIMEOUT:
2155 if (*size < sizeof(DWORD))
2156 return ERROR_INSUFFICIENT_BUFFER;
2158 *size = sizeof(DWORD);
2159 *(DWORD *)buffer = req->connect_timeout;
2160 return ERROR_SUCCESS;
2161 case INTERNET_OPTION_REQUEST_FLAGS: {
2164 if (*size < sizeof(DWORD))
2165 return ERROR_INSUFFICIENT_BUFFER;
2167 /* FIXME: Add support for:
2168 * INTERNET_REQFLAG_FROM_CACHE
2169 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2172 if(req->session->appInfo->proxy)
2173 flags |= INTERNET_REQFLAG_VIA_PROXY;
2174 if(!req->rawHeaders)
2175 flags |= INTERNET_REQFLAG_NO_HEADERS;
2177 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2179 *size = sizeof(DWORD);
2180 *(DWORD*)buffer = flags;
2181 return ERROR_SUCCESS;
2185 return INET_QueryOption(hdr, option, buffer, size, unicode);
2188 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2190 http_request_t *req = (http_request_t*)hdr;
2193 case INTERNET_OPTION_SECURITY_FLAGS:
2197 if (!buffer || size != sizeof(DWORD))
2198 return ERROR_INVALID_PARAMETER;
2199 flags = *(DWORD *)buffer;
2200 TRACE("%08x\n", flags);
2201 req->security_flags = flags;
2203 req->netconn->security_flags = flags;
2204 return ERROR_SUCCESS;
2206 case INTERNET_OPTION_CONNECT_TIMEOUT:
2207 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2208 req->connect_timeout = *(DWORD *)buffer;
2209 return ERROR_SUCCESS;
2211 case INTERNET_OPTION_SEND_TIMEOUT:
2212 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2213 req->send_timeout = *(DWORD *)buffer;
2214 return ERROR_SUCCESS;
2216 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2217 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2218 req->receive_timeout = *(DWORD *)buffer;
2219 return ERROR_SUCCESS;
2221 case INTERNET_OPTION_USERNAME:
2222 heap_free(req->session->userName);
2223 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2224 return ERROR_SUCCESS;
2226 case INTERNET_OPTION_PASSWORD:
2227 heap_free(req->session->password);
2228 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2229 return ERROR_SUCCESS;
2230 case INTERNET_OPTION_HTTP_DECODING:
2231 if(size != sizeof(BOOL))
2232 return ERROR_INVALID_PARAMETER;
2233 req->decoding = *(BOOL*)buffer;
2234 return ERROR_SUCCESS;
2237 return INET_SetOption(hdr, option, buffer, size);
2240 /* read some more data into the read buffer (the read section must be held) */
2241 static DWORD read_more_data( http_request_t *req, int maxlen )
2248 /* move existing data to the start of the buffer */
2250 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2254 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2256 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2257 maxlen - req->read_size, 0, &len );
2258 if(res == ERROR_SUCCESS)
2259 req->read_size += len;
2264 /* remove some amount of data from the read buffer (the read section must be held) */
2265 static void remove_data( http_request_t *req, int count )
2267 if (!(req->read_size -= count)) req->read_pos = 0;
2268 else req->read_pos += count;
2271 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2273 int count, bytes_read, pos = 0;
2276 EnterCriticalSection( &req->read_section );
2279 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2283 count = eol - (req->read_buf + req->read_pos);
2284 bytes_read = count + 1;
2286 else count = bytes_read = req->read_size;
2288 count = min( count, *len - pos );
2289 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2291 remove_data( req, bytes_read );
2294 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2297 TRACE( "returning empty string %u\n", res);
2298 LeaveCriticalSection( &req->read_section );
2299 INTERNET_SetLastError(res);
2303 LeaveCriticalSection( &req->read_section );
2307 if (pos && buffer[pos - 1] == '\r') pos--;
2310 buffer[*len - 1] = 0;
2311 TRACE( "returning %s\n", debugstr_a(buffer));
2315 /* check if we have reached the end of the data to read (the read section must be held) */
2316 static BOOL end_of_read_data( http_request_t *req )
2318 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2321 /* fetch some more data into the read buffer (the read section must be held) */
2322 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2326 if(req->read_size == sizeof(req->read_buf))
2327 return ERROR_SUCCESS;
2331 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2335 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2336 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2337 req->read_size += read;
2339 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2345 /* return the size of data available to be read immediately (the read section must be held) */
2346 static DWORD get_avail_data( http_request_t *req )
2348 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2351 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2353 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2357 NETCON_query_data_available(req->netconn, &avail);
2358 return netconn_stream->content_length == ~0u
2360 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2363 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2365 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2366 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2369 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2370 DWORD *read, read_mode_t read_mode)
2372 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2375 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2377 if(read_mode == READMODE_NOBLOCK)
2378 size = min(size, netconn_get_avail_data(stream, req));
2380 if(size && req->netconn) {
2381 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2384 netconn_stream->content_length = netconn_stream->content_read;
2387 netconn_stream->content_read += *read = len;
2388 TRACE("read %u bytes\n", len);
2389 return ERROR_SUCCESS;
2392 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2394 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2399 if(netconn_end_of_data(stream, req))
2403 avail = netconn_get_avail_data(stream, req);
2407 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2410 netconn_stream->content_read += len;
2411 }while(netconn_stream->content_read < netconn_stream->content_length);
2416 static void netconn_destroy(data_stream_t *stream)
2420 static const data_stream_vtbl_t netconn_stream_vtbl = {
2421 netconn_get_avail_data,
2422 netconn_end_of_data,
2424 netconn_drain_content,
2428 /* read some more data into the read buffer (the read section must be held) */
2429 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2434 if (stream->buf_pos)
2436 /* move existing data to the start of the buffer */
2437 if(stream->buf_size)
2438 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2439 stream->buf_pos = 0;
2442 if (maxlen == -1) maxlen = sizeof(stream->buf);
2444 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2445 maxlen - stream->buf_size, 0, &len );
2446 if(res == ERROR_SUCCESS)
2447 stream->buf_size += len;
2452 /* remove some amount of data from the read buffer (the read section must be held) */
2453 static void remove_chunked_data(chunked_stream_t *stream, int count)
2455 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2456 else stream->buf_pos += count;
2459 /* discard data contents until we reach end of line (the read section must be held) */
2460 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2466 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2469 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2472 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2473 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2474 } while (stream->buf_size);
2475 return ERROR_SUCCESS;
2478 /* read the size of the next chunk (the read section must be held) */
2479 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2482 DWORD chunk_size = 0, res;
2484 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2489 while (stream->buf_size)
2491 char ch = stream->buf[stream->buf_pos];
2492 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2493 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2494 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2495 else if (ch == ';' || ch == '\r' || ch == '\n')
2497 TRACE( "reading %u byte chunk\n", chunk_size );
2498 stream->chunk_size = chunk_size;
2499 req->contentLength += chunk_size;
2500 return discard_chunked_eol(stream, req);
2502 remove_chunked_data(stream, 1);
2504 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2505 if (!stream->buf_size)
2507 stream->chunk_size = 0;
2508 return ERROR_SUCCESS;
2513 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2515 /* Allow reading only from read buffer */
2519 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2521 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2522 return !chunked_stream->chunk_size;
2525 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2526 DWORD *read, read_mode_t read_mode)
2528 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2529 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2531 if(chunked_stream->chunk_size == ~0u) {
2532 res = start_next_chunk(chunked_stream, req);
2533 if(res != ERROR_SUCCESS)
2537 while(size && chunked_stream->chunk_size) {
2538 if(chunked_stream->buf_size) {
2539 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2541 /* this could block */
2542 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2545 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2546 remove_chunked_data(chunked_stream, read_bytes);
2548 read_bytes = min(size, chunked_stream->chunk_size);
2550 if(read_mode == READMODE_NOBLOCK) {
2553 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2555 if(read_bytes > avail)
2558 /* this could block */
2559 if(read_bytes == chunked_stream->chunk_size)
2563 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2564 if(res != ERROR_SUCCESS)
2568 chunked_stream->chunk_size -= read_bytes;
2570 ret_read += read_bytes;
2571 if(!chunked_stream->chunk_size) {
2572 assert(read_mode != READMODE_NOBLOCK);
2573 res = start_next_chunk(chunked_stream, req);
2574 if(res != ERROR_SUCCESS)
2578 if(read_mode == READMODE_ASYNC)
2579 read_mode = READMODE_NOBLOCK;
2582 TRACE("read %u bytes\n", ret_read);
2587 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2589 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2591 /* FIXME: we can do better */
2592 return !chunked_stream->chunk_size;
2595 static void chunked_destroy(data_stream_t *stream)
2597 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2598 heap_free(chunked_stream);
2601 static const data_stream_vtbl_t chunked_stream_vtbl = {
2602 chunked_get_avail_data,
2603 chunked_end_of_data,
2605 chunked_drain_content,
2609 /* set the request content length based on the headers */
2610 static DWORD set_content_length(http_request_t *request)
2612 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2616 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2617 request->contentLength = request->netconn_stream.content_length = 0;
2618 return ERROR_SUCCESS;
2621 size = sizeof(request->contentLength);
2622 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2623 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2624 request->contentLength = ~0u;
2625 request->netconn_stream.content_length = request->contentLength;
2626 request->netconn_stream.content_read = request->read_size;
2628 size = sizeof(encoding);
2629 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2630 !strcmpiW(encoding, szChunked))
2632 chunked_stream_t *chunked_stream;
2634 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2636 return ERROR_OUTOFMEMORY;
2638 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2639 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2640 chunked_stream->chunk_size = ~0u;
2642 if(request->read_size) {
2643 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2644 chunked_stream->buf_size = request->read_size;
2645 request->read_size = request->read_pos = 0;
2648 request->data_stream = &chunked_stream->data_stream;
2649 request->contentLength = ~0u;
2650 request->read_chunked = TRUE;
2653 if(request->decoding) {
2656 static const WCHAR gzipW[] = {'g','z','i','p',0};
2658 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2659 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2660 return init_gzip_stream(request);
2663 return ERROR_SUCCESS;
2666 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2668 INTERNET_ASYNC_RESULT iar;
2670 iar.dwResult = result;
2671 iar.dwError = error;
2673 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2674 sizeof(INTERNET_ASYNC_RESULT));
2677 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2679 DWORD res, read = 0, avail = 0;
2684 EnterCriticalSection( &req->read_section );
2686 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2687 res = refill_read_buffer(req, mode, &read);
2688 if(res == ERROR_SUCCESS && !first_notif)
2689 avail = get_avail_data(req);
2691 LeaveCriticalSection( &req->read_section );
2693 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2694 WARN("res %u read %u, closing connection\n", res, read);
2695 http_release_netconn(req, FALSE);
2698 if(res == ERROR_SUCCESS)
2699 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2701 send_request_complete(req, 0, res);
2704 /* read data from the http connection (the read section must be held) */
2705 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2707 DWORD current_read = 0, ret_read = 0;
2708 read_mode_t read_mode;
2709 DWORD res = ERROR_SUCCESS;
2711 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2713 EnterCriticalSection( &req->read_section );
2715 if(req->read_size) {
2716 ret_read = min(size, req->read_size);
2717 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2718 req->read_size -= ret_read;
2719 req->read_pos += ret_read;
2720 if(read_mode == READMODE_ASYNC)
2721 read_mode = READMODE_NOBLOCK;
2724 if(ret_read < size) {
2725 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2726 ret_read += current_read;
2729 LeaveCriticalSection( &req->read_section );
2732 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2734 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2738 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2740 WARN("WriteFile failed: %u\n", GetLastError());
2743 if(size && !ret_read)
2744 http_release_netconn(req, res == ERROR_SUCCESS);
2750 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2752 http_request_t *req = (http_request_t*)hdr;
2755 EnterCriticalSection( &req->read_section );
2756 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2757 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2759 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2760 if(res == ERROR_SUCCESS)
2762 LeaveCriticalSection( &req->read_section );
2767 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2769 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2770 http_request_t *req = (http_request_t*)workRequest->hdr;
2773 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2775 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2776 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2778 send_request_complete(req, res == ERROR_SUCCESS, res);
2781 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2782 DWORD flags, DWORD_PTR context)
2784 http_request_t *req = (http_request_t*)hdr;
2785 DWORD res, size, read, error = ERROR_SUCCESS;
2787 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2788 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2790 if (buffers->dwStructSize != sizeof(*buffers))
2791 return ERROR_INVALID_PARAMETER;
2793 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2795 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2797 WORKREQUEST workRequest;
2799 if (TryEnterCriticalSection( &req->read_section ))
2801 if (get_avail_data(req))
2803 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2804 &buffers->dwBufferLength, FALSE);
2805 size = buffers->dwBufferLength;
2806 LeaveCriticalSection( &req->read_section );
2809 LeaveCriticalSection( &req->read_section );
2812 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2813 workRequest.hdr = WININET_AddRef(&req->hdr);
2814 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2816 INTERNET_AsyncCall(&workRequest);
2818 return ERROR_IO_PENDING;
2822 size = buffers->dwBufferLength;
2824 EnterCriticalSection( &req->read_section );
2825 if(hdr->dwError == ERROR_SUCCESS)
2826 hdr->dwError = INTERNET_HANDLE_IN_USE;
2827 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2828 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2831 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2832 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2833 if(res != ERROR_SUCCESS)
2836 read += buffers->dwBufferLength;
2837 if(read == size || end_of_read_data(req))
2840 LeaveCriticalSection( &req->read_section );
2842 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2843 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2844 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2845 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2847 EnterCriticalSection( &req->read_section );
2850 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2851 hdr->dwError = ERROR_SUCCESS;
2853 error = hdr->dwError;
2855 LeaveCriticalSection( &req->read_section );
2856 size = buffers->dwBufferLength;
2857 buffers->dwBufferLength = read;
2860 if (res == ERROR_SUCCESS) {
2861 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2862 &size, sizeof(size));
2865 return res==ERROR_SUCCESS ? error : res;
2868 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2870 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2871 http_request_t *req = (http_request_t*)workRequest->hdr;
2874 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2876 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2877 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2879 send_request_complete(req, res == ERROR_SUCCESS, res);
2882 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2883 DWORD flags, DWORD_PTR context)
2886 http_request_t *req = (http_request_t*)hdr;
2887 DWORD res, size, read, error = ERROR_SUCCESS;
2889 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2890 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2892 if (buffers->dwStructSize != sizeof(*buffers))
2893 return ERROR_INVALID_PARAMETER;
2895 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2897 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2899 WORKREQUEST workRequest;
2901 if (TryEnterCriticalSection( &req->read_section ))
2903 if (get_avail_data(req))
2905 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2906 &buffers->dwBufferLength, FALSE);
2907 size = buffers->dwBufferLength;
2908 LeaveCriticalSection( &req->read_section );
2911 LeaveCriticalSection( &req->read_section );
2914 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2915 workRequest.hdr = WININET_AddRef(&req->hdr);
2916 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2918 INTERNET_AsyncCall(&workRequest);
2920 return ERROR_IO_PENDING;
2924 size = buffers->dwBufferLength;
2926 EnterCriticalSection( &req->read_section );
2927 if(hdr->dwError == ERROR_SUCCESS)
2928 hdr->dwError = INTERNET_HANDLE_IN_USE;
2929 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2930 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2933 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2934 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2935 if(res != ERROR_SUCCESS)
2938 read += buffers->dwBufferLength;
2939 if(read == size || end_of_read_data(req))
2942 LeaveCriticalSection( &req->read_section );
2944 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2945 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2946 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2947 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2949 EnterCriticalSection( &req->read_section );
2952 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2953 hdr->dwError = ERROR_SUCCESS;
2955 error = hdr->dwError;
2957 LeaveCriticalSection( &req->read_section );
2958 size = buffers->dwBufferLength;
2959 buffers->dwBufferLength = read;
2962 if (res == ERROR_SUCCESS) {
2963 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2964 &size, sizeof(size));
2967 return res==ERROR_SUCCESS ? error : res;
2970 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2973 http_request_t *request = (http_request_t*)hdr;
2975 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2978 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2979 if (res == ERROR_SUCCESS)
2980 request->bytesWritten += *written;
2982 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2986 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2988 http_request_t *req = (http_request_t*)workRequest->hdr;
2990 HTTP_ReceiveRequestData(req, FALSE);
2993 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2995 http_request_t *req = (http_request_t*)hdr;
2997 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2999 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3001 WORKREQUEST workRequest;
3003 /* never wait, if we can't enter the section we queue an async request right away */
3004 if (TryEnterCriticalSection( &req->read_section ))
3006 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3007 if ((*available = get_avail_data( req ))) goto done;
3008 if (end_of_read_data( req )) goto done;
3009 LeaveCriticalSection( &req->read_section );
3012 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3013 workRequest.hdr = WININET_AddRef( &req->hdr );
3015 INTERNET_AsyncCall(&workRequest);
3017 return ERROR_IO_PENDING;
3020 EnterCriticalSection( &req->read_section );
3022 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3024 refill_read_buffer( req, READMODE_ASYNC, NULL );
3025 *available = get_avail_data( req );
3029 LeaveCriticalSection( &req->read_section );
3031 TRACE( "returning %u\n", *available );
3032 return ERROR_SUCCESS;
3035 static const object_vtbl_t HTTPREQVtbl = {
3037 HTTPREQ_CloseConnection,
3038 HTTPREQ_QueryOption,
3041 HTTPREQ_ReadFileExA,
3042 HTTPREQ_ReadFileExW,
3044 HTTPREQ_QueryDataAvailable,
3048 /***********************************************************************
3049 * HTTP_HttpOpenRequestW (internal)
3051 * Open a HTTP request handle
3054 * HINTERNET a HTTP request handle on success
3058 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3059 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3060 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3061 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3063 appinfo_t *hIC = session->appInfo;
3064 http_request_t *request;
3066 DWORD len, res = ERROR_SUCCESS;
3070 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3072 return ERROR_OUTOFMEMORY;
3074 request->hdr.htype = WH_HHTTPREQ;
3075 request->hdr.dwFlags = dwFlags;
3076 request->hdr.dwContext = dwContext;
3077 request->contentLength = ~0u;
3079 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3080 request->data_stream = &request->netconn_stream.data_stream;
3081 request->connect_timeout = session->connect_timeout;
3082 request->send_timeout = session->send_timeout;
3083 request->receive_timeout = session->receive_timeout;
3085 InitializeCriticalSection( &request->read_section );
3086 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3088 WININET_AddRef( &session->hdr );
3089 request->session = session;
3090 list_add_head( &session->hdr.children, &request->hdr.entry );
3092 port = session->hostPort;
3093 if(port == INTERNET_INVALID_PORT_NUMBER)
3094 port = dwFlags & INTERNET_FLAG_SECURE ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3096 request->server = get_server(session->hostName, port);
3097 if(!request->server) {
3098 WININET_Release(&request->hdr);
3099 return ERROR_OUTOFMEMORY;
3102 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3103 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3104 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3105 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3107 if (lpszObjectName && *lpszObjectName) {
3111 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3112 if (rc != E_POINTER)
3113 len = strlenW(lpszObjectName)+1;
3114 request->path = heap_alloc(len*sizeof(WCHAR));
3115 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3116 URL_ESCAPE_SPACES_ONLY);
3119 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3120 strcpyW(request->path,lpszObjectName);
3123 static const WCHAR slashW[] = {'/',0};
3125 request->path = heap_strdupW(slashW);
3128 if (lpszReferrer && *lpszReferrer)
3129 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3131 if (lpszAcceptTypes)
3134 for (i = 0; lpszAcceptTypes[i]; i++)
3136 if (!*lpszAcceptTypes[i]) continue;
3137 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3138 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3139 HTTP_ADDHDR_FLAG_REQ |
3140 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3144 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3145 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3147 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3148 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3149 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3153 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3155 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3157 res = ERROR_OUTOFMEMORY;
3161 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3162 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3163 heap_free(host_name);
3166 HTTP_ProcessHeader(request, hostW, session->hostName,
3167 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3169 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3170 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3171 INTERNET_DEFAULT_HTTPS_PORT :
3172 INTERNET_DEFAULT_HTTP_PORT);
3174 if (hIC->proxy && hIC->proxy[0])
3175 HTTP_DealWithProxy( hIC, session, request );
3177 INTERNET_SendCallback(&session->hdr, dwContext,
3178 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3182 TRACE("<-- %u (%p)\n", res, request);
3184 if(res != ERROR_SUCCESS) {
3185 WININET_Release( &request->hdr );
3190 *ret = request->hdr.hInternet;
3191 return ERROR_SUCCESS;
3194 /***********************************************************************
3195 * HttpOpenRequestW (WININET.@)
3197 * Open a HTTP request handle
3200 * HINTERNET a HTTP request handle on success
3204 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3205 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3206 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3207 DWORD dwFlags, DWORD_PTR dwContext)
3209 http_session_t *session;
3210 HINTERNET handle = NULL;
3213 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3214 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3215 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3216 dwFlags, dwContext);
3217 if(lpszAcceptTypes!=NULL)
3220 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3221 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3224 session = (http_session_t*) get_handle_object( hHttpSession );
3225 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3227 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3232 * My tests seem to show that the windows version does not
3233 * become asynchronous until after this point. And anyhow
3234 * if this call was asynchronous then how would you get the
3235 * necessary HINTERNET pointer returned by this function.
3238 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3239 lpszVersion, lpszReferrer, lpszAcceptTypes,
3240 dwFlags, dwContext, &handle);
3243 WININET_Release( &session->hdr );
3244 TRACE("returning %p\n", handle);
3245 if(res != ERROR_SUCCESS)
3250 static const LPCWSTR header_lookup[] = {
3251 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3252 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3253 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3254 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3255 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3256 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3257 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3258 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3259 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3260 szDate, /* HTTP_QUERY_DATE = 9 */
3261 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3262 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3263 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3264 szURI, /* HTTP_QUERY_URI = 13 */
3265 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3266 NULL, /* HTTP_QUERY_COST = 15 */
3267 NULL, /* HTTP_QUERY_LINK = 16 */
3268 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3269 NULL, /* HTTP_QUERY_VERSION = 18 */
3270 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3271 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3272 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3273 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3274 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3275 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3276 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3277 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3278 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3279 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3280 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3281 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3282 NULL, /* HTTP_QUERY_FROM = 31 */
3283 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3284 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3285 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3286 szReferer, /* HTTP_QUERY_REFERER = 35 */
3287 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3288 szServer, /* HTTP_QUERY_SERVER = 37 */
3289 NULL, /* HTTP_TITLE = 38 */
3290 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3291 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3292 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3293 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3294 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3295 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3296 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3297 NULL, /* HTTP_QUERY_REFRESH = 46 */
3298 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3299 szAge, /* HTTP_QUERY_AGE = 48 */
3300 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3301 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3302 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3303 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3304 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3305 szETag, /* HTTP_QUERY_ETAG = 54 */
3306 hostW, /* HTTP_QUERY_HOST = 55 */
3307 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3308 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3309 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3310 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3311 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3312 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3313 szRange, /* HTTP_QUERY_RANGE = 62 */
3314 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3315 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3316 szVary, /* HTTP_QUERY_VARY = 65 */
3317 szVia, /* HTTP_QUERY_VIA = 66 */
3318 szWarning, /* HTTP_QUERY_WARNING = 67 */
3319 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3320 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3321 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3324 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3326 /***********************************************************************
3327 * HTTP_HttpQueryInfoW (internal)
3329 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3330 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3332 LPHTTPHEADERW lphttpHdr = NULL;
3333 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3334 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3335 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3338 /* Find requested header structure */
3341 case HTTP_QUERY_CUSTOM:
3342 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3343 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3345 case HTTP_QUERY_RAW_HEADERS_CRLF:
3349 DWORD res = ERROR_INVALID_PARAMETER;
3352 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3354 headers = request->rawHeaders;
3357 len = strlenW(headers) * sizeof(WCHAR);
3359 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3361 len += sizeof(WCHAR);
3362 res = ERROR_INSUFFICIENT_BUFFER;
3367 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3370 len = strlenW(szCrLf) * sizeof(WCHAR);
3371 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3373 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3374 res = ERROR_SUCCESS;
3376 *lpdwBufferLength = len;
3378 if (request_only) heap_free(headers);
3381 case HTTP_QUERY_RAW_HEADERS:
3383 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3385 LPWSTR pszString = lpBuffer;
3387 for (i = 0; ppszRawHeaderLines[i]; i++)
3388 size += strlenW(ppszRawHeaderLines[i]) + 1;
3390 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3392 HTTP_FreeTokens(ppszRawHeaderLines);
3393 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3394 return ERROR_INSUFFICIENT_BUFFER;
3398 for (i = 0; ppszRawHeaderLines[i]; i++)
3400 DWORD len = strlenW(ppszRawHeaderLines[i]);
3401 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3405 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3407 *lpdwBufferLength = size * sizeof(WCHAR);
3408 HTTP_FreeTokens(ppszRawHeaderLines);
3410 return ERROR_SUCCESS;
3412 case HTTP_QUERY_STATUS_TEXT:
3413 if (request->statusText)
3415 DWORD len = strlenW(request->statusText);
3416 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3418 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3419 return ERROR_INSUFFICIENT_BUFFER;
3423 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3424 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3426 *lpdwBufferLength = len * sizeof(WCHAR);
3427 return ERROR_SUCCESS;
3430 case HTTP_QUERY_VERSION:
3431 if (request->version)
3433 DWORD len = strlenW(request->version);
3434 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3436 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3437 return ERROR_INSUFFICIENT_BUFFER;
3441 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3442 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3444 *lpdwBufferLength = len * sizeof(WCHAR);
3445 return ERROR_SUCCESS;
3448 case HTTP_QUERY_CONTENT_ENCODING:
3449 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3450 requested_index,request_only);
3452 case HTTP_QUERY_STATUS_CODE: {
3453 DWORD res = ERROR_SUCCESS;
3456 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3461 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3462 if(*lpdwBufferLength >= sizeof(DWORD))
3463 *(DWORD*)lpBuffer = request->status_code;
3465 res = ERROR_INSUFFICIENT_BUFFER;
3466 *lpdwBufferLength = sizeof(DWORD);
3470 static const WCHAR formatW[] = {'%','u',0};
3472 size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3474 if(size <= *lpdwBufferLength)
3475 memcpy(lpBuffer, buf, size);
3477 res = ERROR_INSUFFICIENT_BUFFER;
3479 *lpdwBufferLength = size;
3484 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3486 if (level < LAST_TABLE_HEADER && header_lookup[level])
3487 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3488 requested_index,request_only);
3492 lphttpHdr = &request->custHeaders[index];
3494 /* Ensure header satisfies requested attributes */
3496 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3497 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3499 return ERROR_HTTP_HEADER_NOT_FOUND;
3502 if (lpdwIndex) (*lpdwIndex)++;
3504 /* coalesce value to requested type */
3505 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3507 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3508 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3510 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3516 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3518 tmpTM = *gmtime(&tmpTime);
3519 STHook = (SYSTEMTIME *)lpBuffer;
3520 STHook->wDay = tmpTM.tm_mday;
3521 STHook->wHour = tmpTM.tm_hour;
3522 STHook->wMilliseconds = 0;
3523 STHook->wMinute = tmpTM.tm_min;
3524 STHook->wDayOfWeek = tmpTM.tm_wday;
3525 STHook->wMonth = tmpTM.tm_mon + 1;
3526 STHook->wSecond = tmpTM.tm_sec;
3527 STHook->wYear = tmpTM.tm_year;
3529 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3530 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3531 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3533 else if (lphttpHdr->lpszValue)
3535 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3537 if (len > *lpdwBufferLength)
3539 *lpdwBufferLength = len;
3540 return ERROR_INSUFFICIENT_BUFFER;
3544 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3545 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3547 *lpdwBufferLength = len - sizeof(WCHAR);
3549 return ERROR_SUCCESS;
3552 /***********************************************************************
3553 * HttpQueryInfoW (WININET.@)
3555 * Queries for information about an HTTP request
3562 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3563 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3565 http_request_t *request;
3568 if (TRACE_ON(wininet)) {
3569 #define FE(x) { x, #x }
3570 static const wininet_flag_info query_flags[] = {
3571 FE(HTTP_QUERY_MIME_VERSION),
3572 FE(HTTP_QUERY_CONTENT_TYPE),
3573 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3574 FE(HTTP_QUERY_CONTENT_ID),
3575 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3576 FE(HTTP_QUERY_CONTENT_LENGTH),
3577 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3578 FE(HTTP_QUERY_ALLOW),
3579 FE(HTTP_QUERY_PUBLIC),
3580 FE(HTTP_QUERY_DATE),
3581 FE(HTTP_QUERY_EXPIRES),
3582 FE(HTTP_QUERY_LAST_MODIFIED),
3583 FE(HTTP_QUERY_MESSAGE_ID),
3585 FE(HTTP_QUERY_DERIVED_FROM),
3586 FE(HTTP_QUERY_COST),
3587 FE(HTTP_QUERY_LINK),
3588 FE(HTTP_QUERY_PRAGMA),
3589 FE(HTTP_QUERY_VERSION),
3590 FE(HTTP_QUERY_STATUS_CODE),
3591 FE(HTTP_QUERY_STATUS_TEXT),
3592 FE(HTTP_QUERY_RAW_HEADERS),
3593 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3594 FE(HTTP_QUERY_CONNECTION),
3595 FE(HTTP_QUERY_ACCEPT),
3596 FE(HTTP_QUERY_ACCEPT_CHARSET),
3597 FE(HTTP_QUERY_ACCEPT_ENCODING),
3598 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3599 FE(HTTP_QUERY_AUTHORIZATION),
3600 FE(HTTP_QUERY_CONTENT_ENCODING),
3601 FE(HTTP_QUERY_FORWARDED),
3602 FE(HTTP_QUERY_FROM),
3603 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3604 FE(HTTP_QUERY_LOCATION),
3605 FE(HTTP_QUERY_ORIG_URI),
3606 FE(HTTP_QUERY_REFERER),
3607 FE(HTTP_QUERY_RETRY_AFTER),
3608 FE(HTTP_QUERY_SERVER),
3609 FE(HTTP_QUERY_TITLE),
3610 FE(HTTP_QUERY_USER_AGENT),
3611 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3612 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3613 FE(HTTP_QUERY_ACCEPT_RANGES),
3614 FE(HTTP_QUERY_SET_COOKIE),
3615 FE(HTTP_QUERY_COOKIE),
3616 FE(HTTP_QUERY_REQUEST_METHOD),
3617 FE(HTTP_QUERY_REFRESH),
3618 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3620 FE(HTTP_QUERY_CACHE_CONTROL),
3621 FE(HTTP_QUERY_CONTENT_BASE),
3622 FE(HTTP_QUERY_CONTENT_LOCATION),
3623 FE(HTTP_QUERY_CONTENT_MD5),
3624 FE(HTTP_QUERY_CONTENT_RANGE),
3625 FE(HTTP_QUERY_ETAG),
3626 FE(HTTP_QUERY_HOST),
3627 FE(HTTP_QUERY_IF_MATCH),
3628 FE(HTTP_QUERY_IF_NONE_MATCH),
3629 FE(HTTP_QUERY_IF_RANGE),
3630 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3631 FE(HTTP_QUERY_MAX_FORWARDS),
3632 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3633 FE(HTTP_QUERY_RANGE),
3634 FE(HTTP_QUERY_TRANSFER_ENCODING),
3635 FE(HTTP_QUERY_UPGRADE),
3636 FE(HTTP_QUERY_VARY),
3638 FE(HTTP_QUERY_WARNING),
3639 FE(HTTP_QUERY_CUSTOM)
3641 static const wininet_flag_info modifier_flags[] = {
3642 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3643 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3644 FE(HTTP_QUERY_FLAG_NUMBER),
3645 FE(HTTP_QUERY_FLAG_COALESCE)
3648 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3649 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3652 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3653 TRACE(" Attribute:");
3654 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3655 if (query_flags[i].val == info) {
3656 TRACE(" %s", query_flags[i].name);
3660 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3661 TRACE(" Unknown (%08x)", info);
3664 TRACE(" Modifier:");
3665 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3666 if (modifier_flags[i].val & info_mod) {
3667 TRACE(" %s", modifier_flags[i].name);
3668 info_mod &= ~ modifier_flags[i].val;
3673 TRACE(" Unknown (%08x)", info_mod);
3678 request = (http_request_t*) get_handle_object( hHttpRequest );
3679 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3681 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3685 if (lpBuffer == NULL)
3686 *lpdwBufferLength = 0;
3687 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3688 lpBuffer, lpdwBufferLength, lpdwIndex);
3692 WININET_Release( &request->hdr );
3694 TRACE("%u <--\n", res);
3695 if(res != ERROR_SUCCESS)
3697 return res == ERROR_SUCCESS;
3700 /***********************************************************************
3701 * HttpQueryInfoA (WININET.@)
3703 * Queries for information about an HTTP request
3710 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3711 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3717 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3718 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3720 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3721 lpdwBufferLength, lpdwIndex );
3727 len = (*lpdwBufferLength)*sizeof(WCHAR);
3728 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3730 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3736 bufferW = heap_alloc(alloclen);
3737 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3738 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3739 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3746 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3750 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3751 lpBuffer, *lpdwBufferLength, NULL, NULL );
3752 *lpdwBufferLength = len - 1;
3754 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3757 /* since the strings being returned from HttpQueryInfoW should be
3758 * only ASCII characters, it is reasonable to assume that all of
3759 * the Unicode characters can be reduced to a single byte */
3760 *lpdwBufferLength = len / sizeof(WCHAR);
3762 heap_free( bufferW );
3766 /***********************************************************************
3767 * HTTP_GetRedirectURL (internal)
3769 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3771 static WCHAR szHttp[] = {'h','t','t','p',0};
3772 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3773 http_session_t *session = request->session;
3774 URL_COMPONENTSW urlComponents;
3775 DWORD url_length = 0;
3777 LPWSTR combined_url;
3779 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3780 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3781 urlComponents.dwSchemeLength = 0;
3782 urlComponents.lpszHostName = session->hostName;
3783 urlComponents.dwHostNameLength = 0;
3784 urlComponents.nPort = session->hostPort;
3785 urlComponents.lpszUserName = session->userName;
3786 urlComponents.dwUserNameLength = 0;
3787 urlComponents.lpszPassword = NULL;
3788 urlComponents.dwPasswordLength = 0;
3789 urlComponents.lpszUrlPath = request->path;
3790 urlComponents.dwUrlPathLength = 0;
3791 urlComponents.lpszExtraInfo = NULL;
3792 urlComponents.dwExtraInfoLength = 0;
3794 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3795 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3798 orig_url = heap_alloc(url_length);
3800 /* convert from bytes to characters */
3801 url_length = url_length / sizeof(WCHAR) - 1;
3802 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3804 heap_free(orig_url);
3809 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3810 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3812 heap_free(orig_url);
3815 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3817 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3819 heap_free(orig_url);
3820 heap_free(combined_url);
3823 heap_free(orig_url);
3824 return combined_url;
3828 /***********************************************************************
3829 * HTTP_HandleRedirect (internal)
3831 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3833 http_session_t *session = request->session;
3834 appinfo_t *hIC = session->appInfo;
3835 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3836 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3841 /* if it's an absolute path, keep the same session info */
3842 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3846 URL_COMPONENTSW urlComponents;
3847 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3848 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3849 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3850 BOOL custom_port = FALSE;
3852 static WCHAR httpW[] = {'h','t','t','p',0};
3853 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3859 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3860 urlComponents.lpszScheme = protocol;
3861 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3862 urlComponents.lpszHostName = hostName;
3863 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3864 urlComponents.lpszUserName = userName;
3865 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3866 urlComponents.lpszPassword = NULL;
3867 urlComponents.dwPasswordLength = 0;
3868 urlComponents.lpszUrlPath = path;
3869 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3870 urlComponents.lpszExtraInfo = NULL;
3871 urlComponents.dwExtraInfoLength = 0;
3872 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3873 return INTERNET_GetLastError();
3875 if(!strcmpiW(protocol, httpW)) {
3876 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3877 TRACE("redirect from secure page to non-secure page\n");
3878 /* FIXME: warn about from secure redirect to non-secure page */
3879 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3882 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3883 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3884 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3886 }else if(!strcmpiW(protocol, httpsW)) {
3887 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3888 TRACE("redirect from non-secure page to secure page\n");
3889 /* FIXME: notify about redirect to secure page */
3890 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3893 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3894 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3895 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3899 heap_free(session->hostName);
3903 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3904 len = lstrlenW(hostName);
3905 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3906 session->hostName = heap_alloc(len*sizeof(WCHAR));
3907 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3910 session->hostName = heap_strdupW(hostName);
3912 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3914 heap_free(session->userName);
3915 session->userName = NULL;
3917 session->userName = heap_strdupW(userName);
3919 reset_data_stream(request);
3921 if(!using_proxy && (strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort)) {
3922 server_t *new_server;
3924 new_server = get_server(hostName, urlComponents.nPort);
3925 server_release(request->server);
3926 request->server = new_server;
3929 heap_free(request->path);
3936 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3937 if (rc != E_POINTER)
3938 needed = strlenW(path)+1;
3939 request->path = heap_alloc(needed*sizeof(WCHAR));
3940 rc = UrlEscapeW(path, request->path, &needed,
3941 URL_ESCAPE_SPACES_ONLY);
3944 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3945 strcpyW(request->path,path);
3949 /* Remove custom content-type/length headers on redirects. */
3950 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3952 HTTP_DeleteCustomHeader(request, index);
3953 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3955 HTTP_DeleteCustomHeader(request, index);
3957 return ERROR_SUCCESS;
3960 /***********************************************************************
3961 * HTTP_build_req (internal)
3963 * concatenate all the strings in the request together
3965 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3970 for( t = list; *t ; t++ )
3971 len += strlenW( *t );
3974 str = heap_alloc(len*sizeof(WCHAR));
3977 for( t = list; *t ; t++ )
3983 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3986 LPWSTR requestString;
3992 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3993 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3994 http_session_t *session = request->session;
3998 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3999 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
4000 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
4001 heap_free( lpszPath );
4003 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4004 NULL, 0, NULL, NULL );
4005 len--; /* the nul terminator isn't needed */
4006 ascii_req = heap_alloc(len);
4007 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4008 heap_free( requestString );
4010 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4012 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4013 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4014 heap_free( ascii_req );
4015 if (res != ERROR_SUCCESS)
4018 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4020 return ERROR_HTTP_INVALID_HEADER;
4022 return ERROR_SUCCESS;
4025 static void HTTP_InsertCookies(http_request_t *request)
4027 DWORD cookie_size, size, cnt = 0;
4031 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4033 host = HTTP_GetHeader(request, hostW);
4037 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4040 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4041 if(!(cookies = heap_alloc(size)))
4044 cnt += sprintfW(cookies, cookieW);
4045 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4046 strcatW(cookies, szCrLf);
4048 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4053 static WORD HTTP_ParseWkday(LPCWSTR day)
4055 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4063 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4064 if (!strcmpiW(day, days[i]))
4071 static WORD HTTP_ParseMonth(LPCWSTR month)
4073 static const WCHAR jan[] = { 'j','a','n',0 };
4074 static const WCHAR feb[] = { 'f','e','b',0 };
4075 static const WCHAR mar[] = { 'm','a','r',0 };
4076 static const WCHAR apr[] = { 'a','p','r',0 };
4077 static const WCHAR may[] = { 'm','a','y',0 };
4078 static const WCHAR jun[] = { 'j','u','n',0 };
4079 static const WCHAR jul[] = { 'j','u','l',0 };
4080 static const WCHAR aug[] = { 'a','u','g',0 };
4081 static const WCHAR sep[] = { 's','e','p',0 };
4082 static const WCHAR oct[] = { 'o','c','t',0 };
4083 static const WCHAR nov[] = { 'n','o','v',0 };
4084 static const WCHAR dec[] = { 'd','e','c',0 };
4086 if (!strcmpiW(month, jan)) return 1;
4087 if (!strcmpiW(month, feb)) return 2;
4088 if (!strcmpiW(month, mar)) return 3;
4089 if (!strcmpiW(month, apr)) return 4;
4090 if (!strcmpiW(month, may)) return 5;
4091 if (!strcmpiW(month, jun)) return 6;
4092 if (!strcmpiW(month, jul)) return 7;
4093 if (!strcmpiW(month, aug)) return 8;
4094 if (!strcmpiW(month, sep)) return 9;
4095 if (!strcmpiW(month, oct)) return 10;
4096 if (!strcmpiW(month, nov)) return 11;
4097 if (!strcmpiW(month, dec)) return 12;
4102 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4103 * optionally preceded by whitespace.
4104 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4105 * st, and sets *str to the first character after the time format.
4107 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4113 while (isspaceW(*ptr))
4116 num = strtoulW(ptr, &nextPtr, 10);
4117 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4119 ERR("unexpected time format %s\n", debugstr_w(ptr));
4124 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4128 st->wHour = (WORD)num;
4129 num = strtoulW(ptr, &nextPtr, 10);
4130 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4132 ERR("unexpected time format %s\n", debugstr_w(ptr));
4137 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4141 st->wMinute = (WORD)num;
4142 num = strtoulW(ptr, &nextPtr, 10);
4143 if (!nextPtr || nextPtr <= ptr)
4145 ERR("unexpected time format %s\n", debugstr_w(ptr));
4150 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4155 st->wSecond = (WORD)num;
4159 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4161 static const WCHAR gmt[]= { 'G','M','T',0 };
4162 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4164 SYSTEMTIME st = { 0 };
4167 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4168 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4171 st.wDayOfWeek = HTTP_ParseWkday(day);
4172 if (st.wDayOfWeek >= 7)
4174 ERR("unexpected weekday %s\n", debugstr_w(day));
4178 while (isspaceW(*ptr))
4181 for (monthPtr = month; !isspace(*ptr) &&
4182 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4186 st.wMonth = HTTP_ParseMonth(month);
4187 if (!st.wMonth || st.wMonth > 12)
4189 ERR("unexpected month %s\n", debugstr_w(month));
4193 while (isspaceW(*ptr))
4196 num = strtoulW(ptr, &nextPtr, 10);
4197 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4199 ERR("unexpected day %s\n", debugstr_w(ptr));
4203 st.wDay = (WORD)num;
4205 while (isspaceW(*ptr))
4208 if (!HTTP_ParseTime(&st, &ptr))
4211 while (isspaceW(*ptr))
4214 num = strtoulW(ptr, &nextPtr, 10);
4215 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4217 ERR("unexpected year %s\n", debugstr_w(ptr));
4221 st.wYear = (WORD)num;
4223 while (isspaceW(*ptr))
4226 /* asctime() doesn't report a timezone, but some web servers do, so accept
4227 * with or without GMT.
4229 if (*ptr && strcmpW(ptr, gmt))
4231 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4234 return SystemTimeToFileTime(&st, ft);
4237 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4239 static const WCHAR gmt[]= { 'G','M','T',0 };
4240 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4243 SYSTEMTIME st = { 0 };
4245 ptr = strchrW(value, ',');
4248 if (ptr - value != 3)
4250 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4253 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4255 st.wDayOfWeek = HTTP_ParseWkday(day);
4256 if (st.wDayOfWeek > 6)
4258 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4263 while (isspaceW(*ptr))
4266 num = strtoulW(ptr, &nextPtr, 10);
4267 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4269 WARN("unexpected day %s\n", debugstr_w(value));
4273 st.wDay = (WORD)num;
4275 while (isspaceW(*ptr))
4278 for (monthPtr = month; !isspace(*ptr) &&
4279 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4283 st.wMonth = HTTP_ParseMonth(month);
4284 if (!st.wMonth || st.wMonth > 12)
4286 WARN("unexpected month %s\n", debugstr_w(month));
4290 while (isspaceW(*ptr))
4293 num = strtoulW(ptr, &nextPtr, 10);
4294 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4296 ERR("unexpected year %s\n", debugstr_w(value));
4300 st.wYear = (WORD)num;
4302 if (!HTTP_ParseTime(&st, &ptr))
4305 while (isspaceW(*ptr))
4308 if (strcmpW(ptr, gmt))
4310 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4313 return SystemTimeToFileTime(&st, ft);
4316 static WORD HTTP_ParseWeekday(LPCWSTR day)
4318 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4319 { 'm','o','n','d','a','y',0 },
4320 { 't','u','e','s','d','a','y',0 },
4321 { 'w','e','d','n','e','s','d','a','y',0 },
4322 { 't','h','u','r','s','d','a','y',0 },
4323 { 'f','r','i','d','a','y',0 },
4324 { 's','a','t','u','r','d','a','y',0 }};
4326 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4327 if (!strcmpiW(day, days[i]))
4334 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4336 static const WCHAR gmt[]= { 'G','M','T',0 };
4337 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4340 SYSTEMTIME st = { 0 };
4342 ptr = strchrW(value, ',');
4345 if (ptr - value == 3)
4347 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4349 st.wDayOfWeek = HTTP_ParseWkday(day);
4350 if (st.wDayOfWeek > 6)
4352 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4356 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4358 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4359 day[ptr - value + 1] = 0;
4360 st.wDayOfWeek = HTTP_ParseWeekday(day);
4361 if (st.wDayOfWeek > 6)
4363 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4369 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4374 while (isspaceW(*ptr))
4377 num = strtoulW(ptr, &nextPtr, 10);
4378 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4380 ERR("unexpected day %s\n", debugstr_w(value));
4384 st.wDay = (WORD)num;
4388 ERR("unexpected month format %s\n", debugstr_w(ptr));
4393 for (monthPtr = month; *ptr != '-' &&
4394 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4398 st.wMonth = HTTP_ParseMonth(month);
4399 if (!st.wMonth || st.wMonth > 12)
4401 ERR("unexpected month %s\n", debugstr_w(month));
4407 ERR("unexpected year format %s\n", debugstr_w(ptr));
4412 num = strtoulW(ptr, &nextPtr, 10);
4413 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4415 ERR("unexpected year %s\n", debugstr_w(value));
4419 st.wYear = (WORD)num;
4421 if (!HTTP_ParseTime(&st, &ptr))
4424 while (isspaceW(*ptr))
4427 if (strcmpW(ptr, gmt))
4429 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4432 return SystemTimeToFileTime(&st, ft);
4435 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4437 static const WCHAR zero[] = { '0',0 };
4440 if (!strcmpW(value, zero))
4442 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4445 else if (strchrW(value, ','))
4447 ret = HTTP_ParseRfc1123Date(value, ft);
4450 ret = HTTP_ParseRfc850Date(value, ft);
4452 ERR("unexpected date format %s\n", debugstr_w(value));
4457 ret = HTTP_ParseDateAsAsctime(value, ft);
4459 ERR("unexpected date format %s\n", debugstr_w(value));
4464 static void HTTP_ProcessExpires(http_request_t *request)
4466 BOOL expirationFound = FALSE;
4469 /* Look for a Cache-Control header with a max-age directive, as it takes
4470 * precedence over the Expires header.
4472 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4473 if (headerIndex != -1)
4475 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4478 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4480 LPWSTR comma = strchrW(ptr, ','), end, equal;
4485 end = ptr + strlenW(ptr);
4486 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4490 static const WCHAR max_age[] = {
4491 'm','a','x','-','a','g','e',0 };
4493 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4498 age = strtoulW(equal + 1, &nextPtr, 10);
4499 if (nextPtr > equal + 1)
4503 NtQuerySystemTime( &ft );
4504 /* Age is in seconds, FILETIME resolution is in
4505 * 100 nanosecond intervals.
4507 ft.QuadPart += age * (ULONGLONG)1000000;
4508 request->expires.dwLowDateTime = ft.u.LowPart;
4509 request->expires.dwHighDateTime = ft.u.HighPart;
4510 expirationFound = TRUE;
4517 while (isspaceW(*ptr))
4524 if (!expirationFound)
4526 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4527 if (headerIndex != -1)
4529 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4532 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4534 expirationFound = TRUE;
4535 request->expires = ft;
4539 if (!expirationFound)
4543 /* With no known age, default to 10 minutes until expiration. */
4544 NtQuerySystemTime( &t );
4545 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4546 request->expires.dwLowDateTime = t.u.LowPart;
4547 request->expires.dwHighDateTime = t.u.HighPart;
4551 static void HTTP_ProcessLastModified(http_request_t *request)
4555 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4556 if (headerIndex != -1)
4558 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4561 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4562 request->last_modified = ft;
4566 static void http_process_keep_alive(http_request_t *req)
4570 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4572 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4574 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4577 static void HTTP_CacheRequest(http_request_t *request)
4579 WCHAR url[INTERNET_MAX_URL_LENGTH];
4580 WCHAR cacheFileName[MAX_PATH+1];
4583 b = HTTP_GetRequestURL(request, url);
4585 WARN("Could not get URL\n");
4589 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4591 heap_free(request->cacheFile);
4592 CloseHandle(request->hCacheFile);
4594 request->cacheFile = heap_strdupW(cacheFileName);
4595 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4596 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4597 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4598 WARN("Could not create file: %u\n", GetLastError());
4599 request->hCacheFile = NULL;
4602 WARN("Could not create cache entry: %08x\n", GetLastError());
4606 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4608 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4609 netconn_t *netconn = NULL;
4612 assert(!request->netconn);
4613 reset_data_stream(request);
4615 res = HTTP_ResolveName(request);
4616 if(res != ERROR_SUCCESS)
4619 EnterCriticalSection(&connection_pool_cs);
4621 while(!list_empty(&request->server->conn_pool)) {
4622 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4623 list_remove(&netconn->pool_entry);
4625 if(NETCON_is_alive(netconn))
4628 TRACE("connection %p closed during idle\n", netconn);
4629 free_netconn(netconn);
4633 LeaveCriticalSection(&connection_pool_cs);
4636 TRACE("<-- reusing %p netconn\n", netconn);
4637 request->netconn = netconn;
4639 return ERROR_SUCCESS;
4642 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4643 INTERNET_STATUS_CONNECTING_TO_SERVER,
4644 request->server->addr_str,
4645 strlen(request->server->addr_str)+1);
4647 res = create_netconn(is_https, request->server, request->security_flags,
4648 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4649 request->connect_timeout, &netconn);
4650 if(res != ERROR_SUCCESS) {
4651 ERR("create_netconn failed: %u\n", res);
4655 request->netconn = netconn;
4657 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4658 INTERNET_STATUS_CONNECTED_TO_SERVER,
4659 request->server->addr_str, strlen(request->server->addr_str)+1);
4662 /* Note: we differ from Microsoft's WinINet here. they seem to have
4663 * a bug that causes no status callbacks to be sent when starting
4664 * a tunnel to a proxy server using the CONNECT verb. i believe our
4665 * behaviour to be more correct and to not cause any incompatibilities
4666 * because using a secure connection through a proxy server is a rare
4667 * case that would be hard for anyone to depend on */
4668 if(request->session->appInfo->proxy)
4669 res = HTTP_SecureProxyConnect(request);
4670 if(res == ERROR_SUCCESS)
4671 res = NETCON_secure_connect(request->netconn);
4674 if(res != ERROR_SUCCESS) {
4675 http_release_netconn(request, FALSE);
4680 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4681 return ERROR_SUCCESS;
4684 /***********************************************************************
4685 * HTTP_HttpSendRequestW (internal)
4687 * Sends the specified request to the HTTP server
4690 * ERROR_SUCCESS on success
4691 * win32 error code on failure
4694 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4695 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4696 DWORD dwContentLength, BOOL bEndRequest)
4699 BOOL redirected = FALSE;
4700 LPWSTR requestString = NULL;
4703 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4704 static const WCHAR szContentLength[] =
4705 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4706 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4709 TRACE("--> %p\n", request);
4711 assert(request->hdr.htype == WH_HHTTPREQ);
4713 /* if the verb is NULL default to GET */
4715 request->verb = heap_strdupW(szGET);
4717 if (dwContentLength || strcmpW(request->verb, szGET))
4719 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4720 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4721 request->bytesToWrite = dwContentLength;
4723 if (request->session->appInfo->agent)
4725 WCHAR *agent_header;
4726 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4729 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4730 agent_header = heap_alloc(len * sizeof(WCHAR));
4731 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4733 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4734 heap_free(agent_header);
4736 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4738 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4739 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4741 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4743 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4744 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4745 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4748 /* add the headers the caller supplied */
4749 if( lpszHeaders && dwHeaderLength )
4750 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4755 BOOL reusing_connection;
4760 /* like native, just in case the caller forgot to call InternetReadFile
4761 * for all the data */
4762 drain_content(request);
4764 request->contentLength = ~0u;
4765 request->bytesToWrite = 0;
4768 if (TRACE_ON(wininet))
4770 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4771 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4774 HTTP_FixURL(request);
4775 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4777 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4779 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4780 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4782 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4783 HTTP_InsertCookies(request);
4785 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4787 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4788 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4792 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4795 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4797 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4800 /* send the request as ASCII, tack on the optional data */
4801 if (!lpOptional || redirected)
4802 dwOptionalLength = 0;
4803 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4804 NULL, 0, NULL, NULL );
4805 ascii_req = heap_alloc(len + dwOptionalLength);
4806 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4807 ascii_req, len, NULL, NULL );
4809 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4810 len = (len + dwOptionalLength - 1);
4812 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4814 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4815 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4817 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4818 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4819 heap_free( ascii_req );
4820 if(res != ERROR_SUCCESS) {
4821 TRACE("send failed: %u\n", res);
4822 if(!reusing_connection)
4824 http_release_netconn(request, FALSE);
4829 request->bytesWritten = dwOptionalLength;
4831 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4832 INTERNET_STATUS_REQUEST_SENT,
4833 &len, sizeof(DWORD));
4839 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4840 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4842 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4843 /* FIXME: We should know that connection is closed before sending
4844 * headers. Otherwise wrong callbacks are executed */
4845 if(!responseLen && reusing_connection) {
4846 TRACE("Connection closed by server, reconnecting\n");
4847 http_release_netconn(request, FALSE);
4852 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4853 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4856 http_process_keep_alive(request);
4857 HTTP_ProcessCookies(request);
4858 HTTP_ProcessExpires(request);
4859 HTTP_ProcessLastModified(request);
4861 res = set_content_length(request);
4862 if(res != ERROR_SUCCESS)
4864 if(!request->contentLength)
4865 http_release_netconn(request, TRUE);
4867 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4869 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4870 dwBufferSize=sizeof(szNewLocation);
4871 switch(request->status_code) {
4872 case HTTP_STATUS_REDIRECT:
4873 case HTTP_STATUS_MOVED:
4874 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4875 case HTTP_STATUS_REDIRECT_METHOD:
4876 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4879 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4880 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4882 heap_free(request->verb);
4883 request->verb = heap_strdupW(szGET);
4885 drain_content(request);
4886 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4888 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4889 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4890 res = HTTP_HandleRedirect(request, new_url);
4891 if (res == ERROR_SUCCESS)
4893 heap_free(requestString);
4896 heap_free( new_url );
4901 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4903 WCHAR szAuthValue[2048];
4905 if (request->status_code == HTTP_STATUS_DENIED)
4907 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4909 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4911 if (HTTP_DoAuthorization(request, szAuthValue,
4913 request->session->userName,
4914 request->session->password,
4917 heap_free(requestString);
4924 TRACE("Cleaning wrong authorization data\n");
4925 destroy_authinfo(request->authInfo);
4926 request->authInfo = NULL;
4929 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4932 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4934 if (HTTP_DoAuthorization(request, szAuthValue,
4935 &request->proxyAuthInfo,
4936 request->session->appInfo->proxyUsername,
4937 request->session->appInfo->proxyPassword,
4946 TRACE("Cleaning wrong proxy authorization data\n");
4947 destroy_authinfo(request->proxyAuthInfo);
4948 request->proxyAuthInfo = NULL;
4954 res = ERROR_SUCCESS;
4958 if(res == ERROR_SUCCESS)
4959 HTTP_CacheRequest(request);
4962 heap_free(requestString);
4964 /* TODO: send notification for P3P header */
4966 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4968 if (res == ERROR_SUCCESS) {
4969 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4970 HTTP_ReceiveRequestData(request, TRUE);
4972 send_request_complete(request,
4973 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4975 send_request_complete(request, 0, res);
4983 /***********************************************************************
4985 * Helper functions for the HttpSendRequest(Ex) functions
4988 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4990 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4991 http_request_t *request = (http_request_t*) workRequest->hdr;
4993 TRACE("%p\n", request);
4995 HTTP_HttpSendRequestW(request, req->lpszHeader,
4996 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4997 req->dwContentLength, req->bEndRequest);
4999 heap_free(req->lpszHeader);
5003 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5007 DWORD res = ERROR_SUCCESS;
5009 if(!request->netconn) {
5010 WARN("Not connected\n");
5011 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5012 return ERROR_INTERNET_OPERATION_CANCELLED;
5015 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5016 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5018 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5020 res = ERROR_HTTP_HEADER_NOT_FOUND;
5022 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5023 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5025 /* process cookies here. Is this right? */
5026 http_process_keep_alive(request);
5027 HTTP_ProcessCookies(request);
5028 HTTP_ProcessExpires(request);
5029 HTTP_ProcessLastModified(request);
5031 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5032 if(!request->contentLength)
5033 http_release_netconn(request, TRUE);
5036 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5038 switch(request->status_code) {
5039 case HTTP_STATUS_REDIRECT:
5040 case HTTP_STATUS_MOVED:
5041 case HTTP_STATUS_REDIRECT_METHOD:
5042 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5043 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5044 dwBufferSize=sizeof(szNewLocation);
5045 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5048 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5049 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5051 heap_free(request->verb);
5052 request->verb = heap_strdupW(szGET);
5054 drain_content(request);
5055 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5057 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5058 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5059 res = HTTP_HandleRedirect(request, new_url);
5060 if (res == ERROR_SUCCESS)
5061 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5062 heap_free( new_url );
5068 if (res == ERROR_SUCCESS && request->contentLength)
5069 HTTP_ReceiveRequestData(request, TRUE);
5071 send_request_complete(request, res == ERROR_SUCCESS, res);
5076 /***********************************************************************
5077 * HttpEndRequestA (WININET.@)
5079 * Ends an HTTP request that was started by HttpSendRequestEx
5082 * TRUE if successful
5086 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5087 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5089 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5093 SetLastError(ERROR_INVALID_PARAMETER);
5097 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5100 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5102 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5103 http_request_t *request = (http_request_t*)work->hdr;
5105 TRACE("%p\n", request);
5107 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5110 /***********************************************************************
5111 * HttpEndRequestW (WININET.@)
5113 * Ends an HTTP request that was started by HttpSendRequestEx
5116 * TRUE if successful
5120 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5121 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5123 http_request_t *request;
5126 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5130 SetLastError(ERROR_INVALID_PARAMETER);
5134 request = (http_request_t*) get_handle_object( hRequest );
5136 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5138 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5140 WININET_Release( &request->hdr );
5143 request->hdr.dwFlags |= dwFlags;
5145 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5148 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5150 work.asyncproc = AsyncHttpEndRequestProc;
5151 work.hdr = WININET_AddRef( &request->hdr );
5153 work_endrequest = &work.u.HttpEndRequestW;
5154 work_endrequest->dwFlags = dwFlags;
5155 work_endrequest->dwContext = dwContext;
5157 INTERNET_AsyncCall(&work);
5158 res = ERROR_IO_PENDING;
5161 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5163 WININET_Release( &request->hdr );
5164 TRACE("%u <--\n", res);
5165 if(res != ERROR_SUCCESS)
5167 return res == ERROR_SUCCESS;
5170 /***********************************************************************
5171 * HttpSendRequestExA (WININET.@)
5173 * Sends the specified request to the HTTP server and allows chunked
5178 * Failure: FALSE, call GetLastError() for more information.
5180 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5181 LPINTERNET_BUFFERSA lpBuffersIn,
5182 LPINTERNET_BUFFERSA lpBuffersOut,
5183 DWORD dwFlags, DWORD_PTR dwContext)
5185 INTERNET_BUFFERSW BuffersInW;
5188 LPWSTR header = NULL;
5190 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5191 lpBuffersOut, dwFlags, dwContext);
5195 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5196 if (lpBuffersIn->lpcszHeader)
5198 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5199 lpBuffersIn->dwHeadersLength,0,0);
5200 header = heap_alloc(headerlen*sizeof(WCHAR));
5201 if (!(BuffersInW.lpcszHeader = header))
5203 SetLastError(ERROR_OUTOFMEMORY);
5206 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5207 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5211 BuffersInW.lpcszHeader = NULL;
5212 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5213 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5214 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5215 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5216 BuffersInW.Next = NULL;
5219 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5225 /***********************************************************************
5226 * HttpSendRequestExW (WININET.@)
5228 * Sends the specified request to the HTTP server and allows chunked
5233 * Failure: FALSE, call GetLastError() for more information.
5235 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5236 LPINTERNET_BUFFERSW lpBuffersIn,
5237 LPINTERNET_BUFFERSW lpBuffersOut,
5238 DWORD dwFlags, DWORD_PTR dwContext)
5240 http_request_t *request;
5241 http_session_t *session;
5245 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5246 lpBuffersOut, dwFlags, dwContext);
5248 request = (http_request_t*) get_handle_object( hRequest );
5250 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5252 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5256 session = request->session;
5257 assert(session->hdr.htype == WH_HHTTPSESSION);
5258 hIC = session->appInfo;
5259 assert(hIC->hdr.htype == WH_HINIT);
5261 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5263 WORKREQUEST workRequest;
5264 struct WORKREQ_HTTPSENDREQUESTW *req;
5266 workRequest.asyncproc = AsyncHttpSendRequestProc;
5267 workRequest.hdr = WININET_AddRef( &request->hdr );
5268 req = &workRequest.u.HttpSendRequestW;
5273 if (lpBuffersIn->lpcszHeader)
5275 if (lpBuffersIn->dwHeadersLength == ~0u)
5276 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5278 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5280 req->lpszHeader = heap_alloc(size);
5281 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5283 else req->lpszHeader = NULL;
5285 req->dwHeaderLength = size / sizeof(WCHAR);
5286 req->lpOptional = lpBuffersIn->lpvBuffer;
5287 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5288 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5292 req->lpszHeader = NULL;
5293 req->dwHeaderLength = 0;
5294 req->lpOptional = NULL;
5295 req->dwOptionalLength = 0;
5296 req->dwContentLength = 0;
5299 req->bEndRequest = FALSE;
5301 INTERNET_AsyncCall(&workRequest);
5303 * This is from windows.
5305 res = ERROR_IO_PENDING;
5310 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5311 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5312 lpBuffersIn->dwBufferTotal, FALSE);
5314 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5319 WININET_Release( &request->hdr );
5323 return res == ERROR_SUCCESS;
5326 /***********************************************************************
5327 * HttpSendRequestW (WININET.@)
5329 * Sends the specified request to the HTTP server
5336 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5337 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5339 http_request_t *request;
5340 http_session_t *session = NULL;
5341 appinfo_t *hIC = NULL;
5342 DWORD res = ERROR_SUCCESS;
5344 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5345 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5347 request = (http_request_t*) get_handle_object( hHttpRequest );
5348 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5350 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5354 session = request->session;
5355 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5357 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5361 hIC = session->appInfo;
5362 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5364 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5368 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5370 WORKREQUEST workRequest;
5371 struct WORKREQ_HTTPSENDREQUESTW *req;
5373 workRequest.asyncproc = AsyncHttpSendRequestProc;
5374 workRequest.hdr = WININET_AddRef( &request->hdr );
5375 req = &workRequest.u.HttpSendRequestW;
5380 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5381 else size = dwHeaderLength * sizeof(WCHAR);
5383 req->lpszHeader = heap_alloc(size);
5384 memcpy(req->lpszHeader, lpszHeaders, size);
5387 req->lpszHeader = 0;
5388 req->dwHeaderLength = dwHeaderLength;
5389 req->lpOptional = lpOptional;
5390 req->dwOptionalLength = dwOptionalLength;
5391 req->dwContentLength = dwOptionalLength;
5392 req->bEndRequest = TRUE;
5394 INTERNET_AsyncCall(&workRequest);
5396 * This is from windows.
5398 res = ERROR_IO_PENDING;
5402 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5403 dwHeaderLength, lpOptional, dwOptionalLength,
5404 dwOptionalLength, TRUE);
5408 WININET_Release( &request->hdr );
5411 return res == ERROR_SUCCESS;
5414 /***********************************************************************
5415 * HttpSendRequestA (WININET.@)
5417 * Sends the specified request to the HTTP server
5424 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5425 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5428 LPWSTR szHeaders=NULL;
5429 DWORD nLen=dwHeaderLength;
5430 if(lpszHeaders!=NULL)
5432 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5433 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5434 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5436 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5437 heap_free(szHeaders);
5441 /***********************************************************************
5442 * HTTPSESSION_Destroy (internal)
5444 * Deallocate session handle
5447 static void HTTPSESSION_Destroy(object_header_t *hdr)
5449 http_session_t *session = (http_session_t*) hdr;
5451 TRACE("%p\n", session);
5453 WININET_Release(&session->appInfo->hdr);
5455 heap_free(session->hostName);
5456 heap_free(session->password);
5457 heap_free(session->userName);
5460 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5462 http_session_t *ses = (http_session_t *)hdr;
5465 case INTERNET_OPTION_HANDLE_TYPE:
5466 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5468 if (*size < sizeof(ULONG))
5469 return ERROR_INSUFFICIENT_BUFFER;
5471 *size = sizeof(DWORD);
5472 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5473 return ERROR_SUCCESS;
5474 case INTERNET_OPTION_CONNECT_TIMEOUT:
5475 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5477 if (*size < sizeof(DWORD))
5478 return ERROR_INSUFFICIENT_BUFFER;
5480 *size = sizeof(DWORD);
5481 *(DWORD *)buffer = ses->connect_timeout;
5482 return ERROR_SUCCESS;
5484 case INTERNET_OPTION_SEND_TIMEOUT:
5485 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5487 if (*size < sizeof(DWORD))
5488 return ERROR_INSUFFICIENT_BUFFER;
5490 *size = sizeof(DWORD);
5491 *(DWORD *)buffer = ses->send_timeout;
5492 return ERROR_SUCCESS;
5494 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5495 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5497 if (*size < sizeof(DWORD))
5498 return ERROR_INSUFFICIENT_BUFFER;
5500 *size = sizeof(DWORD);
5501 *(DWORD *)buffer = ses->receive_timeout;
5502 return ERROR_SUCCESS;
5505 return INET_QueryOption(hdr, option, buffer, size, unicode);
5508 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5510 http_session_t *ses = (http_session_t*)hdr;
5513 case INTERNET_OPTION_USERNAME:
5515 heap_free(ses->userName);
5516 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5517 return ERROR_SUCCESS;
5519 case INTERNET_OPTION_PASSWORD:
5521 heap_free(ses->password);
5522 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5523 return ERROR_SUCCESS;
5525 case INTERNET_OPTION_CONNECT_TIMEOUT:
5527 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5528 ses->connect_timeout = *(DWORD *)buffer;
5529 return ERROR_SUCCESS;
5531 case INTERNET_OPTION_SEND_TIMEOUT:
5533 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5534 ses->send_timeout = *(DWORD *)buffer;
5535 return ERROR_SUCCESS;
5537 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5539 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5540 ses->receive_timeout = *(DWORD *)buffer;
5541 return ERROR_SUCCESS;
5546 return INET_SetOption(hdr, option, buffer, size);
5549 static const object_vtbl_t HTTPSESSIONVtbl = {
5550 HTTPSESSION_Destroy,
5552 HTTPSESSION_QueryOption,
5553 HTTPSESSION_SetOption,
5562 /***********************************************************************
5563 * HTTP_Connect (internal)
5565 * Create http session handle
5568 * HINTERNET a session handle on success
5572 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5573 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5574 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5575 DWORD dwInternalFlags, HINTERNET *ret)
5577 http_session_t *session = NULL;
5581 if (!lpszServerName || !lpszServerName[0])
5582 return ERROR_INVALID_PARAMETER;
5584 assert( hIC->hdr.htype == WH_HINIT );
5586 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5588 return ERROR_OUTOFMEMORY;
5591 * According to my tests. The name is not resolved until a request is sent
5594 session->hdr.htype = WH_HHTTPSESSION;
5595 session->hdr.dwFlags = dwFlags;
5596 session->hdr.dwContext = dwContext;
5597 session->hdr.dwInternalFlags |= dwInternalFlags;
5599 WININET_AddRef( &hIC->hdr );
5600 session->appInfo = hIC;
5601 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5603 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5604 if(hIC->proxyBypass)
5605 FIXME("Proxy bypass is ignored.\n");
5607 session->hostName = heap_strdupW(lpszServerName);
5608 if (lpszUserName && lpszUserName[0])
5609 session->userName = heap_strdupW(lpszUserName);
5610 if (lpszPassword && lpszPassword[0])
5611 session->password = heap_strdupW(lpszPassword);
5612 session->hostPort = serverPort;
5613 session->connect_timeout = hIC->connect_timeout;
5614 session->send_timeout = INFINITE;
5615 session->receive_timeout = INFINITE;
5617 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5618 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5620 INTERNET_SendCallback(&hIC->hdr, dwContext,
5621 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5626 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5630 TRACE("%p --> %p\n", hIC, session);
5632 *ret = session->hdr.hInternet;
5633 return ERROR_SUCCESS;
5636 /***********************************************************************
5637 * HTTP_clear_response_headers (internal)
5639 * clear out any old response headers
5641 static void HTTP_clear_response_headers( http_request_t *request )
5645 for( i=0; i<request->nCustHeaders; i++)
5647 if( !request->custHeaders[i].lpszField )
5649 if( !request->custHeaders[i].lpszValue )
5651 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5653 HTTP_DeleteCustomHeader( request, i );
5658 /***********************************************************************
5659 * HTTP_GetResponseHeaders (internal)
5661 * Read server response
5668 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5671 WCHAR buffer[MAX_REPLY_LEN];
5672 DWORD buflen = MAX_REPLY_LEN;
5673 BOOL bSuccess = FALSE;
5675 char bufferA[MAX_REPLY_LEN];
5676 LPWSTR status_code = NULL, status_text = NULL;
5677 DWORD cchMaxRawHeaders = 1024;
5678 LPWSTR lpszRawHeaders = NULL;
5680 DWORD cchRawHeaders = 0;
5681 BOOL codeHundred = FALSE;
5685 if(!request->netconn)
5688 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5690 static const WCHAR szHundred[] = {'1','0','0',0};
5692 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5694 buflen = MAX_REPLY_LEN;
5695 if (!read_line(request, bufferA, &buflen))
5698 /* clear old response headers (eg. from a redirect response) */
5700 HTTP_clear_response_headers( request );
5705 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5706 /* check is this a status code line? */
5707 if (!strncmpW(buffer, g_szHttp1_0, 4))
5709 /* split the version from the status code */
5710 status_code = strchrW( buffer, ' ' );
5715 /* split the status code from the status text */
5716 status_text = strchrW( status_code, ' ' );
5721 request->status_code = atoiW(status_code);
5723 TRACE("version [%s] status code [%s] status text [%s]\n",
5724 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5726 codeHundred = (!strcmpW(status_code, szHundred));
5728 else if (!codeHundred)
5730 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5732 heap_free(request->version);
5733 heap_free(request->statusText);
5735 request->status_code = HTTP_STATUS_OK;
5736 request->version = heap_strdupW(g_szHttp1_0);
5737 request->statusText = heap_strdupW(szOK);
5739 heap_free(request->rawHeaders);
5740 request->rawHeaders = heap_strdupW(szDefaultHeader);
5745 } while (codeHundred);
5747 /* Add status code */
5748 HTTP_ProcessHeader(request, szStatus, status_code,
5749 HTTP_ADDHDR_FLAG_REPLACE);
5751 heap_free(request->version);
5752 heap_free(request->statusText);
5754 request->version = heap_strdupW(buffer);
5755 request->statusText = heap_strdupW(status_text);
5757 /* Restore the spaces */
5758 *(status_code-1) = ' ';
5759 *(status_text-1) = ' ';
5761 /* regenerate raw headers */
5762 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5763 if (!lpszRawHeaders) goto lend;
5765 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5766 cchMaxRawHeaders *= 2;
5767 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5768 if (temp == NULL) goto lend;
5769 lpszRawHeaders = temp;
5770 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5771 cchRawHeaders += (buflen-1);
5772 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5773 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5774 lpszRawHeaders[cchRawHeaders] = '\0';
5776 /* Parse each response line */
5779 buflen = MAX_REPLY_LEN;
5780 if (read_line(request, bufferA, &buflen))
5782 LPWSTR * pFieldAndValue;
5784 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5786 if (!bufferA[0]) break;
5787 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5789 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5792 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5793 cchMaxRawHeaders *= 2;
5794 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5795 if (temp == NULL) goto lend;
5796 lpszRawHeaders = temp;
5797 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5798 cchRawHeaders += (buflen-1);
5799 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5800 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5801 lpszRawHeaders[cchRawHeaders] = '\0';
5803 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5804 HTTP_ADDREQ_FLAG_ADD );
5806 HTTP_FreeTokens(pFieldAndValue);
5817 /* make sure the response header is terminated with an empty line. Some apps really
5818 truly care about that empty line being there for some reason. Just add it to the
5820 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5822 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5823 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5824 if (temp == NULL) goto lend;
5825 lpszRawHeaders = temp;
5828 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5830 heap_free(request->rawHeaders);
5831 request->rawHeaders = lpszRawHeaders;
5832 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5842 heap_free(lpszRawHeaders);
5847 /***********************************************************************
5848 * HTTP_InterpretHttpHeader (internal)
5850 * Parse server response
5854 * Pointer to array of field, value, NULL on success.
5857 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5859 LPWSTR * pTokenPair;
5863 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5865 pszColon = strchrW(buffer, ':');
5866 /* must have two tokens */
5869 HTTP_FreeTokens(pTokenPair);
5871 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5875 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5878 HTTP_FreeTokens(pTokenPair);
5881 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5882 pTokenPair[0][pszColon - buffer] = '\0';
5886 len = strlenW(pszColon);
5887 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5890 HTTP_FreeTokens(pTokenPair);
5893 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5895 strip_spaces(pTokenPair[0]);
5896 strip_spaces(pTokenPair[1]);
5898 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5902 /***********************************************************************
5903 * HTTP_ProcessHeader (internal)
5905 * Stuff header into header tables according to <dwModifier>
5909 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5911 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5913 LPHTTPHEADERW lphttpHdr = NULL;
5915 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5916 DWORD res = ERROR_HTTP_INVALID_HEADER;
5918 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5920 /* REPLACE wins out over ADD */
5921 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5922 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5924 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5927 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5931 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5932 return ERROR_HTTP_INVALID_HEADER;
5933 lphttpHdr = &request->custHeaders[index];
5939 hdr.lpszField = (LPWSTR)field;
5940 hdr.lpszValue = (LPWSTR)value;
5941 hdr.wFlags = hdr.wCount = 0;
5943 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5944 hdr.wFlags |= HDR_ISREQUEST;
5946 return HTTP_InsertCustomHeader(request, &hdr);
5948 /* no value to delete */
5949 else return ERROR_SUCCESS;
5951 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5952 lphttpHdr->wFlags |= HDR_ISREQUEST;
5954 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5956 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5958 HTTP_DeleteCustomHeader( request, index );
5964 hdr.lpszField = (LPWSTR)field;
5965 hdr.lpszValue = (LPWSTR)value;
5966 hdr.wFlags = hdr.wCount = 0;
5968 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5969 hdr.wFlags |= HDR_ISREQUEST;
5971 return HTTP_InsertCustomHeader(request, &hdr);
5974 return ERROR_SUCCESS;
5976 else if (dwModifier & COALESCEFLAGS)
5981 INT origlen = strlenW(lphttpHdr->lpszValue);
5982 INT valuelen = strlenW(value);
5984 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5987 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5989 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5992 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5995 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5997 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6000 lphttpHdr->lpszValue = lpsztmp;
6001 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6004 lphttpHdr->lpszValue[origlen] = ch;
6006 lphttpHdr->lpszValue[origlen] = ' ';
6010 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6011 lphttpHdr->lpszValue[len] = '\0';
6012 res = ERROR_SUCCESS;
6016 WARN("heap_realloc (%d bytes) failed\n",len+1);
6017 res = ERROR_OUTOFMEMORY;
6020 TRACE("<-- %d\n", res);
6024 /***********************************************************************
6025 * HTTP_GetCustomHeaderIndex (internal)
6027 * Return index of custom header from header array
6030 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6031 int requested_index, BOOL request_only)
6035 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6037 for (index = 0; index < request->nCustHeaders; index++)
6039 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6042 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6045 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6048 if (requested_index == 0)
6053 if (index >= request->nCustHeaders)
6056 TRACE("Return: %d\n", index);
6061 /***********************************************************************
6062 * HTTP_InsertCustomHeader (internal)
6064 * Insert header into array
6067 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6070 LPHTTPHEADERW lph = NULL;
6072 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6073 count = request->nCustHeaders + 1;
6075 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6077 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6080 return ERROR_OUTOFMEMORY;
6082 request->custHeaders = lph;
6083 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6084 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6085 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6086 request->custHeaders[count-1].wCount= lpHdr->wCount;
6087 request->nCustHeaders++;
6089 return ERROR_SUCCESS;
6093 /***********************************************************************
6094 * HTTP_DeleteCustomHeader (internal)
6096 * Delete header from array
6097 * If this function is called, the indexs may change.
6099 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6101 if( request->nCustHeaders <= 0 )
6103 if( index >= request->nCustHeaders )
6105 request->nCustHeaders--;
6107 heap_free(request->custHeaders[index].lpszField);
6108 heap_free(request->custHeaders[index].lpszValue);
6110 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6111 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6112 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6118 /***********************************************************************
6119 * HTTP_VerifyValidHeader (internal)
6121 * Verify the given header is not invalid for the given http request
6124 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6126 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6127 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6128 return ERROR_HTTP_INVALID_HEADER;
6130 return ERROR_SUCCESS;
6133 /***********************************************************************
6134 * IsHostInProxyBypassList (@)
6139 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6141 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6145 /***********************************************************************
6146 * InternetShowSecurityInfoByURLA (@)
6148 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6150 FIXME("stub: %s %p\n", url, window);
6154 /***********************************************************************
6155 * InternetShowSecurityInfoByURLW (@)
6157 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6159 FIXME("stub: %s %p\n", debugstr_w(url), window);
6163 /***********************************************************************
6164 * ShowX509EncodedCertificate (@)
6166 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6168 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6174 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6176 memset(&view, 0, sizeof(view));
6177 view.hwndParent = parent;
6178 view.pCertContext = certContext;
6179 if (CryptUIDlgViewCertificateW(&view, NULL))
6180 ret = ERROR_SUCCESS;
6182 ret = GetLastError();
6183 CertFreeCertificateContext(certContext);
6186 ret = GetLastError();