wininet: Add support for INTERNET_OPTION_PROXY_USERNAME and INTERNET_OPTION_PROXY_PAS...
[wine.git] / dlls / wininet / http.c
blobaa3e46134e2fe12da758a93cae66af0825cf7b61
1 /*
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
12 * Ulrich Czekalla
13 * David Hammerton
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
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
40 #endif
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
43 #endif
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #ifdef HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
50 #include <time.h>
51 #include <assert.h>
52 #ifdef HAVE_ZLIB
53 # include <zlib.h>
54 #endif
56 #include "windef.h"
57 #include "winbase.h"
58 #include "wininet.h"
59 #include "winerror.h"
60 #include "winternl.h"
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
65 #include "shlwapi.h"
66 #include "sspi.h"
67 #include "wincrypt.h"
68 #include "winuser.h"
70 #include "internet.h"
71 #include "wine/debug.h"
72 #include "wine/exception.h"
73 #include "wine/unicode.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
77 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
78 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
79 static const WCHAR szOK[] = {'O','K',0};
80 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
81 static const WCHAR hostW[] = { 'H','o','s','t',0 };
82 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
85 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
86 static const WCHAR szGET[] = { 'G','E','T', 0 };
87 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
88 static const WCHAR szCrLf[] = {'\r','\n', 0};
90 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
91 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
92 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
93 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
94 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
95 static const WCHAR szAge[] = { 'A','g','e',0 };
96 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
97 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
98 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
99 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
100 static const WCHAR szContent_Disposition[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
101 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
102 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
103 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
104 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
105 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
106 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
107 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
108 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
109 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
110 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
111 static const WCHAR szDate[] = { 'D','a','t','e',0 };
112 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
113 static const WCHAR szETag[] = { 'E','T','a','g',0 };
114 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
115 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
116 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
118 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
119 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
120 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
121 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
122 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
123 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
124 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
125 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
126 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
127 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
128 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
129 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
130 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
131 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
132 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
133 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
134 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
135 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
136 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
137 static const WCHAR szURI[] = { 'U','R','I',0 };
138 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
139 static const WCHAR szVary[] = { 'V','a','r','y',0 };
140 static const WCHAR szVia[] = { 'V','i','a',0 };
141 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
142 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define COLLECT_TIME 60000
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
160 struct HttpAuthInfo
162 LPWSTR scheme;
163 CredHandle cred;
164 CtxtHandle ctx;
165 TimeStamp exp;
166 ULONG attr;
167 ULONG max_token;
168 void *auth_data;
169 unsigned int auth_data_len;
170 BOOL finished; /* finished authenticating */
174 typedef struct _basicAuthorizationData
176 struct list entry;
178 LPWSTR host;
179 LPWSTR realm;
180 LPSTR authorization;
181 UINT authorizationLen;
182 } basicAuthorizationData;
184 typedef struct _authorizationData
186 struct list entry;
188 LPWSTR host;
189 LPWSTR scheme;
190 LPWSTR domain;
191 UINT domain_len;
192 LPWSTR user;
193 UINT user_len;
194 LPWSTR password;
195 UINT password_len;
196 } authorizationData;
198 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
199 static struct list authorizationCache = LIST_INIT(authorizationCache);
201 static CRITICAL_SECTION authcache_cs;
202 static CRITICAL_SECTION_DEBUG critsect_debug =
204 0, 0, &authcache_cs,
205 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
206 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
208 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
210 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
211 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
212 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
213 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
214 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
215 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
216 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
217 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
218 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
219 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
220 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
221 static BOOL drain_content(http_request_t*,BOOL);
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))
243 return;
245 list_remove(&server->entry);
247 if(server->cert_chain)
248 CertFreeCertificateChain(server->cert_chain);
249 heap_free(server->name);
250 heap_free(server->scheme_host_port);
251 heap_free(server);
254 static BOOL process_host_port(server_t *server)
256 BOOL default_port;
257 size_t name_len;
258 WCHAR *buf;
260 static const WCHAR httpW[] = {'h','t','t','p',0};
261 static const WCHAR httpsW[] = {'h','t','t','p','s',0};
262 static const WCHAR formatW[] = {'%','s',':','/','/','%','s',':','%','u',0};
264 name_len = strlenW(server->name);
265 buf = heap_alloc((name_len + 10 /* strlen("://:<port>") */)*sizeof(WCHAR) + sizeof(httpsW));
266 if(!buf)
267 return FALSE;
269 sprintfW(buf, formatW, server->is_https ? httpsW : httpW, server->name, server->port);
270 server->scheme_host_port = buf;
272 server->host_port = server->scheme_host_port + 7 /* strlen("http://") */;
273 if(server->is_https)
274 server->host_port++;
276 default_port = server->port == (server->is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
277 server->canon_host_port = default_port ? server->name : server->host_port;
278 return TRUE;
281 server_t *get_server(const WCHAR *name, INTERNET_PORT port, BOOL is_https, BOOL do_create)
283 server_t *iter, *server = NULL;
285 if(port == INTERNET_INVALID_PORT_NUMBER)
286 port = INTERNET_DEFAULT_HTTP_PORT;
288 EnterCriticalSection(&connection_pool_cs);
290 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
291 if(iter->port == port && !strcmpW(iter->name, name) && iter->is_https == is_https) {
292 server = iter;
293 server_addref(server);
294 break;
298 if(!server && do_create) {
299 server = heap_alloc_zero(sizeof(*server));
300 if(server) {
301 server->ref = 2; /* list reference and return */
302 server->port = port;
303 server->is_https = is_https;
304 list_init(&server->conn_pool);
305 server->name = heap_strdupW(name);
306 if(server->name && process_host_port(server)) {
307 list_add_head(&connection_pool, &server->entry);
308 }else {
309 heap_free(server);
310 server = NULL;
315 LeaveCriticalSection(&connection_pool_cs);
317 return server;
320 BOOL collect_connections(collect_type_t collect_type)
322 netconn_t *netconn, *netconn_safe;
323 server_t *server, *server_safe;
324 BOOL remaining = FALSE;
325 DWORD64 now;
327 now = GetTickCount64();
329 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
330 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
331 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
332 TRACE("freeing %p\n", netconn);
333 list_remove(&netconn->pool_entry);
334 free_netconn(netconn);
335 }else {
336 remaining = TRUE;
340 if(collect_type == COLLECT_CLEANUP) {
341 list_remove(&server->entry);
342 list_init(&server->entry);
343 server_release(server);
347 return remaining;
350 static DWORD WINAPI collect_connections_proc(void *arg)
352 BOOL remaining_conns;
354 do {
355 /* FIXME: Use more sophisticated method */
356 Sleep(5000);
358 EnterCriticalSection(&connection_pool_cs);
360 remaining_conns = collect_connections(COLLECT_TIMEOUT);
361 if(!remaining_conns)
362 collector_running = FALSE;
364 LeaveCriticalSection(&connection_pool_cs);
365 }while(remaining_conns);
367 FreeLibraryAndExitThread(WININET_hModule, 0);
370 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
372 int HeaderIndex = 0;
373 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
374 if (HeaderIndex == -1)
375 return NULL;
376 else
377 return &req->custHeaders[HeaderIndex];
380 typedef enum {
381 READMODE_SYNC,
382 READMODE_ASYNC,
383 READMODE_NOBLOCK
384 } read_mode_t;
386 struct data_stream_vtbl_t {
387 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
388 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
389 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
390 BOOL (*drain_content)(data_stream_t*,http_request_t*);
391 void (*destroy)(data_stream_t*);
394 typedef struct {
395 data_stream_t data_stream;
397 BYTE buf[READ_BUFFER_SIZE];
398 DWORD buf_size;
399 DWORD buf_pos;
400 DWORD chunk_size;
401 } chunked_stream_t;
403 static inline void destroy_data_stream(data_stream_t *stream)
405 stream->vtbl->destroy(stream);
408 static void reset_data_stream(http_request_t *req)
410 destroy_data_stream(req->data_stream);
411 req->data_stream = &req->netconn_stream.data_stream;
412 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
413 req->read_chunked = req->read_gzip = FALSE;
416 #ifdef HAVE_ZLIB
418 typedef struct {
419 data_stream_t stream;
420 data_stream_t *parent_stream;
421 z_stream zstream;
422 BYTE buf[READ_BUFFER_SIZE];
423 DWORD buf_size;
424 DWORD buf_pos;
425 BOOL end_of_data;
426 } gzip_stream_t;
428 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
430 /* Allow reading only from read buffer */
431 return 0;
434 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
436 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
437 return gzip_stream->end_of_data;
440 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
441 DWORD *read, read_mode_t read_mode)
443 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
444 z_stream *zstream = &gzip_stream->zstream;
445 DWORD current_read, ret_read = 0;
446 BOOL end;
447 int zres;
448 DWORD res = ERROR_SUCCESS;
450 while(size && !gzip_stream->end_of_data) {
451 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
453 if(gzip_stream->buf_size <= 64 && !end) {
454 if(gzip_stream->buf_pos) {
455 if(gzip_stream->buf_size)
456 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
457 gzip_stream->buf_pos = 0;
459 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
460 sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
461 gzip_stream->buf_size += current_read;
462 if(res != ERROR_SUCCESS)
463 break;
464 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
465 if(!current_read && !end) {
466 if(read_mode != READMODE_NOBLOCK) {
467 WARN("unexpected end of data\n");
468 gzip_stream->end_of_data = TRUE;
470 break;
472 if(gzip_stream->buf_size <= 64 && !end)
473 continue;
476 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
477 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
478 zstream->next_out = buf+ret_read;
479 zstream->avail_out = size;
480 zres = inflate(&gzip_stream->zstream, 0);
481 current_read = size - zstream->avail_out;
482 size -= current_read;
483 ret_read += current_read;
484 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
485 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
486 if(zres == Z_STREAM_END) {
487 TRACE("end of data\n");
488 gzip_stream->end_of_data = TRUE;
489 inflateEnd(zstream);
490 }else if(zres != Z_OK) {
491 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
492 if(!ret_read)
493 res = ERROR_INTERNET_DECODING_FAILED;
494 break;
497 if(ret_read && read_mode == READMODE_ASYNC)
498 read_mode = READMODE_NOBLOCK;
501 TRACE("read %u bytes\n", ret_read);
502 *read = ret_read;
503 return res;
506 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
508 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
509 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
512 static void gzip_destroy(data_stream_t *stream)
514 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
516 destroy_data_stream(gzip_stream->parent_stream);
518 if(!gzip_stream->end_of_data)
519 inflateEnd(&gzip_stream->zstream);
520 heap_free(gzip_stream);
523 static const data_stream_vtbl_t gzip_stream_vtbl = {
524 gzip_get_avail_data,
525 gzip_end_of_data,
526 gzip_read,
527 gzip_drain_content,
528 gzip_destroy
531 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
533 return heap_alloc(items*size);
536 static void wininet_zfree(voidpf opaque, voidpf address)
538 heap_free(address);
541 static DWORD init_gzip_stream(http_request_t *req)
543 gzip_stream_t *gzip_stream;
544 int index, zres;
546 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
547 if(!gzip_stream)
548 return ERROR_OUTOFMEMORY;
550 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
551 gzip_stream->zstream.zalloc = wininet_zalloc;
552 gzip_stream->zstream.zfree = wininet_zfree;
554 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
555 if(zres != Z_OK) {
556 ERR("inflateInit failed: %d\n", zres);
557 heap_free(gzip_stream);
558 return ERROR_OUTOFMEMORY;
561 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
562 if(index != -1)
563 HTTP_DeleteCustomHeader(req, index);
565 if(req->read_size) {
566 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
567 gzip_stream->buf_size = req->read_size;
568 req->read_pos = req->read_size = 0;
571 req->read_gzip = TRUE;
572 gzip_stream->parent_stream = req->data_stream;
573 req->data_stream = &gzip_stream->stream;
574 return ERROR_SUCCESS;
577 #else
579 static DWORD init_gzip_stream(http_request_t *req)
581 ERR("gzip stream not supported, missing zlib.\n");
582 return ERROR_SUCCESS;
585 #endif
587 /***********************************************************************
588 * HTTP_Tokenize (internal)
590 * Tokenize a string, allocating memory for the tokens.
592 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
594 LPWSTR * token_array;
595 int tokens = 0;
596 int i;
597 LPCWSTR next_token;
599 if (string)
601 /* empty string has no tokens */
602 if (*string)
603 tokens++;
604 /* count tokens */
605 for (i = 0; string[i]; i++)
607 if (!strncmpW(string+i, token_string, strlenW(token_string)))
609 DWORD j;
610 tokens++;
611 /* we want to skip over separators, but not the null terminator */
612 for (j = 0; j < strlenW(token_string) - 1; j++)
613 if (!string[i+j])
614 break;
615 i += j;
620 /* add 1 for terminating NULL */
621 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
622 token_array[tokens] = NULL;
623 if (!tokens)
624 return token_array;
625 for (i = 0; i < tokens; i++)
627 int len;
628 next_token = strstrW(string, token_string);
629 if (!next_token) next_token = string+strlenW(string);
630 len = next_token - string;
631 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
632 memcpy(token_array[i], string, len*sizeof(WCHAR));
633 token_array[i][len] = '\0';
634 string = next_token+strlenW(token_string);
636 return token_array;
639 /***********************************************************************
640 * HTTP_FreeTokens (internal)
642 * Frees memory returned from HTTP_Tokenize.
644 static void HTTP_FreeTokens(LPWSTR * token_array)
646 int i;
647 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
648 heap_free(token_array);
651 static void HTTP_FixURL(http_request_t *request)
653 static const WCHAR szSlash[] = { '/',0 };
654 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
656 /* If we don't have a path we set it to root */
657 if (NULL == request->path)
658 request->path = heap_strdupW(szSlash);
659 else /* remove \r and \n*/
661 int nLen = strlenW(request->path);
662 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
664 nLen--;
665 request->path[nLen]='\0';
667 /* Replace '\' with '/' */
668 while (nLen>0) {
669 nLen--;
670 if (request->path[nLen] == '\\') request->path[nLen]='/';
674 if(CSTR_EQUAL != CompareStringW( LOCALE_INVARIANT, NORM_IGNORECASE,
675 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
676 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
678 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
679 *fixurl = '/';
680 strcpyW(fixurl + 1, request->path);
681 heap_free( request->path );
682 request->path = fixurl;
686 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
688 LPWSTR requestString;
689 DWORD len, n;
690 LPCWSTR *req;
691 UINT i;
692 LPWSTR p;
694 static const WCHAR szSpace[] = { ' ',0 };
695 static const WCHAR szColon[] = { ':',' ',0 };
696 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
698 /* allocate space for an array of all the string pointers to be added */
699 len = (request->nCustHeaders)*4 + 10;
700 req = heap_alloc(len*sizeof(LPCWSTR));
702 /* add the verb, path and HTTP version string */
703 n = 0;
704 req[n++] = verb;
705 req[n++] = szSpace;
706 req[n++] = path;
707 req[n++] = szSpace;
708 req[n++] = version;
710 /* Append custom request headers */
711 for (i = 0; i < request->nCustHeaders; i++)
713 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
715 req[n++] = szCrLf;
716 req[n++] = request->custHeaders[i].lpszField;
717 req[n++] = szColon;
718 req[n++] = request->custHeaders[i].lpszValue;
720 TRACE("Adding custom header %s (%s)\n",
721 debugstr_w(request->custHeaders[i].lpszField),
722 debugstr_w(request->custHeaders[i].lpszValue));
726 if( n >= len )
727 ERR("oops. buffer overrun\n");
729 req[n] = NULL;
730 requestString = HTTP_build_req( req, 4 );
731 heap_free( req );
734 * Set (header) termination string for request
735 * Make sure there's exactly two new lines at the end of the request
737 p = &requestString[strlenW(requestString)-1];
738 while ( (*p == '\n') || (*p == '\r') )
739 p--;
740 strcpyW( p+1, sztwocrlf );
742 return requestString;
745 static void HTTP_ProcessCookies( http_request_t *request )
747 int HeaderIndex;
748 int numCookies = 0;
749 LPHTTPHEADERW setCookieHeader;
751 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
752 return;
754 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
756 HTTPHEADERW *host;
757 const WCHAR *data;
758 WCHAR *name;
760 setCookieHeader = &request->custHeaders[HeaderIndex];
762 if (!setCookieHeader->lpszValue)
763 continue;
765 host = HTTP_GetHeader(request, hostW);
766 if(!host)
767 continue;
769 data = strchrW(setCookieHeader->lpszValue, '=');
770 if(!data)
771 continue;
773 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
774 if(!name)
775 continue;
777 data++;
778 set_cookie(host->lpszValue, request->path, name, data);
779 heap_free(name);
783 static void strip_spaces(LPWSTR start)
785 LPWSTR str = start;
786 LPWSTR end;
788 while (*str == ' ' && *str != '\0')
789 str++;
791 if (str != start)
792 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
794 end = start + strlenW(start) - 1;
795 while (end >= start && *end == ' ')
797 *end = '\0';
798 end--;
802 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
804 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
805 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
806 BOOL is_basic;
807 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
808 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
809 if (is_basic && pszRealm)
811 LPCWSTR token;
812 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
813 LPCWSTR realm;
814 ptr++;
815 *pszRealm=NULL;
816 token = strchrW(ptr,'=');
817 if (!token)
818 return TRUE;
819 realm = ptr;
820 while (*realm == ' ' && *realm != '\0')
821 realm++;
822 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
823 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
825 token++;
826 while (*token == ' ' && *token != '\0')
827 token++;
828 if (*token == '\0')
829 return TRUE;
830 *pszRealm = heap_strdupW(token);
831 strip_spaces(*pszRealm);
835 return is_basic;
838 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
840 if (!authinfo) return;
842 if (SecIsValidHandle(&authinfo->ctx))
843 DeleteSecurityContext(&authinfo->ctx);
844 if (SecIsValidHandle(&authinfo->cred))
845 FreeCredentialsHandle(&authinfo->cred);
847 heap_free(authinfo->auth_data);
848 heap_free(authinfo->scheme);
849 heap_free(authinfo);
852 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
854 basicAuthorizationData *ad;
855 UINT rc = 0;
857 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
859 EnterCriticalSection(&authcache_cs);
860 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
862 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
864 TRACE("Authorization found in cache\n");
865 *auth_data = heap_alloc(ad->authorizationLen);
866 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
867 rc = ad->authorizationLen;
868 break;
871 LeaveCriticalSection(&authcache_cs);
872 return rc;
875 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
877 struct list *cursor;
878 basicAuthorizationData* ad = NULL;
880 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
882 EnterCriticalSection(&authcache_cs);
883 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
885 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
886 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
888 ad = check;
889 break;
893 if (ad)
895 TRACE("Found match in cache, replacing\n");
896 heap_free(ad->authorization);
897 ad->authorization = heap_alloc(auth_data_len);
898 memcpy(ad->authorization, auth_data, auth_data_len);
899 ad->authorizationLen = auth_data_len;
901 else
903 ad = heap_alloc(sizeof(basicAuthorizationData));
904 ad->host = heap_strdupW(host);
905 ad->realm = heap_strdupW(realm);
906 ad->authorization = heap_alloc(auth_data_len);
907 memcpy(ad->authorization, auth_data, auth_data_len);
908 ad->authorizationLen = auth_data_len;
909 list_add_head(&basicAuthorizationCache,&ad->entry);
910 TRACE("authorization cached\n");
912 LeaveCriticalSection(&authcache_cs);
915 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
916 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
918 authorizationData *ad;
920 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
922 EnterCriticalSection(&authcache_cs);
923 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
924 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
925 TRACE("Authorization found in cache\n");
927 nt_auth_identity->User = heap_strdupW(ad->user);
928 nt_auth_identity->Password = heap_strdupW(ad->password);
929 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
930 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
931 (!nt_auth_identity->Domain && ad->domain_len)) {
932 heap_free(nt_auth_identity->User);
933 heap_free(nt_auth_identity->Password);
934 heap_free(nt_auth_identity->Domain);
935 break;
938 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
939 nt_auth_identity->UserLength = ad->user_len;
940 nt_auth_identity->PasswordLength = ad->password_len;
941 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
942 nt_auth_identity->DomainLength = ad->domain_len;
943 LeaveCriticalSection(&authcache_cs);
944 return TRUE;
947 LeaveCriticalSection(&authcache_cs);
949 return FALSE;
952 static void cache_authorization(LPWSTR host, LPWSTR scheme,
953 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
955 authorizationData *ad;
956 BOOL found = FALSE;
958 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
960 EnterCriticalSection(&authcache_cs);
961 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
962 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
963 found = TRUE;
964 break;
967 if(found) {
968 heap_free(ad->user);
969 heap_free(ad->password);
970 heap_free(ad->domain);
971 } else {
972 ad = heap_alloc(sizeof(authorizationData));
973 if(!ad) {
974 LeaveCriticalSection(&authcache_cs);
975 return;
978 ad->host = heap_strdupW(host);
979 ad->scheme = heap_strdupW(scheme);
980 list_add_head(&authorizationCache, &ad->entry);
983 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
984 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
985 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
986 ad->user_len = nt_auth_identity->UserLength;
987 ad->password_len = nt_auth_identity->PasswordLength;
988 ad->domain_len = nt_auth_identity->DomainLength;
990 if(!ad->host || !ad->scheme || !ad->user || !ad->password
991 || (nt_auth_identity->Domain && !ad->domain)) {
992 heap_free(ad->host);
993 heap_free(ad->scheme);
994 heap_free(ad->user);
995 heap_free(ad->password);
996 heap_free(ad->domain);
997 list_remove(&ad->entry);
998 heap_free(ad);
1001 LeaveCriticalSection(&authcache_cs);
1004 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
1005 struct HttpAuthInfo **ppAuthInfo,
1006 LPWSTR domain_and_username, LPWSTR password,
1007 LPWSTR host )
1009 SECURITY_STATUS sec_status;
1010 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
1011 BOOL first = FALSE;
1012 LPWSTR szRealm = NULL;
1014 TRACE("%s\n", debugstr_w(pszAuthValue));
1016 if (!pAuthInfo)
1018 TimeStamp exp;
1020 first = TRUE;
1021 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
1022 if (!pAuthInfo)
1023 return FALSE;
1025 SecInvalidateHandle(&pAuthInfo->cred);
1026 SecInvalidateHandle(&pAuthInfo->ctx);
1027 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
1028 pAuthInfo->attr = 0;
1029 pAuthInfo->auth_data = NULL;
1030 pAuthInfo->auth_data_len = 0;
1031 pAuthInfo->finished = FALSE;
1033 if (is_basic_auth_value(pszAuthValue,NULL))
1035 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1036 pAuthInfo->scheme = heap_strdupW(szBasic);
1037 if (!pAuthInfo->scheme)
1039 heap_free(pAuthInfo);
1040 return FALSE;
1043 else
1045 PVOID pAuthData;
1046 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1048 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1049 if (!pAuthInfo->scheme)
1051 heap_free(pAuthInfo);
1052 return FALSE;
1055 if (domain_and_username)
1057 WCHAR *user = strchrW(domain_and_username, '\\');
1058 WCHAR *domain = domain_and_username;
1060 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1062 pAuthData = &nt_auth_identity;
1064 if (user) user++;
1065 else
1067 user = domain_and_username;
1068 domain = NULL;
1071 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1072 nt_auth_identity.User = user;
1073 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1074 nt_auth_identity.Domain = domain;
1075 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1076 nt_auth_identity.Password = password;
1077 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1079 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1081 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1082 pAuthData = &nt_auth_identity;
1083 else
1084 /* use default credentials */
1085 pAuthData = NULL;
1087 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1088 SECPKG_CRED_OUTBOUND, NULL,
1089 pAuthData, NULL,
1090 NULL, &pAuthInfo->cred,
1091 &exp);
1093 if(pAuthData && !domain_and_username) {
1094 heap_free(nt_auth_identity.User);
1095 heap_free(nt_auth_identity.Domain);
1096 heap_free(nt_auth_identity.Password);
1099 if (sec_status == SEC_E_OK)
1101 PSecPkgInfoW sec_pkg_info;
1102 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1103 if (sec_status == SEC_E_OK)
1105 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1106 FreeContextBuffer(sec_pkg_info);
1109 if (sec_status != SEC_E_OK)
1111 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1112 debugstr_w(pAuthInfo->scheme), sec_status);
1113 heap_free(pAuthInfo->scheme);
1114 heap_free(pAuthInfo);
1115 return FALSE;
1118 *ppAuthInfo = pAuthInfo;
1120 else if (pAuthInfo->finished)
1121 return FALSE;
1123 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1124 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1126 ERR("authentication scheme changed from %s to %s\n",
1127 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1128 return FALSE;
1131 if (is_basic_auth_value(pszAuthValue,&szRealm))
1133 int userlen;
1134 int passlen;
1135 char *auth_data = NULL;
1136 UINT auth_data_len = 0;
1138 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1140 if (!domain_and_username)
1142 if (host && szRealm)
1143 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1144 if (auth_data_len == 0)
1146 heap_free(szRealm);
1147 return FALSE;
1150 else
1152 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1153 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1155 /* length includes a nul terminator, which will be re-used for the ':' */
1156 auth_data = heap_alloc(userlen + 1 + passlen);
1157 if (!auth_data)
1159 heap_free(szRealm);
1160 return FALSE;
1163 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1164 auth_data[userlen] = ':';
1165 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1166 auth_data_len = userlen + 1 + passlen;
1167 if (host && szRealm)
1168 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1171 pAuthInfo->auth_data = auth_data;
1172 pAuthInfo->auth_data_len = auth_data_len;
1173 pAuthInfo->finished = TRUE;
1174 heap_free(szRealm);
1175 return TRUE;
1177 else
1179 LPCWSTR pszAuthData;
1180 SecBufferDesc out_desc, in_desc;
1181 SecBuffer out, in;
1182 unsigned char *buffer;
1183 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1184 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1186 in.BufferType = SECBUFFER_TOKEN;
1187 in.cbBuffer = 0;
1188 in.pvBuffer = NULL;
1190 in_desc.ulVersion = 0;
1191 in_desc.cBuffers = 1;
1192 in_desc.pBuffers = &in;
1194 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1195 if (*pszAuthData == ' ')
1197 pszAuthData++;
1198 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1199 in.pvBuffer = heap_alloc(in.cbBuffer);
1200 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1203 buffer = heap_alloc(pAuthInfo->max_token);
1205 out.BufferType = SECBUFFER_TOKEN;
1206 out.cbBuffer = pAuthInfo->max_token;
1207 out.pvBuffer = buffer;
1209 out_desc.ulVersion = 0;
1210 out_desc.cBuffers = 1;
1211 out_desc.pBuffers = &out;
1213 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1214 first ? NULL : &pAuthInfo->ctx,
1215 first ? request->server->name : NULL,
1216 context_req, 0, SECURITY_NETWORK_DREP,
1217 in.pvBuffer ? &in_desc : NULL,
1218 0, &pAuthInfo->ctx, &out_desc,
1219 &pAuthInfo->attr, &pAuthInfo->exp);
1220 if (sec_status == SEC_E_OK)
1222 pAuthInfo->finished = TRUE;
1223 pAuthInfo->auth_data = out.pvBuffer;
1224 pAuthInfo->auth_data_len = out.cbBuffer;
1225 TRACE("sending last auth packet\n");
1227 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1229 pAuthInfo->auth_data = out.pvBuffer;
1230 pAuthInfo->auth_data_len = out.cbBuffer;
1231 TRACE("sending next auth packet\n");
1233 else
1235 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1236 heap_free(out.pvBuffer);
1237 destroy_authinfo(pAuthInfo);
1238 *ppAuthInfo = NULL;
1239 return FALSE;
1243 return TRUE;
1246 /***********************************************************************
1247 * HTTP_HttpAddRequestHeadersW (internal)
1249 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1250 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1252 LPWSTR lpszStart;
1253 LPWSTR lpszEnd;
1254 LPWSTR buffer;
1255 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1257 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1259 if( dwHeaderLength == ~0U )
1260 len = strlenW(lpszHeader);
1261 else
1262 len = dwHeaderLength;
1263 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1264 lstrcpynW( buffer, lpszHeader, len + 1);
1266 lpszStart = buffer;
1270 LPWSTR * pFieldAndValue;
1272 lpszEnd = lpszStart;
1274 while (*lpszEnd != '\0')
1276 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1277 break;
1278 lpszEnd++;
1281 if (*lpszStart == '\0')
1282 break;
1284 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1286 *lpszEnd = '\0';
1287 lpszEnd++; /* Jump over newline */
1289 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1290 if (*lpszStart == '\0')
1292 /* Skip 0-length headers */
1293 lpszStart = lpszEnd;
1294 res = ERROR_SUCCESS;
1295 continue;
1297 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1298 if (pFieldAndValue)
1300 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1301 if (res == ERROR_SUCCESS)
1302 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1303 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1304 HTTP_FreeTokens(pFieldAndValue);
1307 lpszStart = lpszEnd;
1308 } while (res == ERROR_SUCCESS);
1310 heap_free(buffer);
1311 return res;
1314 /***********************************************************************
1315 * HttpAddRequestHeadersW (WININET.@)
1317 * Adds one or more HTTP header to the request handler
1319 * NOTE
1320 * On Windows if dwHeaderLength includes the trailing '\0', then
1321 * HttpAddRequestHeadersW() adds it too. However this results in an
1322 * invalid HTTP header which is rejected by some servers so we probably
1323 * don't need to match Windows on that point.
1325 * RETURNS
1326 * TRUE on success
1327 * FALSE on failure
1330 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1331 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1333 http_request_t *request;
1334 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1336 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1338 if (!lpszHeader)
1339 return TRUE;
1341 request = (http_request_t*) get_handle_object( hHttpRequest );
1342 if (request && request->hdr.htype == WH_HHTTPREQ)
1343 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1344 if( request )
1345 WININET_Release( &request->hdr );
1347 if(res != ERROR_SUCCESS)
1348 SetLastError(res);
1349 return res == ERROR_SUCCESS;
1352 /***********************************************************************
1353 * HttpAddRequestHeadersA (WININET.@)
1355 * Adds one or more HTTP header to the request handler
1357 * RETURNS
1358 * TRUE on success
1359 * FALSE on failure
1362 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1363 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1365 DWORD len;
1366 LPWSTR hdr;
1367 BOOL r;
1369 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1371 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1372 hdr = heap_alloc(len*sizeof(WCHAR));
1373 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1374 if( dwHeaderLength != ~0U )
1375 dwHeaderLength = len;
1377 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1379 heap_free( hdr );
1380 return r;
1383 static void free_accept_types( WCHAR **accept_types )
1385 WCHAR *ptr, **types = accept_types;
1387 if (!types) return;
1388 while ((ptr = *types))
1390 heap_free( ptr );
1391 types++;
1393 heap_free( accept_types );
1396 static WCHAR **convert_accept_types( const char **accept_types )
1398 unsigned int count;
1399 const char **types = accept_types;
1400 WCHAR **typesW;
1401 BOOL invalid_pointer = FALSE;
1403 if (!types) return NULL;
1404 count = 0;
1405 while (*types)
1407 __TRY
1409 /* find out how many there are */
1410 if (*types && **types)
1412 TRACE("accept type: %s\n", debugstr_a(*types));
1413 count++;
1416 __EXCEPT_PAGE_FAULT
1418 WARN("invalid accept type pointer\n");
1419 invalid_pointer = TRUE;
1421 __ENDTRY;
1422 types++;
1424 if (invalid_pointer) return NULL;
1425 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1426 count = 0;
1427 types = accept_types;
1428 while (*types)
1430 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1431 types++;
1433 typesW[count] = NULL;
1434 return typesW;
1437 /***********************************************************************
1438 * HttpOpenRequestA (WININET.@)
1440 * Open a HTTP request handle
1442 * RETURNS
1443 * HINTERNET a HTTP request handle on success
1444 * NULL on failure
1447 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1448 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1449 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1450 DWORD dwFlags, DWORD_PTR dwContext)
1452 LPWSTR szVerb = NULL, szObjectName = NULL;
1453 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1454 HINTERNET rc = FALSE;
1456 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1457 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1458 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1459 dwFlags, dwContext);
1461 if (lpszVerb)
1463 szVerb = heap_strdupAtoW(lpszVerb);
1464 if ( !szVerb )
1465 goto end;
1468 if (lpszObjectName)
1470 szObjectName = heap_strdupAtoW(lpszObjectName);
1471 if ( !szObjectName )
1472 goto end;
1475 if (lpszVersion)
1477 szVersion = heap_strdupAtoW(lpszVersion);
1478 if ( !szVersion )
1479 goto end;
1482 if (lpszReferrer)
1484 szReferrer = heap_strdupAtoW(lpszReferrer);
1485 if ( !szReferrer )
1486 goto end;
1489 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1490 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1491 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1493 end:
1494 free_accept_types(szAcceptTypes);
1495 heap_free(szReferrer);
1496 heap_free(szVersion);
1497 heap_free(szObjectName);
1498 heap_free(szVerb);
1499 return rc;
1502 /***********************************************************************
1503 * HTTP_EncodeBase64
1505 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1507 UINT n = 0, x;
1508 static const CHAR HTTP_Base64Enc[] =
1509 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1511 while( len > 0 )
1513 /* first 6 bits, all from bin[0] */
1514 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1515 x = (bin[0] & 3) << 4;
1517 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1518 if( len == 1 )
1520 base64[n++] = HTTP_Base64Enc[x];
1521 base64[n++] = '=';
1522 base64[n++] = '=';
1523 break;
1525 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1526 x = ( bin[1] & 0x0f ) << 2;
1528 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1529 if( len == 2 )
1531 base64[n++] = HTTP_Base64Enc[x];
1532 base64[n++] = '=';
1533 break;
1535 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1537 /* last 6 bits, all from bin [2] */
1538 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1539 bin += 3;
1540 len -= 3;
1542 base64[n] = 0;
1543 return n;
1546 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1547 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1548 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1549 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1550 static const signed char HTTP_Base64Dec[256] =
1552 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1553 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1554 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1555 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1556 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1557 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1558 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1559 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1560 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1561 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1562 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1563 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1564 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1565 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1566 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1567 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1568 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1569 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1570 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1571 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1572 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1573 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1574 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1575 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1576 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1577 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1579 #undef CH
1581 /***********************************************************************
1582 * HTTP_DecodeBase64
1584 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1586 unsigned int n = 0;
1588 while(*base64)
1590 signed char in[4];
1592 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1593 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1594 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1595 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1597 WARN("invalid base64: %s\n", debugstr_w(base64));
1598 return 0;
1600 if (bin)
1601 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1602 n++;
1604 if ((base64[2] == '=') && (base64[3] == '='))
1605 break;
1606 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1607 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1609 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1610 return 0;
1612 if (bin)
1613 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1614 n++;
1616 if (base64[3] == '=')
1617 break;
1618 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1619 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1621 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1622 return 0;
1624 if (bin)
1625 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1626 n++;
1628 base64 += 4;
1631 return n;
1634 /***********************************************************************
1635 * HTTP_InsertAuthorization
1637 * Insert or delete the authorization field in the request header.
1639 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1641 if (pAuthInfo)
1643 static const WCHAR wszSpace[] = {' ',0};
1644 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1645 unsigned int len;
1646 WCHAR *authorization = NULL;
1648 if (pAuthInfo->auth_data_len)
1650 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1651 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1652 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1653 if (!authorization)
1654 return FALSE;
1656 strcpyW(authorization, pAuthInfo->scheme);
1657 strcatW(authorization, wszSpace);
1658 HTTP_EncodeBase64(pAuthInfo->auth_data,
1659 pAuthInfo->auth_data_len,
1660 authorization+strlenW(authorization));
1662 /* clear the data as it isn't valid now that it has been sent to the
1663 * server, unless it's Basic authentication which doesn't do
1664 * connection tracking */
1665 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1667 heap_free(pAuthInfo->auth_data);
1668 pAuthInfo->auth_data = NULL;
1669 pAuthInfo->auth_data_len = 0;
1673 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1675 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1676 heap_free(authorization);
1678 return TRUE;
1681 static WCHAR *build_proxy_path_url(http_request_t *req)
1683 DWORD size, len;
1684 WCHAR *url;
1686 len = strlenW(req->server->scheme_host_port);
1687 size = len + strlenW(req->path) + 1;
1688 if(*req->path != '/')
1689 size++;
1690 url = heap_alloc(size * sizeof(WCHAR));
1691 if(!url)
1692 return NULL;
1694 memcpy(url, req->server->scheme_host_port, len*sizeof(WCHAR));
1695 if(*req->path != '/')
1696 url[len++] = '/';
1698 strcpyW(url+len, req->path);
1700 TRACE("url=%s\n", debugstr_w(url));
1701 return url;
1704 /***********************************************************************
1705 * HTTP_DealWithProxy
1707 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1709 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1710 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1711 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1712 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1713 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1714 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1715 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1716 static WCHAR szNul[] = { 0 };
1717 URL_COMPONENTSW UrlComponents;
1718 server_t *new_server;
1719 BOOL is_https;
1721 memset( &UrlComponents, 0, sizeof UrlComponents );
1722 UrlComponents.dwStructSize = sizeof UrlComponents;
1723 UrlComponents.lpszHostName = buf;
1724 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1726 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1727 return FALSE;
1728 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1729 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1730 sprintfW(proxy, szFormat, protoProxy);
1731 else
1732 strcpyW(proxy, protoProxy);
1733 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1734 return FALSE;
1735 if( UrlComponents.dwHostNameLength == 0 )
1736 return FALSE;
1738 if( !request->path )
1739 request->path = szNul;
1741 is_https = (UrlComponents.nScheme == INTERNET_SCHEME_HTTPS);
1742 if (is_https && UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1743 UrlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
1745 new_server = get_server(UrlComponents.lpszHostName, UrlComponents.nPort, is_https, TRUE);
1746 if(!new_server)
1747 return FALSE;
1749 request->proxy = new_server;
1751 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1752 return TRUE;
1755 static DWORD HTTP_ResolveName(http_request_t *request)
1757 server_t *server = request->proxy ? request->proxy : request->server;
1758 socklen_t addr_len;
1759 void *addr;
1761 if(server->addr_len)
1762 return ERROR_SUCCESS;
1764 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1765 INTERNET_STATUS_RESOLVING_NAME,
1766 server->name,
1767 (strlenW(server->name)+1) * sizeof(WCHAR));
1769 addr_len = sizeof(server->addr);
1770 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1771 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1773 switch(server->addr.ss_family) {
1774 case AF_INET:
1775 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1776 break;
1777 case AF_INET6:
1778 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1779 break;
1780 default:
1781 WARN("unsupported family %d\n", server->addr.ss_family);
1782 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1785 server->addr_len = addr_len;
1786 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1787 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1788 INTERNET_STATUS_NAME_RESOLVED,
1789 server->addr_str, strlen(server->addr_str)+1);
1791 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1792 return ERROR_SUCCESS;
1795 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1797 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1798 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1799 static const WCHAR slash[] = { '/',0 };
1800 LPHTTPHEADERW host_header;
1801 LPCWSTR scheme;
1803 host_header = HTTP_GetHeader(req, hostW);
1804 if(!host_header)
1805 return FALSE;
1807 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1808 scheme = https;
1809 else
1810 scheme = http;
1811 strcpyW(buf, scheme);
1812 strcatW(buf, host_header->lpszValue);
1813 if (req->path[0] != '/')
1814 strcatW(buf, slash);
1815 strcatW(buf, req->path);
1816 return TRUE;
1820 /***********************************************************************
1821 * HTTPREQ_Destroy (internal)
1823 * Deallocate request handle
1826 static void HTTPREQ_Destroy(object_header_t *hdr)
1828 http_request_t *request = (http_request_t*) hdr;
1829 DWORD i;
1831 TRACE("\n");
1833 if(request->hCacheFile) {
1834 CloseHandle(request->hCacheFile);
1835 DeleteFileW(request->cacheFile);
1837 heap_free(request->cacheFile);
1839 request->read_section.DebugInfo->Spare[0] = 0;
1840 DeleteCriticalSection( &request->read_section );
1841 WININET_Release(&request->session->hdr);
1843 destroy_authinfo(request->authInfo);
1844 destroy_authinfo(request->proxyAuthInfo);
1846 if(request->server)
1847 server_release(request->server);
1848 if(request->proxy)
1849 server_release(request->proxy);
1851 heap_free(request->path);
1852 heap_free(request->verb);
1853 heap_free(request->rawHeaders);
1854 heap_free(request->version);
1855 heap_free(request->statusText);
1857 for (i = 0; i < request->nCustHeaders; i++)
1859 heap_free(request->custHeaders[i].lpszField);
1860 heap_free(request->custHeaders[i].lpszValue);
1862 destroy_data_stream(request->data_stream);
1863 heap_free(request->custHeaders);
1866 static void http_release_netconn(http_request_t *req, BOOL reuse)
1868 TRACE("%p %p\n",req, req->netconn);
1870 if(!req->netconn)
1871 return;
1873 if(reuse && req->netconn->keep_alive) {
1874 BOOL run_collector;
1876 EnterCriticalSection(&connection_pool_cs);
1878 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1879 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1880 req->netconn = NULL;
1882 run_collector = !collector_running;
1883 collector_running = TRUE;
1885 LeaveCriticalSection(&connection_pool_cs);
1887 if(run_collector) {
1888 HANDLE thread = NULL;
1889 HMODULE module;
1891 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1892 if(module)
1893 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1894 if(!thread) {
1895 EnterCriticalSection(&connection_pool_cs);
1896 collector_running = FALSE;
1897 LeaveCriticalSection(&connection_pool_cs);
1899 if(module)
1900 FreeLibrary(module);
1902 else
1903 CloseHandle(thread);
1905 return;
1908 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1909 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1911 free_netconn(req->netconn);
1912 req->netconn = NULL;
1914 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1915 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1918 static BOOL HTTP_KeepAlive(http_request_t *request)
1920 WCHAR szVersion[10];
1921 WCHAR szConnectionResponse[20];
1922 DWORD dwBufferSize = sizeof(szVersion);
1923 BOOL keepalive = FALSE;
1925 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1926 * the connection is keep-alive by default */
1927 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1928 && !strcmpiW(szVersion, g_szHttp1_1))
1930 keepalive = TRUE;
1933 dwBufferSize = sizeof(szConnectionResponse);
1934 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1935 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1937 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1940 return keepalive;
1943 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1945 http_request_t *req = (http_request_t*)hdr;
1947 http_release_netconn(req, drain_content(req, FALSE));
1950 static DWORD str_to_buffer(const WCHAR *str, void *buffer, DWORD *size, BOOL unicode)
1952 int len;
1953 if (unicode)
1955 len = strlenW(str);
1956 if (*size < (len + 1) * sizeof(WCHAR))
1958 *size = (len + 1) * sizeof(WCHAR);
1959 return ERROR_INSUFFICIENT_BUFFER;
1961 strcpyW(buffer, str);
1962 *size = len;
1963 return ERROR_SUCCESS;
1965 else
1967 len = WideCharToMultiByte(CP_ACP, 0, str, -1, buffer, *size, NULL, NULL);
1968 if (*size < len)
1970 *size = len;
1971 return ERROR_INSUFFICIENT_BUFFER;
1973 *size = len - 1;
1974 return ERROR_SUCCESS;
1978 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1980 http_request_t *req = (http_request_t*)hdr;
1982 switch(option) {
1983 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1985 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1987 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1989 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1990 return ERROR_INSUFFICIENT_BUFFER;
1991 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1992 /* FIXME: can't get a SOCKET from our connection since we don't use
1993 * winsock
1995 info->Socket = 0;
1996 /* FIXME: get source port from req->netConnection */
1997 info->SourcePort = 0;
1998 info->DestPort = req->server->port;
1999 info->Flags = 0;
2000 if (HTTP_KeepAlive(req))
2001 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
2002 if (req->proxy)
2003 info->Flags |= IDSI_FLAG_PROXY;
2004 if (req->netconn->secure)
2005 info->Flags |= IDSI_FLAG_SECURE;
2007 return ERROR_SUCCESS;
2010 case 98:
2011 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
2012 /* fall through */
2013 case INTERNET_OPTION_SECURITY_FLAGS:
2015 DWORD flags;
2017 if (*size < sizeof(ULONG))
2018 return ERROR_INSUFFICIENT_BUFFER;
2020 *size = sizeof(DWORD);
2021 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
2022 *(DWORD *)buffer = flags;
2024 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
2025 return ERROR_SUCCESS;
2028 case INTERNET_OPTION_HANDLE_TYPE:
2029 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2031 if (*size < sizeof(ULONG))
2032 return ERROR_INSUFFICIENT_BUFFER;
2034 *size = sizeof(DWORD);
2035 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2036 return ERROR_SUCCESS;
2038 case INTERNET_OPTION_URL: {
2039 WCHAR url[INTERNET_MAX_URL_LENGTH];
2040 HTTPHEADERW *host;
2042 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2044 TRACE("INTERNET_OPTION_URL\n");
2046 host = HTTP_GetHeader(req, hostW);
2047 strcpyW(url, httpW);
2048 strcatW(url, host->lpszValue);
2049 strcatW(url, req->path);
2051 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2052 return str_to_buffer(url, buffer, size, unicode);
2054 case INTERNET_OPTION_USER_AGENT:
2055 return str_to_buffer(req->session->appInfo->agent, buffer, size, unicode);
2056 case INTERNET_OPTION_USERNAME:
2057 return str_to_buffer(req->session->userName, buffer, size, unicode);
2058 case INTERNET_OPTION_PASSWORD:
2059 return str_to_buffer(req->session->password, buffer, size, unicode);
2060 case INTERNET_OPTION_PROXY_USERNAME:
2061 return str_to_buffer(req->session->appInfo->proxyUsername, buffer, size, unicode);
2062 case INTERNET_OPTION_PROXY_PASSWORD:
2063 return str_to_buffer(req->session->appInfo->proxyPassword, buffer, size, unicode);
2065 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2066 INTERNET_CACHE_ENTRY_INFOW *info;
2067 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2068 WCHAR url[INTERNET_MAX_URL_LENGTH];
2069 DWORD nbytes, error;
2070 BOOL ret;
2072 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2074 if (*size < sizeof(*ts))
2076 *size = sizeof(*ts);
2077 return ERROR_INSUFFICIENT_BUFFER;
2079 nbytes = 0;
2080 HTTP_GetRequestURL(req, url);
2081 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2082 error = GetLastError();
2083 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2085 if (!(info = heap_alloc(nbytes)))
2086 return ERROR_OUTOFMEMORY;
2088 GetUrlCacheEntryInfoW(url, info, &nbytes);
2090 ts->ftExpires = info->ExpireTime;
2091 ts->ftLastModified = info->LastModifiedTime;
2093 heap_free(info);
2094 *size = sizeof(*ts);
2095 return ERROR_SUCCESS;
2097 return error;
2100 case INTERNET_OPTION_DATAFILE_NAME: {
2101 DWORD req_size;
2103 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2105 if(!req->cacheFile) {
2106 *size = 0;
2107 return ERROR_INTERNET_ITEM_NOT_FOUND;
2110 if(unicode) {
2111 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2112 if(*size < req_size)
2113 return ERROR_INSUFFICIENT_BUFFER;
2115 *size = req_size;
2116 memcpy(buffer, req->cacheFile, *size);
2117 return ERROR_SUCCESS;
2118 }else {
2119 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2120 if (req_size > *size)
2121 return ERROR_INSUFFICIENT_BUFFER;
2123 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2124 -1, buffer, *size, NULL, NULL);
2125 return ERROR_SUCCESS;
2129 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2130 PCCERT_CONTEXT context;
2132 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2133 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2134 return ERROR_INSUFFICIENT_BUFFER;
2137 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2138 if(context) {
2139 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2140 DWORD len;
2142 memset(info, 0, sizeof(*info));
2143 info->ftExpiry = context->pCertInfo->NotAfter;
2144 info->ftStart = context->pCertInfo->NotBefore;
2145 len = CertNameToStrA(context->dwCertEncodingType,
2146 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2147 info->lpszSubjectInfo = LocalAlloc(0, len);
2148 if(info->lpszSubjectInfo)
2149 CertNameToStrA(context->dwCertEncodingType,
2150 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2151 info->lpszSubjectInfo, len);
2152 len = CertNameToStrA(context->dwCertEncodingType,
2153 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2154 info->lpszIssuerInfo = LocalAlloc(0, len);
2155 if(info->lpszIssuerInfo)
2156 CertNameToStrA(context->dwCertEncodingType,
2157 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2158 info->lpszIssuerInfo, len);
2159 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2160 CertFreeCertificateContext(context);
2161 return ERROR_SUCCESS;
2163 return ERROR_NOT_SUPPORTED;
2165 case INTERNET_OPTION_CONNECT_TIMEOUT:
2166 if (*size < sizeof(DWORD))
2167 return ERROR_INSUFFICIENT_BUFFER;
2169 *size = sizeof(DWORD);
2170 *(DWORD *)buffer = req->connect_timeout;
2171 return ERROR_SUCCESS;
2172 case INTERNET_OPTION_REQUEST_FLAGS: {
2173 DWORD flags = 0;
2175 if (*size < sizeof(DWORD))
2176 return ERROR_INSUFFICIENT_BUFFER;
2178 /* FIXME: Add support for:
2179 * INTERNET_REQFLAG_FROM_CACHE
2180 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2183 if(req->proxy)
2184 flags |= INTERNET_REQFLAG_VIA_PROXY;
2185 if(!req->rawHeaders)
2186 flags |= INTERNET_REQFLAG_NO_HEADERS;
2188 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2190 *size = sizeof(DWORD);
2191 *(DWORD*)buffer = flags;
2192 return ERROR_SUCCESS;
2196 return INET_QueryOption(hdr, option, buffer, size, unicode);
2199 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2201 http_request_t *req = (http_request_t*)hdr;
2203 switch(option) {
2204 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2205 TRACE("Undocumented option 99\n");
2207 if (!buffer || size != sizeof(DWORD))
2208 return ERROR_INVALID_PARAMETER;
2209 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2210 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2212 /* fall through */
2213 case INTERNET_OPTION_SECURITY_FLAGS:
2215 DWORD flags;
2217 if (!buffer || size != sizeof(DWORD))
2218 return ERROR_INVALID_PARAMETER;
2219 flags = *(DWORD *)buffer;
2220 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2221 flags &= SECURITY_SET_MASK;
2222 req->security_flags |= flags;
2223 if(req->netconn)
2224 req->netconn->security_flags |= flags;
2225 return ERROR_SUCCESS;
2227 case INTERNET_OPTION_CONNECT_TIMEOUT:
2228 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2229 req->connect_timeout = *(DWORD *)buffer;
2230 return ERROR_SUCCESS;
2232 case INTERNET_OPTION_SEND_TIMEOUT:
2233 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2234 req->send_timeout = *(DWORD *)buffer;
2235 return ERROR_SUCCESS;
2237 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2238 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2239 req->receive_timeout = *(DWORD *)buffer;
2240 return ERROR_SUCCESS;
2242 case INTERNET_OPTION_USERNAME:
2243 heap_free(req->session->userName);
2244 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2245 return ERROR_SUCCESS;
2247 case INTERNET_OPTION_PASSWORD:
2248 heap_free(req->session->password);
2249 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2250 return ERROR_SUCCESS;
2252 case INTERNET_OPTION_PROXY_USERNAME:
2253 heap_free(req->session->appInfo->proxyUsername);
2254 if (!(req->session->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2255 return ERROR_SUCCESS;
2257 case INTERNET_OPTION_PROXY_PASSWORD:
2258 heap_free(req->session->appInfo->proxyPassword);
2259 if (!(req->session->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2260 return ERROR_SUCCESS;
2262 case INTERNET_OPTION_HTTP_DECODING:
2263 if(size != sizeof(BOOL))
2264 return ERROR_INVALID_PARAMETER;
2265 req->decoding = *(BOOL*)buffer;
2266 return ERROR_SUCCESS;
2269 return INET_SetOption(hdr, option, buffer, size);
2272 static void commit_cache_entry(http_request_t *req)
2274 WCHAR url[INTERNET_MAX_URL_LENGTH];
2276 TRACE("%p\n", req);
2278 CloseHandle(req->hCacheFile);
2279 req->hCacheFile = NULL;
2281 if(HTTP_GetRequestURL(req, url)) {
2282 DWORD headersLen;
2284 headersLen = req->rawHeaders ? strlenW(req->rawHeaders) : 0;
2285 CommitUrlCacheEntryW(url, req->cacheFile, req->expires,
2286 req->last_modified, NORMAL_CACHE_ENTRY,
2287 req->rawHeaders, headersLen, NULL, 0);
2291 static void create_cache_entry(http_request_t *req)
2293 static const WCHAR no_cacheW[] = {'n','o','-','c','a','c','h','e',0};
2294 static const WCHAR no_storeW[] = {'n','o','-','s','t','o','r','e',0};
2296 WCHAR url[INTERNET_MAX_URL_LENGTH];
2297 WCHAR file_name[MAX_PATH+1];
2298 BOOL b = TRUE;
2300 /* FIXME: We should free previous cache file earlier */
2301 heap_free(req->cacheFile);
2302 CloseHandle(req->hCacheFile);
2303 req->hCacheFile = NULL;
2305 if(req->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE)
2306 b = FALSE;
2308 if(b) {
2309 int header_idx = HTTP_GetCustomHeaderIndex(req, szCache_Control, 0, FALSE);
2310 if(header_idx!=-1 && (!strcmpiW(req->custHeaders[header_idx].lpszValue, no_cacheW)
2311 || !strcmpiW(req->custHeaders[header_idx].lpszValue, no_storeW)))
2312 b = FALSE;
2315 if(!b) {
2316 if(!(req->hdr.dwFlags & INTERNET_FLAG_NEED_FILE))
2317 return;
2319 FIXME("INTERNET_FLAG_NEED_FILE is not supported correctly\n");
2322 b = HTTP_GetRequestURL(req, url);
2323 if(!b) {
2324 WARN("Could not get URL\n");
2325 return;
2328 b = CreateUrlCacheEntryW(url, req->contentLength == ~0u ? 0 : req->contentLength, NULL, file_name, 0);
2329 if(!b) {
2330 WARN("Could not create cache entry: %08x\n", GetLastError());
2331 return;
2334 req->cacheFile = heap_strdupW(file_name);
2335 req->hCacheFile = CreateFileW(req->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2336 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2337 if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2338 WARN("Could not create file: %u\n", GetLastError());
2339 req->hCacheFile = NULL;
2340 return;
2343 if(req->read_size) {
2344 DWORD written;
2346 b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2347 if(!b)
2348 FIXME("WriteFile failed: %u\n", GetLastError());
2350 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2351 commit_cache_entry(req);
2355 /* read some more data into the read buffer (the read section must be held) */
2356 static DWORD read_more_data( http_request_t *req, int maxlen )
2358 DWORD res;
2359 int len;
2361 if (req->read_pos)
2363 /* move existing data to the start of the buffer */
2364 if(req->read_size)
2365 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2366 req->read_pos = 0;
2369 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2371 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2372 maxlen - req->read_size, 0, &len );
2373 if(res == ERROR_SUCCESS)
2374 req->read_size += len;
2376 return res;
2379 /* remove some amount of data from the read buffer (the read section must be held) */
2380 static void remove_data( http_request_t *req, int count )
2382 if (!(req->read_size -= count)) req->read_pos = 0;
2383 else req->read_pos += count;
2386 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2388 int count, bytes_read, pos = 0;
2389 DWORD res;
2391 EnterCriticalSection( &req->read_section );
2392 for (;;)
2394 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2396 if (eol)
2398 count = eol - (req->read_buf + req->read_pos);
2399 bytes_read = count + 1;
2401 else count = bytes_read = req->read_size;
2403 count = min( count, *len - pos );
2404 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2405 pos += count;
2406 remove_data( req, bytes_read );
2407 if (eol) break;
2409 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2411 *len = 0;
2412 TRACE( "returning empty string %u\n", res);
2413 LeaveCriticalSection( &req->read_section );
2414 INTERNET_SetLastError(res);
2415 return FALSE;
2418 LeaveCriticalSection( &req->read_section );
2420 if (pos < *len)
2422 if (pos && buffer[pos - 1] == '\r') pos--;
2423 *len = pos + 1;
2425 buffer[*len - 1] = 0;
2426 TRACE( "returning %s\n", debugstr_a(buffer));
2427 return TRUE;
2430 /* check if we have reached the end of the data to read (the read section must be held) */
2431 static BOOL end_of_read_data( http_request_t *req )
2433 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2436 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
2438 DWORD res;
2440 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
2441 assert(*read <= size);
2443 if(req->hCacheFile) {
2444 if(*read) {
2445 BOOL bres;
2446 DWORD written;
2448 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2449 if(!bres)
2450 FIXME("WriteFile failed: %u\n", GetLastError());
2453 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2454 commit_cache_entry(req);
2457 return res;
2460 /* fetch some more data into the read buffer (the read section must be held) */
2461 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2463 DWORD res, read=0;
2465 if(req->read_size == sizeof(req->read_buf))
2466 return ERROR_SUCCESS;
2468 if(req->read_pos) {
2469 if(req->read_size)
2470 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2471 req->read_pos = 0;
2474 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2475 &read, read_mode);
2476 req->read_size += read;
2478 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2479 if(read_bytes)
2480 *read_bytes = read;
2481 return res;
2484 /* return the size of data available to be read immediately (the read section must be held) */
2485 static DWORD get_avail_data( http_request_t *req )
2487 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2490 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2492 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2493 DWORD avail = 0;
2495 if(req->netconn)
2496 NETCON_query_data_available(req->netconn, &avail);
2497 return netconn_stream->content_length == ~0u
2498 ? avail
2499 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2502 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2504 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2505 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2508 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2509 DWORD *read, read_mode_t read_mode)
2511 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2512 int len = 0;
2514 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2516 if(read_mode == READMODE_NOBLOCK) {
2517 DWORD avail = netconn_get_avail_data(stream, req);
2518 if (size > avail)
2519 size = avail;
2522 if(size && req->netconn) {
2523 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2524 len = 0;
2525 if(!len)
2526 netconn_stream->content_length = netconn_stream->content_read;
2529 netconn_stream->content_read += *read = len;
2530 TRACE("read %u bytes\n", len);
2531 return ERROR_SUCCESS;
2534 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2536 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2537 BYTE buf[1024];
2538 DWORD avail;
2539 int len;
2541 if(netconn_end_of_data(stream, req))
2542 return TRUE;
2544 do {
2545 avail = netconn_get_avail_data(stream, req);
2546 if(!avail)
2547 return FALSE;
2549 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2550 return FALSE;
2552 netconn_stream->content_read += len;
2553 }while(netconn_stream->content_read < netconn_stream->content_length);
2555 return TRUE;
2558 static void netconn_destroy(data_stream_t *stream)
2562 static const data_stream_vtbl_t netconn_stream_vtbl = {
2563 netconn_get_avail_data,
2564 netconn_end_of_data,
2565 netconn_read,
2566 netconn_drain_content,
2567 netconn_destroy
2570 /* read some more data into the read buffer (the read section must be held) */
2571 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2573 DWORD res;
2574 int len;
2576 if (stream->buf_pos)
2578 /* move existing data to the start of the buffer */
2579 if(stream->buf_size)
2580 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2581 stream->buf_pos = 0;
2584 if (maxlen == -1) maxlen = sizeof(stream->buf);
2586 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2587 maxlen - stream->buf_size, 0, &len );
2588 if(res == ERROR_SUCCESS)
2589 stream->buf_size += len;
2591 return res;
2594 /* remove some amount of data from the read buffer (the read section must be held) */
2595 static void remove_chunked_data(chunked_stream_t *stream, int count)
2597 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2598 else stream->buf_pos += count;
2601 /* discard data contents until we reach end of line (the read section must be held) */
2602 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2604 DWORD res;
2608 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2609 if (eol)
2611 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2612 break;
2614 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2615 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2616 } while (stream->buf_size);
2617 return ERROR_SUCCESS;
2620 /* read the size of the next chunk (the read section must be held) */
2621 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2623 /* TODOO */
2624 DWORD chunk_size = 0, res;
2626 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2627 return res;
2629 for (;;)
2631 while (stream->buf_size)
2633 char ch = stream->buf[stream->buf_pos];
2634 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2635 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2636 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2637 else if (ch == ';' || ch == '\r' || ch == '\n')
2639 TRACE( "reading %u byte chunk\n", chunk_size );
2640 stream->chunk_size = chunk_size;
2641 req->contentLength += chunk_size;
2642 return discard_chunked_eol(stream, req);
2644 remove_chunked_data(stream, 1);
2646 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2647 if (!stream->buf_size)
2649 stream->chunk_size = 0;
2650 return ERROR_SUCCESS;
2655 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2657 /* Allow reading only from read buffer */
2658 return 0;
2661 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2663 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2664 return !chunked_stream->chunk_size;
2667 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2668 DWORD *read, read_mode_t read_mode)
2670 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2671 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2673 if(chunked_stream->chunk_size == ~0u) {
2674 res = start_next_chunk(chunked_stream, req);
2675 if(res != ERROR_SUCCESS)
2676 return res;
2679 while(size && chunked_stream->chunk_size) {
2680 if(chunked_stream->buf_size) {
2681 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2683 /* this could block */
2684 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2685 break;
2687 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2688 remove_chunked_data(chunked_stream, read_bytes);
2689 }else {
2690 read_bytes = min(size, chunked_stream->chunk_size);
2692 if(read_mode == READMODE_NOBLOCK) {
2693 DWORD avail;
2695 if(!req->netconn || !NETCON_query_data_available(req->netconn, &avail) || !avail)
2696 break;
2697 if(read_bytes > avail)
2698 read_bytes = avail;
2700 /* this could block */
2701 if(read_bytes == chunked_stream->chunk_size)
2702 break;
2705 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2706 if(res != ERROR_SUCCESS)
2707 break;
2710 chunked_stream->chunk_size -= read_bytes;
2711 size -= read_bytes;
2712 ret_read += read_bytes;
2713 if(!chunked_stream->chunk_size) {
2714 assert(read_mode != READMODE_NOBLOCK);
2715 res = start_next_chunk(chunked_stream, req);
2716 if(res != ERROR_SUCCESS)
2717 break;
2720 if(read_mode == READMODE_ASYNC)
2721 read_mode = READMODE_NOBLOCK;
2724 TRACE("read %u bytes\n", ret_read);
2725 *read = ret_read;
2726 return res;
2729 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2731 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2733 /* FIXME: we can do better */
2734 return !chunked_stream->chunk_size;
2737 static void chunked_destroy(data_stream_t *stream)
2739 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2740 heap_free(chunked_stream);
2743 static const data_stream_vtbl_t chunked_stream_vtbl = {
2744 chunked_get_avail_data,
2745 chunked_end_of_data,
2746 chunked_read,
2747 chunked_drain_content,
2748 chunked_destroy
2751 /* set the request content length based on the headers */
2752 static DWORD set_content_length(http_request_t *request)
2754 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2755 WCHAR encoding[20];
2756 DWORD size;
2758 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2759 request->contentLength = request->netconn_stream.content_length = 0;
2760 return ERROR_SUCCESS;
2763 size = sizeof(request->contentLength);
2764 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2765 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2766 request->contentLength = ~0u;
2767 request->netconn_stream.content_length = request->contentLength;
2768 request->netconn_stream.content_read = request->read_size;
2770 size = sizeof(encoding);
2771 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2772 !strcmpiW(encoding, szChunked))
2774 chunked_stream_t *chunked_stream;
2776 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2777 if(!chunked_stream)
2778 return ERROR_OUTOFMEMORY;
2780 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2781 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2782 chunked_stream->chunk_size = ~0u;
2784 if(request->read_size) {
2785 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2786 chunked_stream->buf_size = request->read_size;
2787 request->read_size = request->read_pos = 0;
2790 request->data_stream = &chunked_stream->data_stream;
2791 request->contentLength = ~0u;
2792 request->read_chunked = TRUE;
2795 if(request->decoding) {
2796 int encoding_idx;
2798 static const WCHAR gzipW[] = {'g','z','i','p',0};
2800 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2801 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2802 return init_gzip_stream(request);
2805 return ERROR_SUCCESS;
2808 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2810 INTERNET_ASYNC_RESULT iar;
2812 iar.dwResult = result;
2813 iar.dwError = error;
2815 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2816 sizeof(INTERNET_ASYNC_RESULT));
2819 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif, DWORD *ret_size)
2821 DWORD res, read = 0, avail = 0;
2822 read_mode_t mode;
2824 TRACE("%p\n", req);
2826 EnterCriticalSection( &req->read_section );
2828 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2829 res = refill_read_buffer(req, mode, &read);
2830 if(res == ERROR_SUCCESS)
2831 avail = get_avail_data(req);
2833 LeaveCriticalSection( &req->read_section );
2835 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2836 WARN("res %u read %u, closing connection\n", res, read);
2837 http_release_netconn(req, FALSE);
2840 if(res != ERROR_SUCCESS) {
2841 send_request_complete(req, 0, res);
2842 return;
2845 if(ret_size)
2846 *ret_size = avail;
2847 if(first_notif)
2848 avail = 0;
2850 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2853 /* read data from the http connection (the read section must be held) */
2854 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2856 DWORD current_read = 0, ret_read = 0;
2857 read_mode_t read_mode;
2858 DWORD res = ERROR_SUCCESS;
2860 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2862 EnterCriticalSection( &req->read_section );
2864 if(req->read_size) {
2865 ret_read = min(size, req->read_size);
2866 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2867 req->read_size -= ret_read;
2868 req->read_pos += ret_read;
2869 if(read_mode == READMODE_ASYNC)
2870 read_mode = READMODE_NOBLOCK;
2873 if(ret_read < size) {
2874 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2875 ret_read += current_read;
2878 LeaveCriticalSection( &req->read_section );
2880 *read = ret_read;
2881 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2883 if(size && !ret_read)
2884 http_release_netconn(req, res == ERROR_SUCCESS);
2886 return res;
2889 static BOOL drain_content(http_request_t *req, BOOL blocking)
2891 BOOL ret;
2893 if(!req->netconn || req->contentLength == -1)
2894 return FALSE;
2896 if(!strcmpW(req->verb, szHEAD))
2897 return TRUE;
2899 if(!blocking)
2900 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2902 EnterCriticalSection( &req->read_section );
2904 while(1) {
2905 DWORD bytes_read, res;
2906 BYTE buf[4096];
2908 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2909 if(res != ERROR_SUCCESS) {
2910 ret = FALSE;
2911 break;
2913 if(!bytes_read) {
2914 ret = TRUE;
2915 break;
2919 LeaveCriticalSection( &req->read_section );
2920 return ret;
2923 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2925 http_request_t *req = (http_request_t*)hdr;
2926 DWORD res;
2928 EnterCriticalSection( &req->read_section );
2929 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2930 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2932 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2933 if(res == ERROR_SUCCESS)
2934 res = hdr->dwError;
2935 LeaveCriticalSection( &req->read_section );
2937 return res;
2940 typedef struct {
2941 task_header_t hdr;
2942 void *buf;
2943 DWORD size;
2944 DWORD *ret_read;
2945 } read_file_ex_task_t;
2947 static void AsyncReadFileExProc(task_header_t *hdr)
2949 read_file_ex_task_t *task = (read_file_ex_task_t*)hdr;
2950 http_request_t *req = (http_request_t*)task->hdr.hdr;
2951 DWORD res;
2953 TRACE("INTERNETREADFILEEXW %p\n", task->hdr.hdr);
2955 res = HTTPREQ_Read(req, task->buf, task->size, task->ret_read, TRUE);
2956 send_request_complete(req, res == ERROR_SUCCESS, res);
2959 static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
2960 DWORD flags, DWORD_PTR context)
2963 http_request_t *req = (http_request_t*)hdr;
2964 DWORD res, read, cread, error = ERROR_SUCCESS;
2966 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2967 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2969 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2971 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2973 read_file_ex_task_t *task;
2975 if (TryEnterCriticalSection( &req->read_section ))
2977 if (get_avail_data(req))
2979 res = HTTPREQ_Read(req, buf, size, &read, FALSE);
2980 LeaveCriticalSection( &req->read_section );
2981 goto done;
2983 LeaveCriticalSection( &req->read_section );
2986 task = alloc_async_task(&req->hdr, AsyncReadFileExProc, sizeof(*task));
2987 task->buf = buf;
2988 task->size = size;
2989 task->ret_read = ret_read;
2991 INTERNET_AsyncCall(&task->hdr);
2993 return ERROR_IO_PENDING;
2996 read = 0;
2998 EnterCriticalSection( &req->read_section );
2999 if(hdr->dwError == ERROR_SUCCESS)
3000 hdr->dwError = INTERNET_HANDLE_IN_USE;
3001 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3002 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3004 while(1) {
3005 res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, !(flags & IRF_NO_WAIT));
3006 if(res != ERROR_SUCCESS)
3007 break;
3009 read += cread;
3010 if(read == size || end_of_read_data(req))
3011 break;
3013 LeaveCriticalSection( &req->read_section );
3015 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3016 &cread, sizeof(cread));
3017 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
3018 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3020 EnterCriticalSection( &req->read_section );
3023 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3024 hdr->dwError = ERROR_SUCCESS;
3025 else
3026 error = hdr->dwError;
3028 LeaveCriticalSection( &req->read_section );
3030 done:
3031 *ret_read = read;
3032 if (res == ERROR_SUCCESS) {
3033 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3034 &read, sizeof(read));
3037 return res==ERROR_SUCCESS ? error : res;
3040 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3042 DWORD res;
3043 http_request_t *request = (http_request_t*)hdr;
3045 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3047 *written = 0;
3048 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3049 if (res == ERROR_SUCCESS)
3050 request->bytesWritten += *written;
3052 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3053 return res;
3056 typedef struct {
3057 task_header_t hdr;
3058 DWORD *ret_size;
3059 } http_data_available_task_t;
3061 static void AsyncQueryDataAvailableProc(task_header_t *hdr)
3063 http_data_available_task_t *task = (http_data_available_task_t*)hdr;
3065 HTTP_ReceiveRequestData((http_request_t*)task->hdr.hdr, FALSE, task->ret_size);
3068 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3070 http_request_t *req = (http_request_t*)hdr;
3072 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3074 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3076 http_data_available_task_t *task;
3078 /* never wait, if we can't enter the section we queue an async request right away */
3079 if (TryEnterCriticalSection( &req->read_section ))
3081 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3082 if ((*available = get_avail_data( req ))) goto done;
3083 if (end_of_read_data( req )) goto done;
3084 LeaveCriticalSection( &req->read_section );
3087 task = alloc_async_task(&req->hdr, AsyncQueryDataAvailableProc, sizeof(*task));
3088 task->ret_size = available;
3089 INTERNET_AsyncCall(&task->hdr);
3090 return ERROR_IO_PENDING;
3093 EnterCriticalSection( &req->read_section );
3095 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3097 refill_read_buffer( req, READMODE_ASYNC, NULL );
3098 *available = get_avail_data( req );
3101 done:
3102 LeaveCriticalSection( &req->read_section );
3104 TRACE( "returning %u\n", *available );
3105 return ERROR_SUCCESS;
3108 static const object_vtbl_t HTTPREQVtbl = {
3109 HTTPREQ_Destroy,
3110 HTTPREQ_CloseConnection,
3111 HTTPREQ_QueryOption,
3112 HTTPREQ_SetOption,
3113 HTTPREQ_ReadFile,
3114 HTTPREQ_ReadFileEx,
3115 HTTPREQ_WriteFile,
3116 HTTPREQ_QueryDataAvailable,
3117 NULL
3120 /***********************************************************************
3121 * HTTP_HttpOpenRequestW (internal)
3123 * Open a HTTP request handle
3125 * RETURNS
3126 * HINTERNET a HTTP request handle on success
3127 * NULL on failure
3130 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3131 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3132 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3133 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3135 appinfo_t *hIC = session->appInfo;
3136 http_request_t *request;
3137 DWORD len, res = ERROR_SUCCESS;
3139 TRACE("-->\n");
3141 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3142 if(!request)
3143 return ERROR_OUTOFMEMORY;
3145 request->hdr.htype = WH_HHTTPREQ;
3146 request->hdr.dwFlags = dwFlags;
3147 request->hdr.dwContext = dwContext;
3148 request->contentLength = ~0u;
3150 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3151 request->data_stream = &request->netconn_stream.data_stream;
3152 request->connect_timeout = session->connect_timeout;
3153 request->send_timeout = session->send_timeout;
3154 request->receive_timeout = session->receive_timeout;
3156 InitializeCriticalSection( &request->read_section );
3157 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3159 WININET_AddRef( &session->hdr );
3160 request->session = session;
3161 list_add_head( &session->hdr.children, &request->hdr.entry );
3163 request->server = get_server(session->hostName, session->hostPort, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3164 if(!request->server) {
3165 WININET_Release(&request->hdr);
3166 return ERROR_OUTOFMEMORY;
3169 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3170 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3171 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3172 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3174 if (lpszObjectName && *lpszObjectName) {
3175 HRESULT rc;
3177 len = 0;
3178 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3179 if (rc != E_POINTER)
3180 len = strlenW(lpszObjectName)+1;
3181 request->path = heap_alloc(len*sizeof(WCHAR));
3182 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3183 URL_ESCAPE_SPACES_ONLY);
3184 if (rc != S_OK)
3186 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3187 strcpyW(request->path,lpszObjectName);
3189 }else {
3190 static const WCHAR slashW[] = {'/',0};
3192 request->path = heap_strdupW(slashW);
3195 if (lpszReferrer && *lpszReferrer)
3196 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3198 if (lpszAcceptTypes)
3200 int i;
3201 for (i = 0; lpszAcceptTypes[i]; i++)
3203 if (!*lpszAcceptTypes[i]) continue;
3204 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3205 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3206 HTTP_ADDHDR_FLAG_REQ |
3207 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3211 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3212 request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3214 HTTP_ProcessHeader(request, hostW, request->server->canon_host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3216 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3217 session->hostPort = INTERNET_DEFAULT_HTTP_PORT;
3219 if (hIC->proxy && hIC->proxy[0])
3220 HTTP_DealWithProxy( hIC, session, request );
3222 INTERNET_SendCallback(&session->hdr, dwContext,
3223 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3224 sizeof(HINTERNET));
3226 TRACE("<-- %u (%p)\n", res, request);
3228 if(res != ERROR_SUCCESS) {
3229 WININET_Release( &request->hdr );
3230 *ret = NULL;
3231 return res;
3234 *ret = request->hdr.hInternet;
3235 return ERROR_SUCCESS;
3238 /***********************************************************************
3239 * HttpOpenRequestW (WININET.@)
3241 * Open a HTTP request handle
3243 * RETURNS
3244 * HINTERNET a HTTP request handle on success
3245 * NULL on failure
3248 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3249 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3250 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3251 DWORD dwFlags, DWORD_PTR dwContext)
3253 http_session_t *session;
3254 HINTERNET handle = NULL;
3255 DWORD res;
3257 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3258 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3259 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3260 dwFlags, dwContext);
3261 if(lpszAcceptTypes!=NULL)
3263 int i;
3264 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3265 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3268 session = (http_session_t*) get_handle_object( hHttpSession );
3269 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3271 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3272 goto lend;
3276 * My tests seem to show that the windows version does not
3277 * become asynchronous until after this point. And anyhow
3278 * if this call was asynchronous then how would you get the
3279 * necessary HINTERNET pointer returned by this function.
3282 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3283 lpszVersion, lpszReferrer, lpszAcceptTypes,
3284 dwFlags, dwContext, &handle);
3285 lend:
3286 if( session )
3287 WININET_Release( &session->hdr );
3288 TRACE("returning %p\n", handle);
3289 if(res != ERROR_SUCCESS)
3290 SetLastError(res);
3291 return handle;
3294 static const LPCWSTR header_lookup[] = {
3295 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3296 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3297 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3298 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3299 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3300 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3301 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3302 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3303 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3304 szDate, /* HTTP_QUERY_DATE = 9 */
3305 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3306 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3307 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3308 szURI, /* HTTP_QUERY_URI = 13 */
3309 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3310 NULL, /* HTTP_QUERY_COST = 15 */
3311 NULL, /* HTTP_QUERY_LINK = 16 */
3312 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3313 NULL, /* HTTP_QUERY_VERSION = 18 */
3314 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3315 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3316 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3317 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3318 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3319 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3320 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3321 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3322 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3323 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3324 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3325 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3326 NULL, /* HTTP_QUERY_FROM = 31 */
3327 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3328 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3329 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3330 szReferer, /* HTTP_QUERY_REFERER = 35 */
3331 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3332 szServer, /* HTTP_QUERY_SERVER = 37 */
3333 NULL, /* HTTP_TITLE = 38 */
3334 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3335 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3336 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3337 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3338 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3339 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3340 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3341 NULL, /* HTTP_QUERY_REFRESH = 46 */
3342 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3343 szAge, /* HTTP_QUERY_AGE = 48 */
3344 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3345 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3346 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3347 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3348 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3349 szETag, /* HTTP_QUERY_ETAG = 54 */
3350 hostW, /* HTTP_QUERY_HOST = 55 */
3351 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3352 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3353 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3354 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3355 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3356 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3357 szRange, /* HTTP_QUERY_RANGE = 62 */
3358 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3359 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3360 szVary, /* HTTP_QUERY_VARY = 65 */
3361 szVia, /* HTTP_QUERY_VIA = 66 */
3362 szWarning, /* HTTP_QUERY_WARNING = 67 */
3363 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3364 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3365 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3368 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3370 /***********************************************************************
3371 * HTTP_HttpQueryInfoW (internal)
3373 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3374 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3376 LPHTTPHEADERW lphttpHdr = NULL;
3377 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3378 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3379 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3380 INT index = -1;
3382 /* Find requested header structure */
3383 switch (level)
3385 case HTTP_QUERY_CUSTOM:
3386 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3387 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3388 break;
3389 case HTTP_QUERY_RAW_HEADERS_CRLF:
3391 LPWSTR headers;
3392 DWORD len = 0;
3393 DWORD res = ERROR_INVALID_PARAMETER;
3395 if (request_only)
3396 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3397 else
3398 headers = request->rawHeaders;
3400 if (headers)
3401 len = strlenW(headers) * sizeof(WCHAR);
3403 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3405 len += sizeof(WCHAR);
3406 res = ERROR_INSUFFICIENT_BUFFER;
3408 else if (lpBuffer)
3410 if (headers)
3411 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3412 else
3414 len = strlenW(szCrLf) * sizeof(WCHAR);
3415 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3417 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3418 res = ERROR_SUCCESS;
3420 *lpdwBufferLength = len;
3422 if (request_only) heap_free(headers);
3423 return res;
3425 case HTTP_QUERY_RAW_HEADERS:
3427 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3428 DWORD i, size = 0;
3429 LPWSTR pszString = lpBuffer;
3431 for (i = 0; ppszRawHeaderLines[i]; i++)
3432 size += strlenW(ppszRawHeaderLines[i]) + 1;
3434 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3436 HTTP_FreeTokens(ppszRawHeaderLines);
3437 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3438 return ERROR_INSUFFICIENT_BUFFER;
3440 if (pszString)
3442 for (i = 0; ppszRawHeaderLines[i]; i++)
3444 DWORD len = strlenW(ppszRawHeaderLines[i]);
3445 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3446 pszString += len+1;
3448 *pszString = '\0';
3449 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3451 *lpdwBufferLength = size * sizeof(WCHAR);
3452 HTTP_FreeTokens(ppszRawHeaderLines);
3454 return ERROR_SUCCESS;
3456 case HTTP_QUERY_STATUS_TEXT:
3457 if (request->statusText)
3459 DWORD len = strlenW(request->statusText);
3460 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3462 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3463 return ERROR_INSUFFICIENT_BUFFER;
3465 if (lpBuffer)
3467 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3468 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3470 *lpdwBufferLength = len * sizeof(WCHAR);
3471 return ERROR_SUCCESS;
3473 break;
3474 case HTTP_QUERY_VERSION:
3475 if (request->version)
3477 DWORD len = strlenW(request->version);
3478 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3480 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3481 return ERROR_INSUFFICIENT_BUFFER;
3483 if (lpBuffer)
3485 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3486 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3488 *lpdwBufferLength = len * sizeof(WCHAR);
3489 return ERROR_SUCCESS;
3491 break;
3492 case HTTP_QUERY_CONTENT_ENCODING:
3493 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3494 requested_index,request_only);
3495 break;
3496 case HTTP_QUERY_STATUS_CODE: {
3497 DWORD res = ERROR_SUCCESS;
3499 if(request_only)
3500 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3502 if(requested_index)
3503 break;
3505 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3506 if(*lpdwBufferLength >= sizeof(DWORD))
3507 *(DWORD*)lpBuffer = request->status_code;
3508 else
3509 res = ERROR_INSUFFICIENT_BUFFER;
3510 *lpdwBufferLength = sizeof(DWORD);
3511 }else {
3512 WCHAR buf[12];
3513 DWORD size;
3514 static const WCHAR formatW[] = {'%','u',0};
3516 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3518 if(size <= *lpdwBufferLength) {
3519 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3520 }else {
3521 size += sizeof(WCHAR);
3522 res = ERROR_INSUFFICIENT_BUFFER;
3525 *lpdwBufferLength = size;
3527 return res;
3529 default:
3530 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3532 if (level < LAST_TABLE_HEADER && header_lookup[level])
3533 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3534 requested_index,request_only);
3537 if (index >= 0)
3538 lphttpHdr = &request->custHeaders[index];
3540 /* Ensure header satisfies requested attributes */
3541 if (!lphttpHdr ||
3542 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3543 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3545 return ERROR_HTTP_HEADER_NOT_FOUND;
3548 if (lpdwIndex) (*lpdwIndex)++;
3550 /* coalesce value to requested type */
3551 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3553 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3554 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3556 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3558 time_t tmpTime;
3559 struct tm tmpTM;
3560 SYSTEMTIME *STHook;
3562 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3564 tmpTM = *gmtime(&tmpTime);
3565 STHook = (SYSTEMTIME *)lpBuffer;
3566 STHook->wDay = tmpTM.tm_mday;
3567 STHook->wHour = tmpTM.tm_hour;
3568 STHook->wMilliseconds = 0;
3569 STHook->wMinute = tmpTM.tm_min;
3570 STHook->wDayOfWeek = tmpTM.tm_wday;
3571 STHook->wMonth = tmpTM.tm_mon + 1;
3572 STHook->wSecond = tmpTM.tm_sec;
3573 STHook->wYear = 1900+tmpTM.tm_year;
3575 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3576 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3577 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3579 else if (lphttpHdr->lpszValue)
3581 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3583 if (len > *lpdwBufferLength)
3585 *lpdwBufferLength = len;
3586 return ERROR_INSUFFICIENT_BUFFER;
3588 if (lpBuffer)
3590 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3591 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3593 *lpdwBufferLength = len - sizeof(WCHAR);
3595 return ERROR_SUCCESS;
3598 /***********************************************************************
3599 * HttpQueryInfoW (WININET.@)
3601 * Queries for information about an HTTP request
3603 * RETURNS
3604 * TRUE on success
3605 * FALSE on failure
3608 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3609 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3611 http_request_t *request;
3612 DWORD res;
3614 if (TRACE_ON(wininet)) {
3615 #define FE(x) { x, #x }
3616 static const wininet_flag_info query_flags[] = {
3617 FE(HTTP_QUERY_MIME_VERSION),
3618 FE(HTTP_QUERY_CONTENT_TYPE),
3619 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3620 FE(HTTP_QUERY_CONTENT_ID),
3621 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3622 FE(HTTP_QUERY_CONTENT_LENGTH),
3623 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3624 FE(HTTP_QUERY_ALLOW),
3625 FE(HTTP_QUERY_PUBLIC),
3626 FE(HTTP_QUERY_DATE),
3627 FE(HTTP_QUERY_EXPIRES),
3628 FE(HTTP_QUERY_LAST_MODIFIED),
3629 FE(HTTP_QUERY_MESSAGE_ID),
3630 FE(HTTP_QUERY_URI),
3631 FE(HTTP_QUERY_DERIVED_FROM),
3632 FE(HTTP_QUERY_COST),
3633 FE(HTTP_QUERY_LINK),
3634 FE(HTTP_QUERY_PRAGMA),
3635 FE(HTTP_QUERY_VERSION),
3636 FE(HTTP_QUERY_STATUS_CODE),
3637 FE(HTTP_QUERY_STATUS_TEXT),
3638 FE(HTTP_QUERY_RAW_HEADERS),
3639 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3640 FE(HTTP_QUERY_CONNECTION),
3641 FE(HTTP_QUERY_ACCEPT),
3642 FE(HTTP_QUERY_ACCEPT_CHARSET),
3643 FE(HTTP_QUERY_ACCEPT_ENCODING),
3644 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3645 FE(HTTP_QUERY_AUTHORIZATION),
3646 FE(HTTP_QUERY_CONTENT_ENCODING),
3647 FE(HTTP_QUERY_FORWARDED),
3648 FE(HTTP_QUERY_FROM),
3649 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3650 FE(HTTP_QUERY_LOCATION),
3651 FE(HTTP_QUERY_ORIG_URI),
3652 FE(HTTP_QUERY_REFERER),
3653 FE(HTTP_QUERY_RETRY_AFTER),
3654 FE(HTTP_QUERY_SERVER),
3655 FE(HTTP_QUERY_TITLE),
3656 FE(HTTP_QUERY_USER_AGENT),
3657 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3658 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3659 FE(HTTP_QUERY_ACCEPT_RANGES),
3660 FE(HTTP_QUERY_SET_COOKIE),
3661 FE(HTTP_QUERY_COOKIE),
3662 FE(HTTP_QUERY_REQUEST_METHOD),
3663 FE(HTTP_QUERY_REFRESH),
3664 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3665 FE(HTTP_QUERY_AGE),
3666 FE(HTTP_QUERY_CACHE_CONTROL),
3667 FE(HTTP_QUERY_CONTENT_BASE),
3668 FE(HTTP_QUERY_CONTENT_LOCATION),
3669 FE(HTTP_QUERY_CONTENT_MD5),
3670 FE(HTTP_QUERY_CONTENT_RANGE),
3671 FE(HTTP_QUERY_ETAG),
3672 FE(HTTP_QUERY_HOST),
3673 FE(HTTP_QUERY_IF_MATCH),
3674 FE(HTTP_QUERY_IF_NONE_MATCH),
3675 FE(HTTP_QUERY_IF_RANGE),
3676 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3677 FE(HTTP_QUERY_MAX_FORWARDS),
3678 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3679 FE(HTTP_QUERY_RANGE),
3680 FE(HTTP_QUERY_TRANSFER_ENCODING),
3681 FE(HTTP_QUERY_UPGRADE),
3682 FE(HTTP_QUERY_VARY),
3683 FE(HTTP_QUERY_VIA),
3684 FE(HTTP_QUERY_WARNING),
3685 FE(HTTP_QUERY_CUSTOM)
3687 static const wininet_flag_info modifier_flags[] = {
3688 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3689 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3690 FE(HTTP_QUERY_FLAG_NUMBER),
3691 FE(HTTP_QUERY_FLAG_COALESCE)
3693 #undef FE
3694 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3695 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3696 DWORD i;
3698 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3699 TRACE(" Attribute:");
3700 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3701 if (query_flags[i].val == info) {
3702 TRACE(" %s", query_flags[i].name);
3703 break;
3706 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3707 TRACE(" Unknown (%08x)", info);
3710 TRACE(" Modifier:");
3711 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3712 if (modifier_flags[i].val & info_mod) {
3713 TRACE(" %s", modifier_flags[i].name);
3714 info_mod &= ~ modifier_flags[i].val;
3718 if (info_mod) {
3719 TRACE(" Unknown (%08x)", info_mod);
3721 TRACE("\n");
3724 request = (http_request_t*) get_handle_object( hHttpRequest );
3725 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3727 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3728 goto lend;
3731 if (lpBuffer == NULL)
3732 *lpdwBufferLength = 0;
3733 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3734 lpBuffer, lpdwBufferLength, lpdwIndex);
3736 lend:
3737 if( request )
3738 WININET_Release( &request->hdr );
3740 TRACE("%u <--\n", res);
3741 if(res != ERROR_SUCCESS)
3742 SetLastError(res);
3743 return res == ERROR_SUCCESS;
3746 /***********************************************************************
3747 * HttpQueryInfoA (WININET.@)
3749 * Queries for information about an HTTP request
3751 * RETURNS
3752 * TRUE on success
3753 * FALSE on failure
3756 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3757 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3759 BOOL result;
3760 DWORD len;
3761 WCHAR* bufferW;
3763 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3764 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3766 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3767 lpdwBufferLength, lpdwIndex );
3770 if (lpBuffer)
3772 DWORD alloclen;
3773 len = (*lpdwBufferLength)*sizeof(WCHAR);
3774 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3776 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3777 if (alloclen < len)
3778 alloclen = len;
3780 else
3781 alloclen = len;
3782 bufferW = heap_alloc(alloclen);
3783 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3784 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3785 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3786 } else
3788 bufferW = NULL;
3789 len = 0;
3792 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3793 &len, lpdwIndex );
3794 if( result )
3796 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3797 lpBuffer, *lpdwBufferLength, NULL, NULL );
3798 *lpdwBufferLength = len - 1;
3800 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3802 else
3803 /* since the strings being returned from HttpQueryInfoW should be
3804 * only ASCII characters, it is reasonable to assume that all of
3805 * the Unicode characters can be reduced to a single byte */
3806 *lpdwBufferLength = len / sizeof(WCHAR);
3808 heap_free( bufferW );
3809 return result;
3812 /***********************************************************************
3813 * HTTP_GetRedirectURL (internal)
3815 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3817 static WCHAR szHttp[] = {'h','t','t','p',0};
3818 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3819 http_session_t *session = request->session;
3820 URL_COMPONENTSW urlComponents;
3821 DWORD url_length = 0;
3822 LPWSTR orig_url;
3823 LPWSTR combined_url;
3825 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3826 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3827 urlComponents.dwSchemeLength = 0;
3828 urlComponents.lpszHostName = session->hostName;
3829 urlComponents.dwHostNameLength = 0;
3830 urlComponents.nPort = session->hostPort;
3831 urlComponents.lpszUserName = session->userName;
3832 urlComponents.dwUserNameLength = 0;
3833 urlComponents.lpszPassword = NULL;
3834 urlComponents.dwPasswordLength = 0;
3835 urlComponents.lpszUrlPath = request->path;
3836 urlComponents.dwUrlPathLength = 0;
3837 urlComponents.lpszExtraInfo = NULL;
3838 urlComponents.dwExtraInfoLength = 0;
3840 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3841 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3842 return NULL;
3844 orig_url = heap_alloc(url_length);
3846 /* convert from bytes to characters */
3847 url_length = url_length / sizeof(WCHAR) - 1;
3848 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3850 heap_free(orig_url);
3851 return NULL;
3854 url_length = 0;
3855 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3856 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3858 heap_free(orig_url);
3859 return NULL;
3861 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3863 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3865 heap_free(orig_url);
3866 heap_free(combined_url);
3867 return NULL;
3869 heap_free(orig_url);
3870 return combined_url;
3874 /***********************************************************************
3875 * HTTP_HandleRedirect (internal)
3877 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3879 http_session_t *session = request->session;
3880 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3881 int index;
3883 if(lpszUrl[0]=='/')
3885 /* if it's an absolute path, keep the same session info */
3886 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3888 else
3890 URL_COMPONENTSW urlComponents;
3891 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3892 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3893 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3894 BOOL custom_port = FALSE;
3896 static WCHAR httpW[] = {'h','t','t','p',0};
3897 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3899 userName[0] = 0;
3900 hostName[0] = 0;
3901 protocol[0] = 0;
3903 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3904 urlComponents.lpszScheme = protocol;
3905 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3906 urlComponents.lpszHostName = hostName;
3907 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3908 urlComponents.lpszUserName = userName;
3909 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3910 urlComponents.lpszPassword = NULL;
3911 urlComponents.dwPasswordLength = 0;
3912 urlComponents.lpszUrlPath = path;
3913 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3914 urlComponents.lpszExtraInfo = NULL;
3915 urlComponents.dwExtraInfoLength = 0;
3916 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3917 return INTERNET_GetLastError();
3919 if(!strcmpiW(protocol, httpW)) {
3920 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3921 TRACE("redirect from secure page to non-secure page\n");
3922 /* FIXME: warn about from secure redirect to non-secure page */
3923 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3926 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3927 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3928 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3929 custom_port = TRUE;
3930 }else if(!strcmpiW(protocol, httpsW)) {
3931 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3932 TRACE("redirect from non-secure page to secure page\n");
3933 /* FIXME: notify about redirect to secure page */
3934 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3937 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3938 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3939 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3940 custom_port = TRUE;
3943 heap_free(session->hostName);
3945 if(custom_port) {
3946 int len;
3947 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3948 len = lstrlenW(hostName);
3949 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3950 session->hostName = heap_alloc(len*sizeof(WCHAR));
3951 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3953 else
3954 session->hostName = heap_strdupW(hostName);
3956 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3958 heap_free(session->userName);
3959 session->userName = NULL;
3960 if (userName[0])
3961 session->userName = heap_strdupW(userName);
3963 reset_data_stream(request);
3965 if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
3966 server_t *new_server;
3968 new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
3969 server_release(request->server);
3970 request->server = new_server;
3973 heap_free(request->path);
3974 request->path=NULL;
3975 if (*path)
3977 DWORD needed = 0;
3978 HRESULT rc;
3980 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3981 if (rc != E_POINTER)
3982 needed = strlenW(path)+1;
3983 request->path = heap_alloc(needed*sizeof(WCHAR));
3984 rc = UrlEscapeW(path, request->path, &needed,
3985 URL_ESCAPE_SPACES_ONLY);
3986 if (rc != S_OK)
3988 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3989 strcpyW(request->path,path);
3993 /* Remove custom content-type/length headers on redirects. */
3994 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3995 if (0 <= index)
3996 HTTP_DeleteCustomHeader(request, index);
3997 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3998 if (0 <= index)
3999 HTTP_DeleteCustomHeader(request, index);
4001 return ERROR_SUCCESS;
4004 /***********************************************************************
4005 * HTTP_build_req (internal)
4007 * concatenate all the strings in the request together
4009 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
4011 LPCWSTR *t;
4012 LPWSTR str;
4014 for( t = list; *t ; t++ )
4015 len += strlenW( *t );
4016 len++;
4018 str = heap_alloc(len*sizeof(WCHAR));
4019 *str = 0;
4021 for( t = list; *t ; t++ )
4022 strcatW( str, *t );
4024 return str;
4027 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
4029 server_t *server = request->server;
4030 LPWSTR requestString;
4031 INT len;
4032 INT cnt;
4033 INT responseLen;
4034 char *ascii_req;
4035 DWORD res;
4037 static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
4039 TRACE("\n");
4041 requestString = HTTP_BuildHeaderRequestString( request, connectW, server->host_port, g_szHttp1_1 );
4043 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4044 NULL, 0, NULL, NULL );
4045 len--; /* the nul terminator isn't needed */
4046 ascii_req = heap_alloc(len);
4047 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4048 heap_free( requestString );
4050 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4052 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4053 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4054 heap_free( ascii_req );
4055 if (res != ERROR_SUCCESS)
4056 return res;
4058 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4059 if (!responseLen)
4060 return ERROR_HTTP_INVALID_HEADER;
4062 return ERROR_SUCCESS;
4065 static void HTTP_InsertCookies(http_request_t *request)
4067 DWORD cookie_size, size, cnt = 0;
4068 HTTPHEADERW *host;
4069 WCHAR *cookies;
4071 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4073 host = HTTP_GetHeader(request, hostW);
4074 if(!host)
4075 return;
4077 if(get_cookie(host->lpszValue, request->path, NULL, &cookie_size) != ERROR_SUCCESS)
4078 return;
4080 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4081 if(!(cookies = heap_alloc(size)))
4082 return;
4084 cnt += sprintfW(cookies, cookieW);
4085 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4086 strcatW(cookies, szCrLf);
4088 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4090 heap_free(cookies);
4093 static WORD HTTP_ParseWkday(LPCWSTR day)
4095 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4096 { 'm','o','n',0 },
4097 { 't','u','e',0 },
4098 { 'w','e','d',0 },
4099 { 't','h','u',0 },
4100 { 'f','r','i',0 },
4101 { 's','a','t',0 }};
4102 unsigned int i;
4103 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4104 if (!strcmpiW(day, days[i]))
4105 return i;
4107 /* Invalid */
4108 return 7;
4111 static WORD HTTP_ParseMonth(LPCWSTR month)
4113 static const WCHAR jan[] = { 'j','a','n',0 };
4114 static const WCHAR feb[] = { 'f','e','b',0 };
4115 static const WCHAR mar[] = { 'm','a','r',0 };
4116 static const WCHAR apr[] = { 'a','p','r',0 };
4117 static const WCHAR may[] = { 'm','a','y',0 };
4118 static const WCHAR jun[] = { 'j','u','n',0 };
4119 static const WCHAR jul[] = { 'j','u','l',0 };
4120 static const WCHAR aug[] = { 'a','u','g',0 };
4121 static const WCHAR sep[] = { 's','e','p',0 };
4122 static const WCHAR oct[] = { 'o','c','t',0 };
4123 static const WCHAR nov[] = { 'n','o','v',0 };
4124 static const WCHAR dec[] = { 'd','e','c',0 };
4126 if (!strcmpiW(month, jan)) return 1;
4127 if (!strcmpiW(month, feb)) return 2;
4128 if (!strcmpiW(month, mar)) return 3;
4129 if (!strcmpiW(month, apr)) return 4;
4130 if (!strcmpiW(month, may)) return 5;
4131 if (!strcmpiW(month, jun)) return 6;
4132 if (!strcmpiW(month, jul)) return 7;
4133 if (!strcmpiW(month, aug)) return 8;
4134 if (!strcmpiW(month, sep)) return 9;
4135 if (!strcmpiW(month, oct)) return 10;
4136 if (!strcmpiW(month, nov)) return 11;
4137 if (!strcmpiW(month, dec)) return 12;
4138 /* Invalid */
4139 return 0;
4142 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4143 * optionally preceded by whitespace.
4144 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4145 * st, and sets *str to the first character after the time format.
4147 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4149 LPCWSTR ptr = *str;
4150 WCHAR *nextPtr;
4151 unsigned long num;
4153 while (isspaceW(*ptr))
4154 ptr++;
4156 num = strtoulW(ptr, &nextPtr, 10);
4157 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4159 ERR("unexpected time format %s\n", debugstr_w(ptr));
4160 return FALSE;
4162 if (num > 23)
4164 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4165 return FALSE;
4167 ptr = nextPtr + 1;
4168 st->wHour = (WORD)num;
4169 num = strtoulW(ptr, &nextPtr, 10);
4170 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4172 ERR("unexpected time format %s\n", debugstr_w(ptr));
4173 return FALSE;
4175 if (num > 59)
4177 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4178 return FALSE;
4180 ptr = nextPtr + 1;
4181 st->wMinute = (WORD)num;
4182 num = strtoulW(ptr, &nextPtr, 10);
4183 if (!nextPtr || nextPtr <= ptr)
4185 ERR("unexpected time format %s\n", debugstr_w(ptr));
4186 return FALSE;
4188 if (num > 59)
4190 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4191 return FALSE;
4193 ptr = nextPtr + 1;
4194 *str = ptr;
4195 st->wSecond = (WORD)num;
4196 return TRUE;
4199 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4201 static const WCHAR gmt[]= { 'G','M','T',0 };
4202 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4203 LPCWSTR ptr;
4204 SYSTEMTIME st = { 0 };
4205 unsigned long num;
4207 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4208 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4209 *dayPtr = *ptr;
4210 *dayPtr = 0;
4211 st.wDayOfWeek = HTTP_ParseWkday(day);
4212 if (st.wDayOfWeek >= 7)
4214 ERR("unexpected weekday %s\n", debugstr_w(day));
4215 return FALSE;
4218 while (isspaceW(*ptr))
4219 ptr++;
4221 for (monthPtr = month; !isspace(*ptr) &&
4222 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4223 monthPtr++, ptr++)
4224 *monthPtr = *ptr;
4225 *monthPtr = 0;
4226 st.wMonth = HTTP_ParseMonth(month);
4227 if (!st.wMonth || st.wMonth > 12)
4229 ERR("unexpected month %s\n", debugstr_w(month));
4230 return FALSE;
4233 while (isspaceW(*ptr))
4234 ptr++;
4236 num = strtoulW(ptr, &nextPtr, 10);
4237 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4239 ERR("unexpected day %s\n", debugstr_w(ptr));
4240 return FALSE;
4242 ptr = nextPtr;
4243 st.wDay = (WORD)num;
4245 while (isspaceW(*ptr))
4246 ptr++;
4248 if (!HTTP_ParseTime(&st, &ptr))
4249 return FALSE;
4251 while (isspaceW(*ptr))
4252 ptr++;
4254 num = strtoulW(ptr, &nextPtr, 10);
4255 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4257 ERR("unexpected year %s\n", debugstr_w(ptr));
4258 return FALSE;
4260 ptr = nextPtr;
4261 st.wYear = (WORD)num;
4263 while (isspaceW(*ptr))
4264 ptr++;
4266 /* asctime() doesn't report a timezone, but some web servers do, so accept
4267 * with or without GMT.
4269 if (*ptr && strcmpW(ptr, gmt))
4271 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4272 return FALSE;
4274 return SystemTimeToFileTime(&st, ft);
4277 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4279 static const WCHAR gmt[]= { 'G','M','T',0 };
4280 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4281 LPCWSTR ptr;
4282 unsigned long num;
4283 SYSTEMTIME st = { 0 };
4285 ptr = strchrW(value, ',');
4286 if (!ptr)
4287 return FALSE;
4288 if (ptr - value != 3)
4290 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4291 return FALSE;
4293 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4294 day[3] = 0;
4295 st.wDayOfWeek = HTTP_ParseWkday(day);
4296 if (st.wDayOfWeek > 6)
4298 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4299 return FALSE;
4301 ptr++;
4303 while (isspaceW(*ptr))
4304 ptr++;
4306 num = strtoulW(ptr, &nextPtr, 10);
4307 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4309 WARN("unexpected day %s\n", debugstr_w(value));
4310 return FALSE;
4312 ptr = nextPtr;
4313 st.wDay = (WORD)num;
4315 while (isspaceW(*ptr))
4316 ptr++;
4318 for (monthPtr = month; !isspace(*ptr) &&
4319 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4320 monthPtr++, ptr++)
4321 *monthPtr = *ptr;
4322 *monthPtr = 0;
4323 st.wMonth = HTTP_ParseMonth(month);
4324 if (!st.wMonth || st.wMonth > 12)
4326 WARN("unexpected month %s\n", debugstr_w(month));
4327 return FALSE;
4330 while (isspaceW(*ptr))
4331 ptr++;
4333 num = strtoulW(ptr, &nextPtr, 10);
4334 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4336 ERR("unexpected year %s\n", debugstr_w(value));
4337 return FALSE;
4339 ptr = nextPtr;
4340 st.wYear = (WORD)num;
4342 if (!HTTP_ParseTime(&st, &ptr))
4343 return FALSE;
4345 while (isspaceW(*ptr))
4346 ptr++;
4348 if (strcmpW(ptr, gmt))
4350 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4351 return FALSE;
4353 return SystemTimeToFileTime(&st, ft);
4356 static WORD HTTP_ParseWeekday(LPCWSTR day)
4358 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4359 { 'm','o','n','d','a','y',0 },
4360 { 't','u','e','s','d','a','y',0 },
4361 { 'w','e','d','n','e','s','d','a','y',0 },
4362 { 't','h','u','r','s','d','a','y',0 },
4363 { 'f','r','i','d','a','y',0 },
4364 { 's','a','t','u','r','d','a','y',0 }};
4365 unsigned int i;
4366 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4367 if (!strcmpiW(day, days[i]))
4368 return i;
4370 /* Invalid */
4371 return 7;
4374 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4376 static const WCHAR gmt[]= { 'G','M','T',0 };
4377 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4378 LPCWSTR ptr;
4379 unsigned long num;
4380 SYSTEMTIME st = { 0 };
4382 ptr = strchrW(value, ',');
4383 if (!ptr)
4384 return FALSE;
4385 if (ptr - value == 3)
4387 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4388 day[3] = 0;
4389 st.wDayOfWeek = HTTP_ParseWkday(day);
4390 if (st.wDayOfWeek > 6)
4392 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4393 return FALSE;
4396 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4398 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4399 day[ptr - value + 1] = 0;
4400 st.wDayOfWeek = HTTP_ParseWeekday(day);
4401 if (st.wDayOfWeek > 6)
4403 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4404 return FALSE;
4407 else
4409 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4410 return FALSE;
4412 ptr++;
4414 while (isspaceW(*ptr))
4415 ptr++;
4417 num = strtoulW(ptr, &nextPtr, 10);
4418 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4420 ERR("unexpected day %s\n", debugstr_w(value));
4421 return FALSE;
4423 ptr = nextPtr;
4424 st.wDay = (WORD)num;
4426 if (*ptr != '-')
4428 ERR("unexpected month format %s\n", debugstr_w(ptr));
4429 return FALSE;
4431 ptr++;
4433 for (monthPtr = month; *ptr != '-' &&
4434 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4435 monthPtr++, ptr++)
4436 *monthPtr = *ptr;
4437 *monthPtr = 0;
4438 st.wMonth = HTTP_ParseMonth(month);
4439 if (!st.wMonth || st.wMonth > 12)
4441 ERR("unexpected month %s\n", debugstr_w(month));
4442 return FALSE;
4445 if (*ptr != '-')
4447 ERR("unexpected year format %s\n", debugstr_w(ptr));
4448 return FALSE;
4450 ptr++;
4452 num = strtoulW(ptr, &nextPtr, 10);
4453 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4455 ERR("unexpected year %s\n", debugstr_w(value));
4456 return FALSE;
4458 ptr = nextPtr;
4459 st.wYear = (WORD)num;
4461 if (!HTTP_ParseTime(&st, &ptr))
4462 return FALSE;
4464 while (isspaceW(*ptr))
4465 ptr++;
4467 if (strcmpW(ptr, gmt))
4469 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4470 return FALSE;
4472 return SystemTimeToFileTime(&st, ft);
4475 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4477 static const WCHAR zero[] = { '0',0 };
4478 BOOL ret;
4480 if (!strcmpW(value, zero))
4482 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4483 ret = TRUE;
4485 else if (strchrW(value, ','))
4487 ret = HTTP_ParseRfc1123Date(value, ft);
4488 if (!ret)
4490 ret = HTTP_ParseRfc850Date(value, ft);
4491 if (!ret)
4492 ERR("unexpected date format %s\n", debugstr_w(value));
4495 else
4497 ret = HTTP_ParseDateAsAsctime(value, ft);
4498 if (!ret)
4499 ERR("unexpected date format %s\n", debugstr_w(value));
4501 return ret;
4504 static void HTTP_ProcessExpires(http_request_t *request)
4506 BOOL expirationFound = FALSE;
4507 int headerIndex;
4509 /* Look for a Cache-Control header with a max-age directive, as it takes
4510 * precedence over the Expires header.
4512 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4513 if (headerIndex != -1)
4515 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4516 LPWSTR ptr;
4518 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4520 LPWSTR comma = strchrW(ptr, ','), end, equal;
4522 if (comma)
4523 end = comma;
4524 else
4525 end = ptr + strlenW(ptr);
4526 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4528 if (*equal == '=')
4530 static const WCHAR max_age[] = {
4531 'm','a','x','-','a','g','e',0 };
4533 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4535 LPWSTR nextPtr;
4536 unsigned long age;
4538 age = strtoulW(equal + 1, &nextPtr, 10);
4539 if (nextPtr > equal + 1)
4541 LARGE_INTEGER ft;
4543 NtQuerySystemTime( &ft );
4544 /* Age is in seconds, FILETIME resolution is in
4545 * 100 nanosecond intervals.
4547 ft.QuadPart += age * (ULONGLONG)1000000;
4548 request->expires.dwLowDateTime = ft.u.LowPart;
4549 request->expires.dwHighDateTime = ft.u.HighPart;
4550 expirationFound = TRUE;
4554 if (comma)
4556 ptr = comma + 1;
4557 while (isspaceW(*ptr))
4558 ptr++;
4560 else
4561 ptr = NULL;
4564 if (!expirationFound)
4566 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4567 if (headerIndex != -1)
4569 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4570 FILETIME ft;
4572 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4574 expirationFound = TRUE;
4575 request->expires = ft;
4579 if (!expirationFound)
4581 LARGE_INTEGER t;
4583 /* With no known age, default to 10 minutes until expiration. */
4584 NtQuerySystemTime( &t );
4585 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4586 request->expires.dwLowDateTime = t.u.LowPart;
4587 request->expires.dwHighDateTime = t.u.HighPart;
4591 static void HTTP_ProcessLastModified(http_request_t *request)
4593 int headerIndex;
4595 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4596 if (headerIndex != -1)
4598 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4599 FILETIME ft;
4601 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4602 request->last_modified = ft;
4606 static void http_process_keep_alive(http_request_t *req)
4608 int index;
4610 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4611 if(index != -1)
4612 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4613 else
4614 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4617 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4619 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4620 netconn_t *netconn = NULL;
4621 DWORD res;
4623 assert(!request->netconn);
4624 reset_data_stream(request);
4626 res = HTTP_ResolveName(request);
4627 if(res != ERROR_SUCCESS)
4628 return res;
4630 EnterCriticalSection(&connection_pool_cs);
4632 while(!list_empty(&request->server->conn_pool)) {
4633 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4634 list_remove(&netconn->pool_entry);
4636 if(NETCON_is_alive(netconn))
4637 break;
4639 TRACE("connection %p closed during idle\n", netconn);
4640 free_netconn(netconn);
4641 netconn = NULL;
4644 LeaveCriticalSection(&connection_pool_cs);
4646 if(netconn) {
4647 TRACE("<-- reusing %p netconn\n", netconn);
4648 request->netconn = netconn;
4649 *reusing = TRUE;
4650 return ERROR_SUCCESS;
4653 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4654 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4656 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4657 INTERNET_STATUS_CONNECTING_TO_SERVER,
4658 request->server->addr_str,
4659 strlen(request->server->addr_str)+1);
4661 res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4662 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4663 request->connect_timeout, &netconn);
4664 if(res != ERROR_SUCCESS) {
4665 ERR("create_netconn failed: %u\n", res);
4666 return res;
4669 request->netconn = netconn;
4671 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4672 INTERNET_STATUS_CONNECTED_TO_SERVER,
4673 request->server->addr_str, strlen(request->server->addr_str)+1);
4675 if(is_https) {
4676 /* Note: we differ from Microsoft's WinINet here. they seem to have
4677 * a bug that causes no status callbacks to be sent when starting
4678 * a tunnel to a proxy server using the CONNECT verb. i believe our
4679 * behaviour to be more correct and to not cause any incompatibilities
4680 * because using a secure connection through a proxy server is a rare
4681 * case that would be hard for anyone to depend on */
4682 if(request->proxy)
4683 res = HTTP_SecureProxyConnect(request);
4684 if(res == ERROR_SUCCESS)
4685 res = NETCON_secure_connect(request->netconn, request->server);
4688 if(res != ERROR_SUCCESS) {
4689 http_release_netconn(request, FALSE);
4690 return res;
4693 *reusing = FALSE;
4694 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4695 return ERROR_SUCCESS;
4698 /***********************************************************************
4699 * HTTP_HttpSendRequestW (internal)
4701 * Sends the specified request to the HTTP server
4703 * RETURNS
4704 * ERROR_SUCCESS on success
4705 * win32 error code on failure
4708 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4709 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4710 DWORD dwContentLength, BOOL bEndRequest)
4712 INT cnt;
4713 BOOL redirected = FALSE;
4714 LPWSTR requestString = NULL;
4715 INT responseLen;
4716 BOOL loop_next;
4717 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4718 static const WCHAR szContentLength[] =
4719 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4720 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4721 DWORD res;
4723 TRACE("--> %p\n", request);
4725 assert(request->hdr.htype == WH_HHTTPREQ);
4727 /* if the verb is NULL default to GET */
4728 if (!request->verb)
4729 request->verb = heap_strdupW(szGET);
4731 if (dwContentLength || strcmpW(request->verb, szGET))
4733 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4734 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4735 request->bytesToWrite = dwContentLength;
4737 if (request->session->appInfo->agent)
4739 WCHAR *agent_header;
4740 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4741 int len;
4743 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4744 agent_header = heap_alloc(len * sizeof(WCHAR));
4745 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4747 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4748 heap_free(agent_header);
4750 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4752 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4753 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4755 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4757 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4758 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4759 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4762 /* add the headers the caller supplied */
4763 if( lpszHeaders && dwHeaderLength )
4764 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4768 DWORD len;
4769 BOOL reusing_connection;
4770 char *ascii_req;
4772 loop_next = FALSE;
4773 reusing_connection = request->netconn != NULL;
4775 if(redirected) {
4776 request->contentLength = ~0u;
4777 request->bytesToWrite = 0;
4780 if (TRACE_ON(wininet))
4782 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4783 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4786 HTTP_FixURL(request);
4787 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4789 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4791 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4792 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4794 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4795 HTTP_InsertCookies(request);
4797 if (request->proxy)
4799 WCHAR *url = build_proxy_path_url(request);
4800 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4801 heap_free(url);
4803 else
4804 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4807 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4809 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4810 break;
4812 /* send the request as ASCII, tack on the optional data */
4813 if (!lpOptional || redirected)
4814 dwOptionalLength = 0;
4815 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4816 NULL, 0, NULL, NULL );
4817 ascii_req = heap_alloc(len + dwOptionalLength);
4818 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4819 ascii_req, len, NULL, NULL );
4820 if( lpOptional )
4821 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4822 len = (len + dwOptionalLength - 1);
4823 ascii_req[len] = 0;
4824 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4826 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4827 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4829 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4830 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4831 heap_free( ascii_req );
4832 if(res != ERROR_SUCCESS) {
4833 TRACE("send failed: %u\n", res);
4834 if(!reusing_connection)
4835 break;
4836 http_release_netconn(request, FALSE);
4837 loop_next = TRUE;
4838 continue;
4841 request->bytesWritten = dwOptionalLength;
4843 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4844 INTERNET_STATUS_REQUEST_SENT,
4845 &len, sizeof(DWORD));
4847 if (bEndRequest)
4849 DWORD dwBufferSize;
4851 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4852 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4854 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4855 /* FIXME: We should know that connection is closed before sending
4856 * headers. Otherwise wrong callbacks are executed */
4857 if(!responseLen && reusing_connection) {
4858 TRACE("Connection closed by server, reconnecting\n");
4859 http_release_netconn(request, FALSE);
4860 loop_next = TRUE;
4861 continue;
4864 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4865 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4866 sizeof(DWORD));
4868 http_process_keep_alive(request);
4869 HTTP_ProcessCookies(request);
4870 HTTP_ProcessExpires(request);
4871 HTTP_ProcessLastModified(request);
4873 res = set_content_length(request);
4874 if(res != ERROR_SUCCESS)
4875 goto lend;
4876 if(!request->contentLength)
4877 http_release_netconn(request, TRUE);
4879 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4881 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4882 dwBufferSize=sizeof(szNewLocation);
4883 switch(request->status_code) {
4884 case HTTP_STATUS_REDIRECT:
4885 case HTTP_STATUS_MOVED:
4886 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4887 case HTTP_STATUS_REDIRECT_METHOD:
4888 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4889 break;
4891 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4892 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4894 heap_free(request->verb);
4895 request->verb = heap_strdupW(szGET);
4897 http_release_netconn(request, drain_content(request, FALSE));
4898 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4900 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4901 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4902 res = HTTP_HandleRedirect(request, new_url);
4903 if (res == ERROR_SUCCESS)
4905 heap_free(requestString);
4906 loop_next = TRUE;
4908 heap_free( new_url );
4910 redirected = TRUE;
4913 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4915 WCHAR szAuthValue[2048];
4916 dwBufferSize=2048;
4917 if (request->status_code == HTTP_STATUS_DENIED)
4919 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4920 DWORD dwIndex = 0;
4921 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4923 if (HTTP_DoAuthorization(request, szAuthValue,
4924 &request->authInfo,
4925 request->session->userName,
4926 request->session->password,
4927 Host->lpszValue))
4929 heap_free(requestString);
4930 if(!drain_content(request, TRUE)) {
4931 FIXME("Could not drain content\n");
4932 http_release_netconn(request, FALSE);
4934 loop_next = TRUE;
4935 break;
4939 if(!loop_next) {
4940 TRACE("Cleaning wrong authorization data\n");
4941 destroy_authinfo(request->authInfo);
4942 request->authInfo = NULL;
4945 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4947 DWORD dwIndex = 0;
4948 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4950 if (HTTP_DoAuthorization(request, szAuthValue,
4951 &request->proxyAuthInfo,
4952 request->session->appInfo->proxyUsername,
4953 request->session->appInfo->proxyPassword,
4954 NULL))
4956 heap_free(requestString);
4957 if(!drain_content(request, TRUE)) {
4958 FIXME("Could not drain content\n");
4959 http_release_netconn(request, FALSE);
4961 loop_next = TRUE;
4962 break;
4966 if(!loop_next) {
4967 TRACE("Cleaning wrong proxy authorization data\n");
4968 destroy_authinfo(request->proxyAuthInfo);
4969 request->proxyAuthInfo = NULL;
4974 else
4975 res = ERROR_SUCCESS;
4977 while (loop_next);
4979 lend:
4980 heap_free(requestString);
4982 /* TODO: send notification for P3P header */
4984 if(res == ERROR_SUCCESS)
4985 create_cache_entry(request);
4987 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4989 if (res == ERROR_SUCCESS) {
4990 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4991 HTTP_ReceiveRequestData(request, TRUE, NULL);
4992 else
4993 send_request_complete(request,
4994 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4995 }else {
4996 send_request_complete(request, 0, res);
5000 TRACE("<--\n");
5001 return res;
5004 typedef struct {
5005 task_header_t hdr;
5006 WCHAR *headers;
5007 DWORD headers_len;
5008 void *optional;
5009 DWORD optional_len;
5010 DWORD content_len;
5011 BOOL end_request;
5012 } send_request_task_t;
5014 /***********************************************************************
5016 * Helper functions for the HttpSendRequest(Ex) functions
5019 static void AsyncHttpSendRequestProc(task_header_t *hdr)
5021 send_request_task_t *task = (send_request_task_t*)hdr;
5022 http_request_t *request = (http_request_t*)task->hdr.hdr;
5024 TRACE("%p\n", request);
5026 HTTP_HttpSendRequestW(request, task->headers, task->headers_len, task->optional,
5027 task->optional_len, task->content_len, task->end_request);
5029 heap_free(task->headers);
5033 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5035 DWORD dwBufferSize;
5036 INT responseLen;
5037 DWORD res = ERROR_SUCCESS;
5039 if(!request->netconn) {
5040 WARN("Not connected\n");
5041 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5042 return ERROR_INTERNET_OPERATION_CANCELLED;
5045 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5046 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5048 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5049 if (!responseLen)
5050 res = ERROR_HTTP_HEADER_NOT_FOUND;
5052 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5053 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5055 /* process cookies here. Is this right? */
5056 http_process_keep_alive(request);
5057 HTTP_ProcessCookies(request);
5058 HTTP_ProcessExpires(request);
5059 HTTP_ProcessLastModified(request);
5061 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5062 if(!request->contentLength)
5063 http_release_netconn(request, TRUE);
5066 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5068 switch(request->status_code) {
5069 case HTTP_STATUS_REDIRECT:
5070 case HTTP_STATUS_MOVED:
5071 case HTTP_STATUS_REDIRECT_METHOD:
5072 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5073 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5074 dwBufferSize=sizeof(szNewLocation);
5075 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5076 break;
5078 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5079 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5081 heap_free(request->verb);
5082 request->verb = heap_strdupW(szGET);
5084 http_release_netconn(request, drain_content(request, FALSE));
5085 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5087 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5088 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5089 res = HTTP_HandleRedirect(request, new_url);
5090 if (res == ERROR_SUCCESS)
5091 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5092 heap_free( new_url );
5098 if(res == ERROR_SUCCESS)
5099 create_cache_entry(request);
5101 if (res == ERROR_SUCCESS && request->contentLength)
5102 HTTP_ReceiveRequestData(request, TRUE, NULL);
5103 else
5104 send_request_complete(request, res == ERROR_SUCCESS, res);
5106 return res;
5109 /***********************************************************************
5110 * HttpEndRequestA (WININET.@)
5112 * Ends an HTTP request that was started by HttpSendRequestEx
5114 * RETURNS
5115 * TRUE if successful
5116 * FALSE on failure
5119 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5120 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5122 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5124 if (lpBuffersOut)
5126 SetLastError(ERROR_INVALID_PARAMETER);
5127 return FALSE;
5130 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5133 typedef struct {
5134 task_header_t hdr;
5135 DWORD flags;
5136 DWORD context;
5137 } end_request_task_t;
5139 static void AsyncHttpEndRequestProc(task_header_t *hdr)
5141 end_request_task_t *task = (end_request_task_t*)hdr;
5142 http_request_t *req = (http_request_t*)task->hdr.hdr;
5144 TRACE("%p\n", req);
5146 HTTP_HttpEndRequestW(req, task->flags, task->context);
5149 /***********************************************************************
5150 * HttpEndRequestW (WININET.@)
5152 * Ends an HTTP request that was started by HttpSendRequestEx
5154 * RETURNS
5155 * TRUE if successful
5156 * FALSE on failure
5159 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5160 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5162 http_request_t *request;
5163 DWORD res;
5165 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5167 if (lpBuffersOut)
5169 SetLastError(ERROR_INVALID_PARAMETER);
5170 return FALSE;
5173 request = (http_request_t*) get_handle_object( hRequest );
5175 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5177 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5178 if (request)
5179 WININET_Release( &request->hdr );
5180 return FALSE;
5182 request->hdr.dwFlags |= dwFlags;
5184 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5186 end_request_task_t *task;
5188 task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task));
5189 task->flags = dwFlags;
5190 task->context = dwContext;
5192 INTERNET_AsyncCall(&task->hdr);
5193 res = ERROR_IO_PENDING;
5195 else
5196 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5198 WININET_Release( &request->hdr );
5199 TRACE("%u <--\n", res);
5200 if(res != ERROR_SUCCESS)
5201 SetLastError(res);
5202 return res == ERROR_SUCCESS;
5205 /***********************************************************************
5206 * HttpSendRequestExA (WININET.@)
5208 * Sends the specified request to the HTTP server and allows chunked
5209 * transfers.
5211 * RETURNS
5212 * Success: TRUE
5213 * Failure: FALSE, call GetLastError() for more information.
5215 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5216 LPINTERNET_BUFFERSA lpBuffersIn,
5217 LPINTERNET_BUFFERSA lpBuffersOut,
5218 DWORD dwFlags, DWORD_PTR dwContext)
5220 INTERNET_BUFFERSW BuffersInW;
5221 BOOL rc = FALSE;
5222 DWORD headerlen;
5223 LPWSTR header = NULL;
5225 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5226 lpBuffersOut, dwFlags, dwContext);
5228 if (lpBuffersIn)
5230 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5231 if (lpBuffersIn->lpcszHeader)
5233 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5234 lpBuffersIn->dwHeadersLength,0,0);
5235 header = heap_alloc(headerlen*sizeof(WCHAR));
5236 if (!(BuffersInW.lpcszHeader = header))
5238 SetLastError(ERROR_OUTOFMEMORY);
5239 return FALSE;
5241 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5242 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5243 header, headerlen);
5245 else
5246 BuffersInW.lpcszHeader = NULL;
5247 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5248 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5249 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5250 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5251 BuffersInW.Next = NULL;
5254 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5256 heap_free(header);
5257 return rc;
5260 /***********************************************************************
5261 * HttpSendRequestExW (WININET.@)
5263 * Sends the specified request to the HTTP server and allows chunked
5264 * transfers
5266 * RETURNS
5267 * Success: TRUE
5268 * Failure: FALSE, call GetLastError() for more information.
5270 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5271 LPINTERNET_BUFFERSW lpBuffersIn,
5272 LPINTERNET_BUFFERSW lpBuffersOut,
5273 DWORD dwFlags, DWORD_PTR dwContext)
5275 http_request_t *request;
5276 http_session_t *session;
5277 appinfo_t *hIC;
5278 DWORD res;
5280 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5281 lpBuffersOut, dwFlags, dwContext);
5283 request = (http_request_t*) get_handle_object( hRequest );
5285 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5287 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5288 goto lend;
5291 session = request->session;
5292 assert(session->hdr.htype == WH_HHTTPSESSION);
5293 hIC = session->appInfo;
5294 assert(hIC->hdr.htype == WH_HINIT);
5296 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5298 send_request_task_t *task;
5300 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5301 if (lpBuffersIn)
5303 DWORD size = 0;
5305 if (lpBuffersIn->lpcszHeader)
5307 if (lpBuffersIn->dwHeadersLength == ~0u)
5308 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5309 else
5310 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5312 task->headers = heap_alloc(size);
5313 memcpy(task->headers, lpBuffersIn->lpcszHeader, size);
5315 else task->headers = NULL;
5317 task->headers_len = size / sizeof(WCHAR);
5318 task->optional = lpBuffersIn->lpvBuffer;
5319 task->optional_len = lpBuffersIn->dwBufferLength;
5320 task->content_len = lpBuffersIn->dwBufferTotal;
5322 else
5324 task->headers = NULL;
5325 task->headers_len = 0;
5326 task->optional = NULL;
5327 task->optional_len = 0;
5328 task->content_len = 0;
5331 task->end_request = FALSE;
5333 INTERNET_AsyncCall(&task->hdr);
5334 res = ERROR_IO_PENDING;
5336 else
5338 if (lpBuffersIn)
5339 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5340 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5341 lpBuffersIn->dwBufferTotal, FALSE);
5342 else
5343 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5346 lend:
5347 if ( request )
5348 WININET_Release( &request->hdr );
5350 TRACE("<---\n");
5351 SetLastError(res);
5352 return res == ERROR_SUCCESS;
5355 /***********************************************************************
5356 * HttpSendRequestW (WININET.@)
5358 * Sends the specified request to the HTTP server
5360 * RETURNS
5361 * TRUE on success
5362 * FALSE on failure
5365 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5366 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5368 http_request_t *request;
5369 http_session_t *session = NULL;
5370 appinfo_t *hIC = NULL;
5371 DWORD res = ERROR_SUCCESS;
5373 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5374 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5376 request = (http_request_t*) get_handle_object( hHttpRequest );
5377 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5379 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5380 goto lend;
5383 session = request->session;
5384 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5386 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5387 goto lend;
5390 hIC = session->appInfo;
5391 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5393 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5394 goto lend;
5397 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5399 send_request_task_t *task;
5401 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5402 if (lpszHeaders)
5404 DWORD size;
5406 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5407 else size = dwHeaderLength * sizeof(WCHAR);
5409 task->headers = heap_alloc(size);
5410 memcpy(task->headers, lpszHeaders, size);
5412 else
5413 task->headers = NULL;
5414 task->headers_len = dwHeaderLength;
5415 task->optional = lpOptional;
5416 task->optional_len = dwOptionalLength;
5417 task->content_len = dwOptionalLength;
5418 task->end_request = TRUE;
5420 INTERNET_AsyncCall(&task->hdr);
5421 res = ERROR_IO_PENDING;
5423 else
5425 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5426 dwHeaderLength, lpOptional, dwOptionalLength,
5427 dwOptionalLength, TRUE);
5429 lend:
5430 if( request )
5431 WININET_Release( &request->hdr );
5433 SetLastError(res);
5434 return res == ERROR_SUCCESS;
5437 /***********************************************************************
5438 * HttpSendRequestA (WININET.@)
5440 * Sends the specified request to the HTTP server
5442 * RETURNS
5443 * TRUE on success
5444 * FALSE on failure
5447 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5448 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5450 BOOL result;
5451 LPWSTR szHeaders=NULL;
5452 DWORD nLen=dwHeaderLength;
5453 if(lpszHeaders!=NULL)
5455 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5456 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5457 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5459 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5460 heap_free(szHeaders);
5461 return result;
5464 /***********************************************************************
5465 * HTTPSESSION_Destroy (internal)
5467 * Deallocate session handle
5470 static void HTTPSESSION_Destroy(object_header_t *hdr)
5472 http_session_t *session = (http_session_t*) hdr;
5474 TRACE("%p\n", session);
5476 WININET_Release(&session->appInfo->hdr);
5478 heap_free(session->hostName);
5479 heap_free(session->password);
5480 heap_free(session->userName);
5483 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5485 http_session_t *ses = (http_session_t *)hdr;
5487 switch(option) {
5488 case INTERNET_OPTION_HANDLE_TYPE:
5489 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5491 if (*size < sizeof(ULONG))
5492 return ERROR_INSUFFICIENT_BUFFER;
5494 *size = sizeof(DWORD);
5495 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5496 return ERROR_SUCCESS;
5497 case INTERNET_OPTION_CONNECT_TIMEOUT:
5498 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5500 if (*size < sizeof(DWORD))
5501 return ERROR_INSUFFICIENT_BUFFER;
5503 *size = sizeof(DWORD);
5504 *(DWORD *)buffer = ses->connect_timeout;
5505 return ERROR_SUCCESS;
5507 case INTERNET_OPTION_SEND_TIMEOUT:
5508 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5510 if (*size < sizeof(DWORD))
5511 return ERROR_INSUFFICIENT_BUFFER;
5513 *size = sizeof(DWORD);
5514 *(DWORD *)buffer = ses->send_timeout;
5515 return ERROR_SUCCESS;
5517 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5518 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5520 if (*size < sizeof(DWORD))
5521 return ERROR_INSUFFICIENT_BUFFER;
5523 *size = sizeof(DWORD);
5524 *(DWORD *)buffer = ses->receive_timeout;
5525 return ERROR_SUCCESS;
5528 return INET_QueryOption(hdr, option, buffer, size, unicode);
5531 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5533 http_session_t *ses = (http_session_t*)hdr;
5535 switch(option) {
5536 case INTERNET_OPTION_USERNAME:
5538 heap_free(ses->userName);
5539 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5540 return ERROR_SUCCESS;
5542 case INTERNET_OPTION_PASSWORD:
5544 heap_free(ses->password);
5545 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5546 return ERROR_SUCCESS;
5548 case INTERNET_OPTION_PROXY_USERNAME:
5550 heap_free(ses->appInfo->proxyUsername);
5551 if (!(ses->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5552 return ERROR_SUCCESS;
5554 case INTERNET_OPTION_PROXY_PASSWORD:
5556 heap_free(ses->appInfo->proxyPassword);
5557 if (!(ses->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5558 return ERROR_SUCCESS;
5560 case INTERNET_OPTION_CONNECT_TIMEOUT:
5562 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5563 ses->connect_timeout = *(DWORD *)buffer;
5564 return ERROR_SUCCESS;
5566 case INTERNET_OPTION_SEND_TIMEOUT:
5568 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5569 ses->send_timeout = *(DWORD *)buffer;
5570 return ERROR_SUCCESS;
5572 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5574 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5575 ses->receive_timeout = *(DWORD *)buffer;
5576 return ERROR_SUCCESS;
5578 default: break;
5581 return INET_SetOption(hdr, option, buffer, size);
5584 static const object_vtbl_t HTTPSESSIONVtbl = {
5585 HTTPSESSION_Destroy,
5586 NULL,
5587 HTTPSESSION_QueryOption,
5588 HTTPSESSION_SetOption,
5589 NULL,
5590 NULL,
5591 NULL,
5592 NULL,
5593 NULL
5597 /***********************************************************************
5598 * HTTP_Connect (internal)
5600 * Create http session handle
5602 * RETURNS
5603 * HINTERNET a session handle on success
5604 * NULL on failure
5607 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5608 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5609 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5610 DWORD dwInternalFlags, HINTERNET *ret)
5612 http_session_t *session = NULL;
5614 TRACE("-->\n");
5616 if (!lpszServerName || !lpszServerName[0])
5617 return ERROR_INVALID_PARAMETER;
5619 assert( hIC->hdr.htype == WH_HINIT );
5621 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5622 if (!session)
5623 return ERROR_OUTOFMEMORY;
5626 * According to my tests. The name is not resolved until a request is sent
5629 session->hdr.htype = WH_HHTTPSESSION;
5630 session->hdr.dwFlags = dwFlags;
5631 session->hdr.dwContext = dwContext;
5632 session->hdr.dwInternalFlags |= dwInternalFlags;
5634 WININET_AddRef( &hIC->hdr );
5635 session->appInfo = hIC;
5636 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5638 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5639 if(hIC->proxyBypass)
5640 FIXME("Proxy bypass is ignored.\n");
5642 session->hostName = heap_strdupW(lpszServerName);
5643 if (lpszUserName && lpszUserName[0])
5644 session->userName = heap_strdupW(lpszUserName);
5645 if (lpszPassword && lpszPassword[0])
5646 session->password = heap_strdupW(lpszPassword);
5647 session->hostPort = serverPort;
5648 session->connect_timeout = hIC->connect_timeout;
5649 session->send_timeout = INFINITE;
5650 session->receive_timeout = INFINITE;
5652 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5653 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5655 INTERNET_SendCallback(&hIC->hdr, dwContext,
5656 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5657 sizeof(HINTERNET));
5661 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5662 * windows
5665 TRACE("%p --> %p\n", hIC, session);
5667 *ret = session->hdr.hInternet;
5668 return ERROR_SUCCESS;
5671 /***********************************************************************
5672 * HTTP_clear_response_headers (internal)
5674 * clear out any old response headers
5676 static void HTTP_clear_response_headers( http_request_t *request )
5678 DWORD i;
5680 for( i=0; i<request->nCustHeaders; i++)
5682 if( !request->custHeaders[i].lpszField )
5683 continue;
5684 if( !request->custHeaders[i].lpszValue )
5685 continue;
5686 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5687 continue;
5688 HTTP_DeleteCustomHeader( request, i );
5689 i--;
5693 /***********************************************************************
5694 * HTTP_GetResponseHeaders (internal)
5696 * Read server response
5698 * RETURNS
5700 * TRUE on success
5701 * FALSE on error
5703 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5705 INT cbreaks = 0;
5706 WCHAR buffer[MAX_REPLY_LEN];
5707 DWORD buflen = MAX_REPLY_LEN;
5708 BOOL bSuccess = FALSE;
5709 INT rc = 0;
5710 char bufferA[MAX_REPLY_LEN];
5711 LPWSTR status_code = NULL, status_text = NULL;
5712 DWORD cchMaxRawHeaders = 1024;
5713 LPWSTR lpszRawHeaders = NULL;
5714 LPWSTR temp;
5715 DWORD cchRawHeaders = 0;
5716 BOOL codeHundred = FALSE;
5718 TRACE("-->\n");
5720 if(!request->netconn)
5721 goto lend;
5723 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5724 do {
5726 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5728 buflen = MAX_REPLY_LEN;
5729 if (!read_line(request, bufferA, &buflen))
5730 goto lend;
5732 /* clear old response headers (eg. from a redirect response) */
5733 if (clear) {
5734 HTTP_clear_response_headers( request );
5735 clear = FALSE;
5738 rc += buflen;
5739 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5740 /* check is this a status code line? */
5741 if (!strncmpW(buffer, g_szHttp1_0, 4))
5743 /* split the version from the status code */
5744 status_code = strchrW( buffer, ' ' );
5745 if( !status_code )
5746 goto lend;
5747 *status_code++=0;
5749 /* split the status code from the status text */
5750 status_text = strchrW( status_code, ' ' );
5751 if( !status_text )
5752 goto lend;
5753 *status_text++=0;
5755 request->status_code = atoiW(status_code);
5757 TRACE("version [%s] status code [%s] status text [%s]\n",
5758 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5760 codeHundred = request->status_code == HTTP_STATUS_CONTINUE;
5762 else if (!codeHundred)
5764 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5766 heap_free(request->version);
5767 heap_free(request->statusText);
5769 request->status_code = HTTP_STATUS_OK;
5770 request->version = heap_strdupW(g_szHttp1_0);
5771 request->statusText = heap_strdupW(szOK);
5773 heap_free(request->rawHeaders);
5774 request->rawHeaders = heap_strdupW(szDefaultHeader);
5776 bSuccess = TRUE;
5777 goto lend;
5779 } while (codeHundred);
5781 /* Add status code */
5782 HTTP_ProcessHeader(request, szStatus, status_code,
5783 HTTP_ADDHDR_FLAG_REPLACE);
5785 heap_free(request->version);
5786 heap_free(request->statusText);
5788 request->version = heap_strdupW(buffer);
5789 request->statusText = heap_strdupW(status_text);
5791 /* Restore the spaces */
5792 *(status_code-1) = ' ';
5793 *(status_text-1) = ' ';
5795 /* regenerate raw headers */
5796 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5797 if (!lpszRawHeaders) goto lend;
5799 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5800 cchMaxRawHeaders *= 2;
5801 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5802 if (temp == NULL) goto lend;
5803 lpszRawHeaders = temp;
5804 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5805 cchRawHeaders += (buflen-1);
5806 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5807 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5808 lpszRawHeaders[cchRawHeaders] = '\0';
5810 /* Parse each response line */
5813 buflen = MAX_REPLY_LEN;
5814 if (read_line(request, bufferA, &buflen))
5816 LPWSTR * pFieldAndValue;
5818 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5820 if (!bufferA[0]) break;
5821 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5823 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5824 if (pFieldAndValue)
5826 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5827 cchMaxRawHeaders *= 2;
5828 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5829 if (temp == NULL) goto lend;
5830 lpszRawHeaders = temp;
5831 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5832 cchRawHeaders += (buflen-1);
5833 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5834 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5835 lpszRawHeaders[cchRawHeaders] = '\0';
5837 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5838 HTTP_ADDREQ_FLAG_ADD );
5840 HTTP_FreeTokens(pFieldAndValue);
5843 else
5845 cbreaks++;
5846 if (cbreaks >= 2)
5847 break;
5849 }while(1);
5851 /* make sure the response header is terminated with an empty line. Some apps really
5852 truly care about that empty line being there for some reason. Just add it to the
5853 header. */
5854 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5856 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5857 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5858 if (temp == NULL) goto lend;
5859 lpszRawHeaders = temp;
5862 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5864 heap_free(request->rawHeaders);
5865 request->rawHeaders = lpszRawHeaders;
5866 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5867 bSuccess = TRUE;
5869 lend:
5871 TRACE("<--\n");
5872 if (bSuccess)
5873 return rc;
5874 else
5876 heap_free(lpszRawHeaders);
5877 return 0;
5881 /***********************************************************************
5882 * HTTP_InterpretHttpHeader (internal)
5884 * Parse server response
5886 * RETURNS
5888 * Pointer to array of field, value, NULL on success.
5889 * NULL on error.
5891 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5893 LPWSTR * pTokenPair;
5894 LPWSTR pszColon;
5895 INT len;
5897 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5899 pszColon = strchrW(buffer, ':');
5900 /* must have two tokens */
5901 if (!pszColon)
5903 HTTP_FreeTokens(pTokenPair);
5904 if (buffer[0])
5905 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5906 return NULL;
5909 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5910 if (!pTokenPair[0])
5912 HTTP_FreeTokens(pTokenPair);
5913 return NULL;
5915 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5916 pTokenPair[0][pszColon - buffer] = '\0';
5918 /* skip colon */
5919 pszColon++;
5920 len = strlenW(pszColon);
5921 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5922 if (!pTokenPair[1])
5924 HTTP_FreeTokens(pTokenPair);
5925 return NULL;
5927 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5929 strip_spaces(pTokenPair[0]);
5930 strip_spaces(pTokenPair[1]);
5932 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5933 return pTokenPair;
5936 /***********************************************************************
5937 * HTTP_ProcessHeader (internal)
5939 * Stuff header into header tables according to <dwModifier>
5943 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5945 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5947 LPHTTPHEADERW lphttpHdr = NULL;
5948 INT index = -1;
5949 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5950 DWORD res = ERROR_HTTP_INVALID_HEADER;
5952 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5954 /* REPLACE wins out over ADD */
5955 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5956 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5958 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5959 index = -1;
5960 else
5961 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5963 if (index >= 0)
5965 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5966 return ERROR_HTTP_INVALID_HEADER;
5967 lphttpHdr = &request->custHeaders[index];
5969 else if (value)
5971 HTTPHEADERW hdr;
5973 hdr.lpszField = (LPWSTR)field;
5974 hdr.lpszValue = (LPWSTR)value;
5975 hdr.wFlags = hdr.wCount = 0;
5977 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5978 hdr.wFlags |= HDR_ISREQUEST;
5980 return HTTP_InsertCustomHeader(request, &hdr);
5982 /* no value to delete */
5983 else return ERROR_SUCCESS;
5985 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5986 lphttpHdr->wFlags |= HDR_ISREQUEST;
5987 else
5988 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5990 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5992 HTTP_DeleteCustomHeader( request, index );
5994 if (value)
5996 HTTPHEADERW hdr;
5998 hdr.lpszField = (LPWSTR)field;
5999 hdr.lpszValue = (LPWSTR)value;
6000 hdr.wFlags = hdr.wCount = 0;
6002 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6003 hdr.wFlags |= HDR_ISREQUEST;
6005 return HTTP_InsertCustomHeader(request, &hdr);
6008 return ERROR_SUCCESS;
6010 else if (dwModifier & COALESCEFLAGS)
6012 LPWSTR lpsztmp;
6013 WCHAR ch = 0;
6014 INT len = 0;
6015 INT origlen = strlenW(lphttpHdr->lpszValue);
6016 INT valuelen = strlenW(value);
6018 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6020 ch = ',';
6021 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6023 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6025 ch = ';';
6026 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6029 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6031 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6032 if (lpsztmp)
6034 lphttpHdr->lpszValue = lpsztmp;
6035 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6036 if (ch > 0)
6038 lphttpHdr->lpszValue[origlen] = ch;
6039 origlen++;
6040 lphttpHdr->lpszValue[origlen] = ' ';
6041 origlen++;
6044 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6045 lphttpHdr->lpszValue[len] = '\0';
6046 res = ERROR_SUCCESS;
6048 else
6050 WARN("heap_realloc (%d bytes) failed\n",len+1);
6051 res = ERROR_OUTOFMEMORY;
6054 TRACE("<-- %d\n", res);
6055 return res;
6058 /***********************************************************************
6059 * HTTP_GetCustomHeaderIndex (internal)
6061 * Return index of custom header from header array
6064 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6065 int requested_index, BOOL request_only)
6067 DWORD index;
6069 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6071 for (index = 0; index < request->nCustHeaders; index++)
6073 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6074 continue;
6076 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6077 continue;
6079 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6080 continue;
6082 if (requested_index == 0)
6083 break;
6084 requested_index --;
6087 if (index >= request->nCustHeaders)
6088 index = -1;
6090 TRACE("Return: %d\n", index);
6091 return index;
6095 /***********************************************************************
6096 * HTTP_InsertCustomHeader (internal)
6098 * Insert header into array
6101 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6103 INT count;
6104 LPHTTPHEADERW lph = NULL;
6106 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6107 count = request->nCustHeaders + 1;
6108 if (count > 1)
6109 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6110 else
6111 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6113 if (!lph)
6114 return ERROR_OUTOFMEMORY;
6116 request->custHeaders = lph;
6117 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6118 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6119 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6120 request->custHeaders[count-1].wCount= lpHdr->wCount;
6121 request->nCustHeaders++;
6123 return ERROR_SUCCESS;
6127 /***********************************************************************
6128 * HTTP_DeleteCustomHeader (internal)
6130 * Delete header from array
6131 * If this function is called, the indexs may change.
6133 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6135 if( request->nCustHeaders <= 0 )
6136 return FALSE;
6137 if( index >= request->nCustHeaders )
6138 return FALSE;
6139 request->nCustHeaders--;
6141 heap_free(request->custHeaders[index].lpszField);
6142 heap_free(request->custHeaders[index].lpszValue);
6144 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6145 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6146 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6148 return TRUE;
6152 /***********************************************************************
6153 * HTTP_VerifyValidHeader (internal)
6155 * Verify the given header is not invalid for the given http request
6158 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6160 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6161 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6162 return ERROR_HTTP_INVALID_HEADER;
6164 return ERROR_SUCCESS;
6167 /***********************************************************************
6168 * IsHostInProxyBypassList (@)
6170 * Undocumented
6173 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6175 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6176 return FALSE;