wininet: Fix a request string leak.
[wine.git] / dlls / wininet / http.c
blobc28335a8b4d847bbdafa09877118259833efb7ca
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 HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1952 http_request_t *req = (http_request_t*)hdr;
1954 switch(option) {
1955 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1957 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1959 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1961 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1962 return ERROR_INSUFFICIENT_BUFFER;
1963 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1964 /* FIXME: can't get a SOCKET from our connection since we don't use
1965 * winsock
1967 info->Socket = 0;
1968 /* FIXME: get source port from req->netConnection */
1969 info->SourcePort = 0;
1970 info->DestPort = req->server->port;
1971 info->Flags = 0;
1972 if (HTTP_KeepAlive(req))
1973 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1974 if (req->proxy)
1975 info->Flags |= IDSI_FLAG_PROXY;
1976 if (req->netconn->secure)
1977 info->Flags |= IDSI_FLAG_SECURE;
1979 return ERROR_SUCCESS;
1982 case 98:
1983 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1984 /* fall through */
1985 case INTERNET_OPTION_SECURITY_FLAGS:
1987 DWORD flags;
1989 if (*size < sizeof(ULONG))
1990 return ERROR_INSUFFICIENT_BUFFER;
1992 *size = sizeof(DWORD);
1993 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
1994 *(DWORD *)buffer = flags;
1996 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
1997 return ERROR_SUCCESS;
2000 case INTERNET_OPTION_HANDLE_TYPE:
2001 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2003 if (*size < sizeof(ULONG))
2004 return ERROR_INSUFFICIENT_BUFFER;
2006 *size = sizeof(DWORD);
2007 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2008 return ERROR_SUCCESS;
2010 case INTERNET_OPTION_URL: {
2011 WCHAR url[INTERNET_MAX_URL_LENGTH];
2012 HTTPHEADERW *host;
2013 DWORD len;
2014 WCHAR *pch;
2016 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2018 TRACE("INTERNET_OPTION_URL\n");
2020 host = HTTP_GetHeader(req, hostW);
2021 strcpyW(url, httpW);
2022 strcatW(url, host->lpszValue);
2023 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2024 *pch = 0;
2025 strcatW(url, req->path);
2027 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2029 if(unicode) {
2030 len = (strlenW(url)+1) * sizeof(WCHAR);
2031 if(*size < len)
2032 return ERROR_INSUFFICIENT_BUFFER;
2034 *size = len;
2035 strcpyW(buffer, url);
2036 return ERROR_SUCCESS;
2037 }else {
2038 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2039 if(len > *size)
2040 return ERROR_INSUFFICIENT_BUFFER;
2042 *size = len;
2043 return ERROR_SUCCESS;
2047 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2048 INTERNET_CACHE_ENTRY_INFOW *info;
2049 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2050 WCHAR url[INTERNET_MAX_URL_LENGTH];
2051 DWORD nbytes, error;
2052 BOOL ret;
2054 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2056 if (*size < sizeof(*ts))
2058 *size = sizeof(*ts);
2059 return ERROR_INSUFFICIENT_BUFFER;
2061 nbytes = 0;
2062 HTTP_GetRequestURL(req, url);
2063 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2064 error = GetLastError();
2065 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2067 if (!(info = heap_alloc(nbytes)))
2068 return ERROR_OUTOFMEMORY;
2070 GetUrlCacheEntryInfoW(url, info, &nbytes);
2072 ts->ftExpires = info->ExpireTime;
2073 ts->ftLastModified = info->LastModifiedTime;
2075 heap_free(info);
2076 *size = sizeof(*ts);
2077 return ERROR_SUCCESS;
2079 return error;
2082 case INTERNET_OPTION_DATAFILE_NAME: {
2083 DWORD req_size;
2085 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2087 if(!req->cacheFile) {
2088 *size = 0;
2089 return ERROR_INTERNET_ITEM_NOT_FOUND;
2092 if(unicode) {
2093 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2094 if(*size < req_size)
2095 return ERROR_INSUFFICIENT_BUFFER;
2097 *size = req_size;
2098 memcpy(buffer, req->cacheFile, *size);
2099 return ERROR_SUCCESS;
2100 }else {
2101 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2102 if (req_size > *size)
2103 return ERROR_INSUFFICIENT_BUFFER;
2105 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2106 -1, buffer, *size, NULL, NULL);
2107 return ERROR_SUCCESS;
2111 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2112 PCCERT_CONTEXT context;
2114 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2115 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2116 return ERROR_INSUFFICIENT_BUFFER;
2119 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2120 if(context) {
2121 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2122 DWORD len;
2124 memset(info, 0, sizeof(*info));
2125 info->ftExpiry = context->pCertInfo->NotAfter;
2126 info->ftStart = context->pCertInfo->NotBefore;
2127 len = CertNameToStrA(context->dwCertEncodingType,
2128 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2129 info->lpszSubjectInfo = LocalAlloc(0, len);
2130 if(info->lpszSubjectInfo)
2131 CertNameToStrA(context->dwCertEncodingType,
2132 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2133 info->lpszSubjectInfo, len);
2134 len = CertNameToStrA(context->dwCertEncodingType,
2135 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2136 info->lpszIssuerInfo = LocalAlloc(0, len);
2137 if(info->lpszIssuerInfo)
2138 CertNameToStrA(context->dwCertEncodingType,
2139 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2140 info->lpszIssuerInfo, len);
2141 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2142 CertFreeCertificateContext(context);
2143 return ERROR_SUCCESS;
2145 return ERROR_NOT_SUPPORTED;
2147 case INTERNET_OPTION_CONNECT_TIMEOUT:
2148 if (*size < sizeof(DWORD))
2149 return ERROR_INSUFFICIENT_BUFFER;
2151 *size = sizeof(DWORD);
2152 *(DWORD *)buffer = req->connect_timeout;
2153 return ERROR_SUCCESS;
2154 case INTERNET_OPTION_REQUEST_FLAGS: {
2155 DWORD flags = 0;
2157 if (*size < sizeof(DWORD))
2158 return ERROR_INSUFFICIENT_BUFFER;
2160 /* FIXME: Add support for:
2161 * INTERNET_REQFLAG_FROM_CACHE
2162 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2165 if(req->proxy)
2166 flags |= INTERNET_REQFLAG_VIA_PROXY;
2167 if(!req->rawHeaders)
2168 flags |= INTERNET_REQFLAG_NO_HEADERS;
2170 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2172 *size = sizeof(DWORD);
2173 *(DWORD*)buffer = flags;
2174 return ERROR_SUCCESS;
2178 return INET_QueryOption(hdr, option, buffer, size, unicode);
2181 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2183 http_request_t *req = (http_request_t*)hdr;
2185 switch(option) {
2186 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2187 TRACE("Undocumented option 99\n");
2189 if (!buffer || size != sizeof(DWORD))
2190 return ERROR_INVALID_PARAMETER;
2191 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2192 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2194 /* fall through */
2195 case INTERNET_OPTION_SECURITY_FLAGS:
2197 DWORD flags;
2199 if (!buffer || size != sizeof(DWORD))
2200 return ERROR_INVALID_PARAMETER;
2201 flags = *(DWORD *)buffer;
2202 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2203 flags &= SECURITY_SET_MASK;
2204 req->security_flags |= flags;
2205 if(req->netconn)
2206 req->netconn->security_flags |= flags;
2207 return ERROR_SUCCESS;
2209 case INTERNET_OPTION_CONNECT_TIMEOUT:
2210 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2211 req->connect_timeout = *(DWORD *)buffer;
2212 return ERROR_SUCCESS;
2214 case INTERNET_OPTION_SEND_TIMEOUT:
2215 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2216 req->send_timeout = *(DWORD *)buffer;
2217 return ERROR_SUCCESS;
2219 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2220 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2221 req->receive_timeout = *(DWORD *)buffer;
2222 return ERROR_SUCCESS;
2224 case INTERNET_OPTION_USERNAME:
2225 heap_free(req->session->userName);
2226 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2227 return ERROR_SUCCESS;
2229 case INTERNET_OPTION_PASSWORD:
2230 heap_free(req->session->password);
2231 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2232 return ERROR_SUCCESS;
2233 case INTERNET_OPTION_HTTP_DECODING:
2234 if(size != sizeof(BOOL))
2235 return ERROR_INVALID_PARAMETER;
2236 req->decoding = *(BOOL*)buffer;
2237 return ERROR_SUCCESS;
2240 return INET_SetOption(hdr, option, buffer, size);
2243 static void commit_cache_entry(http_request_t *req)
2245 WCHAR url[INTERNET_MAX_URL_LENGTH];
2247 TRACE("%p\n", req);
2249 CloseHandle(req->hCacheFile);
2250 req->hCacheFile = NULL;
2252 if(HTTP_GetRequestURL(req, url)) {
2253 DWORD headersLen;
2255 headersLen = req->rawHeaders ? strlenW(req->rawHeaders) : 0;
2256 CommitUrlCacheEntryW(url, req->cacheFile, req->expires,
2257 req->last_modified, NORMAL_CACHE_ENTRY,
2258 req->rawHeaders, headersLen, NULL, 0);
2262 static void create_cache_entry(http_request_t *req)
2264 static const WCHAR no_cacheW[] = {'n','o','-','c','a','c','h','e',0};
2265 static const WCHAR no_storeW[] = {'n','o','-','s','t','o','r','e',0};
2267 WCHAR url[INTERNET_MAX_URL_LENGTH];
2268 WCHAR file_name[MAX_PATH+1];
2269 BOOL b = TRUE;
2271 /* FIXME: We should free previous cache file earlier */
2272 heap_free(req->cacheFile);
2273 CloseHandle(req->hCacheFile);
2274 req->hCacheFile = NULL;
2276 if(req->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE)
2277 b = FALSE;
2279 if(b) {
2280 int header_idx = HTTP_GetCustomHeaderIndex(req, szCache_Control, 0, FALSE);
2281 if(header_idx!=-1 && (!strcmpiW(req->custHeaders[header_idx].lpszValue, no_cacheW)
2282 || !strcmpiW(req->custHeaders[header_idx].lpszValue, no_storeW)))
2283 b = FALSE;
2286 if(!b) {
2287 if(!(req->hdr.dwFlags & INTERNET_FLAG_NEED_FILE))
2288 return;
2290 FIXME("INTERNET_FLAG_NEED_FILE is not supported correctly\n");
2293 b = HTTP_GetRequestURL(req, url);
2294 if(!b) {
2295 WARN("Could not get URL\n");
2296 return;
2299 b = CreateUrlCacheEntryW(url, req->contentLength == ~0u ? 0 : req->contentLength, NULL, file_name, 0);
2300 if(!b) {
2301 WARN("Could not create cache entry: %08x\n", GetLastError());
2302 return;
2305 req->cacheFile = heap_strdupW(file_name);
2306 req->hCacheFile = CreateFileW(req->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2307 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2308 if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2309 WARN("Could not create file: %u\n", GetLastError());
2310 req->hCacheFile = NULL;
2311 return;
2314 if(req->read_size) {
2315 DWORD written;
2317 b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2318 if(!b)
2319 FIXME("WriteFile failed: %u\n", GetLastError());
2321 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2322 commit_cache_entry(req);
2326 /* read some more data into the read buffer (the read section must be held) */
2327 static DWORD read_more_data( http_request_t *req, int maxlen )
2329 DWORD res;
2330 int len;
2332 if (req->read_pos)
2334 /* move existing data to the start of the buffer */
2335 if(req->read_size)
2336 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2337 req->read_pos = 0;
2340 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2342 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2343 maxlen - req->read_size, 0, &len );
2344 if(res == ERROR_SUCCESS)
2345 req->read_size += len;
2347 return res;
2350 /* remove some amount of data from the read buffer (the read section must be held) */
2351 static void remove_data( http_request_t *req, int count )
2353 if (!(req->read_size -= count)) req->read_pos = 0;
2354 else req->read_pos += count;
2357 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2359 int count, bytes_read, pos = 0;
2360 DWORD res;
2362 EnterCriticalSection( &req->read_section );
2363 for (;;)
2365 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2367 if (eol)
2369 count = eol - (req->read_buf + req->read_pos);
2370 bytes_read = count + 1;
2372 else count = bytes_read = req->read_size;
2374 count = min( count, *len - pos );
2375 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2376 pos += count;
2377 remove_data( req, bytes_read );
2378 if (eol) break;
2380 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2382 *len = 0;
2383 TRACE( "returning empty string %u\n", res);
2384 LeaveCriticalSection( &req->read_section );
2385 INTERNET_SetLastError(res);
2386 return FALSE;
2389 LeaveCriticalSection( &req->read_section );
2391 if (pos < *len)
2393 if (pos && buffer[pos - 1] == '\r') pos--;
2394 *len = pos + 1;
2396 buffer[*len - 1] = 0;
2397 TRACE( "returning %s\n", debugstr_a(buffer));
2398 return TRUE;
2401 /* check if we have reached the end of the data to read (the read section must be held) */
2402 static BOOL end_of_read_data( http_request_t *req )
2404 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2407 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
2409 DWORD res;
2411 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
2412 assert(*read <= size);
2414 if(req->hCacheFile) {
2415 if(*read) {
2416 BOOL bres;
2417 DWORD written;
2419 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2420 if(!bres)
2421 FIXME("WriteFile failed: %u\n", GetLastError());
2424 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2425 commit_cache_entry(req);
2428 return res;
2431 /* fetch some more data into the read buffer (the read section must be held) */
2432 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2434 DWORD res, read=0;
2436 if(req->read_size == sizeof(req->read_buf))
2437 return ERROR_SUCCESS;
2439 if(req->read_pos) {
2440 if(req->read_size)
2441 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2442 req->read_pos = 0;
2445 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2446 &read, read_mode);
2447 req->read_size += read;
2449 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2450 if(read_bytes)
2451 *read_bytes = read;
2452 return res;
2455 /* return the size of data available to be read immediately (the read section must be held) */
2456 static DWORD get_avail_data( http_request_t *req )
2458 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2461 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2463 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2464 DWORD avail = 0;
2466 if(req->netconn)
2467 NETCON_query_data_available(req->netconn, &avail);
2468 return netconn_stream->content_length == ~0u
2469 ? avail
2470 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2473 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2475 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2476 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2479 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2480 DWORD *read, read_mode_t read_mode)
2482 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2483 int len = 0;
2485 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2487 if(read_mode == READMODE_NOBLOCK) {
2488 DWORD avail = netconn_get_avail_data(stream, req);
2489 if (size > avail)
2490 size = avail;
2493 if(size && req->netconn) {
2494 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2495 len = 0;
2496 if(!len)
2497 netconn_stream->content_length = netconn_stream->content_read;
2500 netconn_stream->content_read += *read = len;
2501 TRACE("read %u bytes\n", len);
2502 return ERROR_SUCCESS;
2505 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2507 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2508 BYTE buf[1024];
2509 DWORD avail;
2510 int len;
2512 if(netconn_end_of_data(stream, req))
2513 return TRUE;
2515 do {
2516 avail = netconn_get_avail_data(stream, req);
2517 if(!avail)
2518 return FALSE;
2520 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2521 return FALSE;
2523 netconn_stream->content_read += len;
2524 }while(netconn_stream->content_read < netconn_stream->content_length);
2526 return TRUE;
2529 static void netconn_destroy(data_stream_t *stream)
2533 static const data_stream_vtbl_t netconn_stream_vtbl = {
2534 netconn_get_avail_data,
2535 netconn_end_of_data,
2536 netconn_read,
2537 netconn_drain_content,
2538 netconn_destroy
2541 /* read some more data into the read buffer (the read section must be held) */
2542 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2544 DWORD res;
2545 int len;
2547 if (stream->buf_pos)
2549 /* move existing data to the start of the buffer */
2550 if(stream->buf_size)
2551 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2552 stream->buf_pos = 0;
2555 if (maxlen == -1) maxlen = sizeof(stream->buf);
2557 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2558 maxlen - stream->buf_size, 0, &len );
2559 if(res == ERROR_SUCCESS)
2560 stream->buf_size += len;
2562 return res;
2565 /* remove some amount of data from the read buffer (the read section must be held) */
2566 static void remove_chunked_data(chunked_stream_t *stream, int count)
2568 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2569 else stream->buf_pos += count;
2572 /* discard data contents until we reach end of line (the read section must be held) */
2573 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2575 DWORD res;
2579 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2580 if (eol)
2582 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2583 break;
2585 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2586 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2587 } while (stream->buf_size);
2588 return ERROR_SUCCESS;
2591 /* read the size of the next chunk (the read section must be held) */
2592 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2594 /* TODOO */
2595 DWORD chunk_size = 0, res;
2597 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2598 return res;
2600 for (;;)
2602 while (stream->buf_size)
2604 char ch = stream->buf[stream->buf_pos];
2605 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2606 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2607 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2608 else if (ch == ';' || ch == '\r' || ch == '\n')
2610 TRACE( "reading %u byte chunk\n", chunk_size );
2611 stream->chunk_size = chunk_size;
2612 req->contentLength += chunk_size;
2613 return discard_chunked_eol(stream, req);
2615 remove_chunked_data(stream, 1);
2617 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2618 if (!stream->buf_size)
2620 stream->chunk_size = 0;
2621 return ERROR_SUCCESS;
2626 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2628 /* Allow reading only from read buffer */
2629 return 0;
2632 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2634 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2635 return !chunked_stream->chunk_size;
2638 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2639 DWORD *read, read_mode_t read_mode)
2641 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2642 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2644 if(chunked_stream->chunk_size == ~0u) {
2645 res = start_next_chunk(chunked_stream, req);
2646 if(res != ERROR_SUCCESS)
2647 return res;
2650 while(size && chunked_stream->chunk_size) {
2651 if(chunked_stream->buf_size) {
2652 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2654 /* this could block */
2655 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2656 break;
2658 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2659 remove_chunked_data(chunked_stream, read_bytes);
2660 }else {
2661 read_bytes = min(size, chunked_stream->chunk_size);
2663 if(read_mode == READMODE_NOBLOCK) {
2664 DWORD avail;
2666 if(!req->netconn || !NETCON_query_data_available(req->netconn, &avail) || !avail)
2667 break;
2668 if(read_bytes > avail)
2669 read_bytes = avail;
2671 /* this could block */
2672 if(read_bytes == chunked_stream->chunk_size)
2673 break;
2676 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2677 if(res != ERROR_SUCCESS)
2678 break;
2681 chunked_stream->chunk_size -= read_bytes;
2682 size -= read_bytes;
2683 ret_read += read_bytes;
2684 if(!chunked_stream->chunk_size) {
2685 assert(read_mode != READMODE_NOBLOCK);
2686 res = start_next_chunk(chunked_stream, req);
2687 if(res != ERROR_SUCCESS)
2688 break;
2691 if(read_mode == READMODE_ASYNC)
2692 read_mode = READMODE_NOBLOCK;
2695 TRACE("read %u bytes\n", ret_read);
2696 *read = ret_read;
2697 return res;
2700 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2702 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2704 /* FIXME: we can do better */
2705 return !chunked_stream->chunk_size;
2708 static void chunked_destroy(data_stream_t *stream)
2710 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2711 heap_free(chunked_stream);
2714 static const data_stream_vtbl_t chunked_stream_vtbl = {
2715 chunked_get_avail_data,
2716 chunked_end_of_data,
2717 chunked_read,
2718 chunked_drain_content,
2719 chunked_destroy
2722 /* set the request content length based on the headers */
2723 static DWORD set_content_length(http_request_t *request)
2725 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2726 WCHAR encoding[20];
2727 DWORD size;
2729 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2730 request->contentLength = request->netconn_stream.content_length = 0;
2731 return ERROR_SUCCESS;
2734 size = sizeof(request->contentLength);
2735 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2736 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2737 request->contentLength = ~0u;
2738 request->netconn_stream.content_length = request->contentLength;
2739 request->netconn_stream.content_read = request->read_size;
2741 size = sizeof(encoding);
2742 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2743 !strcmpiW(encoding, szChunked))
2745 chunked_stream_t *chunked_stream;
2747 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2748 if(!chunked_stream)
2749 return ERROR_OUTOFMEMORY;
2751 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2752 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2753 chunked_stream->chunk_size = ~0u;
2755 if(request->read_size) {
2756 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2757 chunked_stream->buf_size = request->read_size;
2758 request->read_size = request->read_pos = 0;
2761 request->data_stream = &chunked_stream->data_stream;
2762 request->contentLength = ~0u;
2763 request->read_chunked = TRUE;
2766 if(request->decoding) {
2767 int encoding_idx;
2769 static const WCHAR gzipW[] = {'g','z','i','p',0};
2771 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2772 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2773 return init_gzip_stream(request);
2776 return ERROR_SUCCESS;
2779 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2781 INTERNET_ASYNC_RESULT iar;
2783 iar.dwResult = result;
2784 iar.dwError = error;
2786 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2787 sizeof(INTERNET_ASYNC_RESULT));
2790 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif, DWORD *ret_size)
2792 DWORD res, read = 0, avail = 0;
2793 read_mode_t mode;
2795 TRACE("%p\n", req);
2797 EnterCriticalSection( &req->read_section );
2799 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2800 res = refill_read_buffer(req, mode, &read);
2801 if(res == ERROR_SUCCESS)
2802 avail = get_avail_data(req);
2804 LeaveCriticalSection( &req->read_section );
2806 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2807 WARN("res %u read %u, closing connection\n", res, read);
2808 http_release_netconn(req, FALSE);
2811 if(res != ERROR_SUCCESS) {
2812 send_request_complete(req, 0, res);
2813 return;
2816 if(ret_size)
2817 *ret_size = avail;
2818 if(first_notif)
2819 avail = 0;
2821 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2824 /* read data from the http connection (the read section must be held) */
2825 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2827 DWORD current_read = 0, ret_read = 0;
2828 read_mode_t read_mode;
2829 DWORD res = ERROR_SUCCESS;
2831 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2833 EnterCriticalSection( &req->read_section );
2835 if(req->read_size) {
2836 ret_read = min(size, req->read_size);
2837 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2838 req->read_size -= ret_read;
2839 req->read_pos += ret_read;
2840 if(read_mode == READMODE_ASYNC)
2841 read_mode = READMODE_NOBLOCK;
2844 if(ret_read < size) {
2845 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2846 ret_read += current_read;
2849 LeaveCriticalSection( &req->read_section );
2851 *read = ret_read;
2852 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2854 if(size && !ret_read)
2855 http_release_netconn(req, res == ERROR_SUCCESS);
2857 return res;
2860 static BOOL drain_content(http_request_t *req, BOOL blocking)
2862 BOOL ret;
2864 if(!req->netconn || req->contentLength == -1)
2865 return FALSE;
2867 if(!strcmpW(req->verb, szHEAD))
2868 return TRUE;
2870 if(!blocking)
2871 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2873 EnterCriticalSection( &req->read_section );
2875 while(1) {
2876 DWORD bytes_read, res;
2877 BYTE buf[4096];
2879 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2880 if(res != ERROR_SUCCESS) {
2881 ret = FALSE;
2882 break;
2884 if(!bytes_read) {
2885 ret = TRUE;
2886 break;
2890 LeaveCriticalSection( &req->read_section );
2891 return ret;
2894 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2896 http_request_t *req = (http_request_t*)hdr;
2897 DWORD res;
2899 EnterCriticalSection( &req->read_section );
2900 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2901 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2903 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2904 if(res == ERROR_SUCCESS)
2905 res = hdr->dwError;
2906 LeaveCriticalSection( &req->read_section );
2908 return res;
2911 typedef struct {
2912 task_header_t hdr;
2913 void *buf;
2914 DWORD size;
2915 DWORD *ret_read;
2916 } read_file_ex_task_t;
2918 static void AsyncReadFileExProc(task_header_t *hdr)
2920 read_file_ex_task_t *task = (read_file_ex_task_t*)hdr;
2921 http_request_t *req = (http_request_t*)task->hdr.hdr;
2922 DWORD res;
2924 TRACE("INTERNETREADFILEEXW %p\n", task->hdr.hdr);
2926 res = HTTPREQ_Read(req, task->buf, task->size, task->ret_read, TRUE);
2927 send_request_complete(req, res == ERROR_SUCCESS, res);
2930 static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
2931 DWORD flags, DWORD_PTR context)
2934 http_request_t *req = (http_request_t*)hdr;
2935 DWORD res, read, cread, error = ERROR_SUCCESS;
2937 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2938 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2940 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2942 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2944 read_file_ex_task_t *task;
2946 if (TryEnterCriticalSection( &req->read_section ))
2948 if (get_avail_data(req))
2950 res = HTTPREQ_Read(req, buf, size, &read, FALSE);
2951 LeaveCriticalSection( &req->read_section );
2952 goto done;
2954 LeaveCriticalSection( &req->read_section );
2957 task = alloc_async_task(&req->hdr, AsyncReadFileExProc, sizeof(*task));
2958 task->buf = buf;
2959 task->size = size;
2960 task->ret_read = ret_read;
2962 INTERNET_AsyncCall(&task->hdr);
2964 return ERROR_IO_PENDING;
2967 read = 0;
2969 EnterCriticalSection( &req->read_section );
2970 if(hdr->dwError == ERROR_SUCCESS)
2971 hdr->dwError = INTERNET_HANDLE_IN_USE;
2972 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2973 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2975 while(1) {
2976 res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, !(flags & IRF_NO_WAIT));
2977 if(res != ERROR_SUCCESS)
2978 break;
2980 read += cread;
2981 if(read == size || end_of_read_data(req))
2982 break;
2984 LeaveCriticalSection( &req->read_section );
2986 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2987 &cread, sizeof(cread));
2988 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2989 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2991 EnterCriticalSection( &req->read_section );
2994 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2995 hdr->dwError = ERROR_SUCCESS;
2996 else
2997 error = hdr->dwError;
2999 LeaveCriticalSection( &req->read_section );
3001 done:
3002 *ret_read = read;
3003 if (res == ERROR_SUCCESS) {
3004 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3005 &read, sizeof(read));
3008 return res==ERROR_SUCCESS ? error : res;
3011 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3013 DWORD res;
3014 http_request_t *request = (http_request_t*)hdr;
3016 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3018 *written = 0;
3019 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3020 if (res == ERROR_SUCCESS)
3021 request->bytesWritten += *written;
3023 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3024 return res;
3027 typedef struct {
3028 task_header_t hdr;
3029 DWORD *ret_size;
3030 } http_data_available_task_t;
3032 static void AsyncQueryDataAvailableProc(task_header_t *hdr)
3034 http_data_available_task_t *task = (http_data_available_task_t*)hdr;
3036 HTTP_ReceiveRequestData((http_request_t*)task->hdr.hdr, FALSE, task->ret_size);
3039 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3041 http_request_t *req = (http_request_t*)hdr;
3043 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3045 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3047 http_data_available_task_t *task;
3049 /* never wait, if we can't enter the section we queue an async request right away */
3050 if (TryEnterCriticalSection( &req->read_section ))
3052 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3053 if ((*available = get_avail_data( req ))) goto done;
3054 if (end_of_read_data( req )) goto done;
3055 LeaveCriticalSection( &req->read_section );
3058 task = alloc_async_task(&req->hdr, AsyncQueryDataAvailableProc, sizeof(*task));
3059 task->ret_size = available;
3060 INTERNET_AsyncCall(&task->hdr);
3061 return ERROR_IO_PENDING;
3064 EnterCriticalSection( &req->read_section );
3066 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3068 refill_read_buffer( req, READMODE_ASYNC, NULL );
3069 *available = get_avail_data( req );
3072 done:
3073 LeaveCriticalSection( &req->read_section );
3075 TRACE( "returning %u\n", *available );
3076 return ERROR_SUCCESS;
3079 static const object_vtbl_t HTTPREQVtbl = {
3080 HTTPREQ_Destroy,
3081 HTTPREQ_CloseConnection,
3082 HTTPREQ_QueryOption,
3083 HTTPREQ_SetOption,
3084 HTTPREQ_ReadFile,
3085 HTTPREQ_ReadFileEx,
3086 HTTPREQ_WriteFile,
3087 HTTPREQ_QueryDataAvailable,
3088 NULL
3091 /***********************************************************************
3092 * HTTP_HttpOpenRequestW (internal)
3094 * Open a HTTP request handle
3096 * RETURNS
3097 * HINTERNET a HTTP request handle on success
3098 * NULL on failure
3101 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3102 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3103 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3104 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3106 appinfo_t *hIC = session->appInfo;
3107 http_request_t *request;
3108 DWORD len, res = ERROR_SUCCESS;
3110 TRACE("-->\n");
3112 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3113 if(!request)
3114 return ERROR_OUTOFMEMORY;
3116 request->hdr.htype = WH_HHTTPREQ;
3117 request->hdr.dwFlags = dwFlags;
3118 request->hdr.dwContext = dwContext;
3119 request->contentLength = ~0u;
3121 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3122 request->data_stream = &request->netconn_stream.data_stream;
3123 request->connect_timeout = session->connect_timeout;
3124 request->send_timeout = session->send_timeout;
3125 request->receive_timeout = session->receive_timeout;
3127 InitializeCriticalSection( &request->read_section );
3128 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3130 WININET_AddRef( &session->hdr );
3131 request->session = session;
3132 list_add_head( &session->hdr.children, &request->hdr.entry );
3134 request->server = get_server(session->hostName, session->hostPort, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3135 if(!request->server) {
3136 WININET_Release(&request->hdr);
3137 return ERROR_OUTOFMEMORY;
3140 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3141 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3142 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3143 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3145 if (lpszObjectName && *lpszObjectName) {
3146 HRESULT rc;
3148 len = 0;
3149 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3150 if (rc != E_POINTER)
3151 len = strlenW(lpszObjectName)+1;
3152 request->path = heap_alloc(len*sizeof(WCHAR));
3153 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3154 URL_ESCAPE_SPACES_ONLY);
3155 if (rc != S_OK)
3157 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3158 strcpyW(request->path,lpszObjectName);
3160 }else {
3161 static const WCHAR slashW[] = {'/',0};
3163 request->path = heap_strdupW(slashW);
3166 if (lpszReferrer && *lpszReferrer)
3167 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3169 if (lpszAcceptTypes)
3171 int i;
3172 for (i = 0; lpszAcceptTypes[i]; i++)
3174 if (!*lpszAcceptTypes[i]) continue;
3175 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3176 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3177 HTTP_ADDHDR_FLAG_REQ |
3178 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3182 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3183 request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3185 HTTP_ProcessHeader(request, hostW, request->server->canon_host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3187 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3188 session->hostPort = INTERNET_DEFAULT_HTTP_PORT;
3190 if (hIC->proxy && hIC->proxy[0])
3191 HTTP_DealWithProxy( hIC, session, request );
3193 INTERNET_SendCallback(&session->hdr, dwContext,
3194 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3195 sizeof(HINTERNET));
3197 TRACE("<-- %u (%p)\n", res, request);
3199 if(res != ERROR_SUCCESS) {
3200 WININET_Release( &request->hdr );
3201 *ret = NULL;
3202 return res;
3205 *ret = request->hdr.hInternet;
3206 return ERROR_SUCCESS;
3209 /***********************************************************************
3210 * HttpOpenRequestW (WININET.@)
3212 * Open a HTTP request handle
3214 * RETURNS
3215 * HINTERNET a HTTP request handle on success
3216 * NULL on failure
3219 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3220 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3221 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3222 DWORD dwFlags, DWORD_PTR dwContext)
3224 http_session_t *session;
3225 HINTERNET handle = NULL;
3226 DWORD res;
3228 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3229 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3230 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3231 dwFlags, dwContext);
3232 if(lpszAcceptTypes!=NULL)
3234 int i;
3235 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3236 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3239 session = (http_session_t*) get_handle_object( hHttpSession );
3240 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3242 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3243 goto lend;
3247 * My tests seem to show that the windows version does not
3248 * become asynchronous until after this point. And anyhow
3249 * if this call was asynchronous then how would you get the
3250 * necessary HINTERNET pointer returned by this function.
3253 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3254 lpszVersion, lpszReferrer, lpszAcceptTypes,
3255 dwFlags, dwContext, &handle);
3256 lend:
3257 if( session )
3258 WININET_Release( &session->hdr );
3259 TRACE("returning %p\n", handle);
3260 if(res != ERROR_SUCCESS)
3261 SetLastError(res);
3262 return handle;
3265 static const LPCWSTR header_lookup[] = {
3266 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3267 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3268 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3269 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3270 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3271 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3272 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3273 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3274 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3275 szDate, /* HTTP_QUERY_DATE = 9 */
3276 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3277 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3278 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3279 szURI, /* HTTP_QUERY_URI = 13 */
3280 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3281 NULL, /* HTTP_QUERY_COST = 15 */
3282 NULL, /* HTTP_QUERY_LINK = 16 */
3283 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3284 NULL, /* HTTP_QUERY_VERSION = 18 */
3285 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3286 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3287 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3288 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3289 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3290 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3291 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3292 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3293 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3294 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3295 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3296 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3297 NULL, /* HTTP_QUERY_FROM = 31 */
3298 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3299 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3300 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3301 szReferer, /* HTTP_QUERY_REFERER = 35 */
3302 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3303 szServer, /* HTTP_QUERY_SERVER = 37 */
3304 NULL, /* HTTP_TITLE = 38 */
3305 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3306 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3307 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3308 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3309 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3310 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3311 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3312 NULL, /* HTTP_QUERY_REFRESH = 46 */
3313 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3314 szAge, /* HTTP_QUERY_AGE = 48 */
3315 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3316 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3317 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3318 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3319 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3320 szETag, /* HTTP_QUERY_ETAG = 54 */
3321 hostW, /* HTTP_QUERY_HOST = 55 */
3322 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3323 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3324 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3325 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3326 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3327 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3328 szRange, /* HTTP_QUERY_RANGE = 62 */
3329 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3330 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3331 szVary, /* HTTP_QUERY_VARY = 65 */
3332 szVia, /* HTTP_QUERY_VIA = 66 */
3333 szWarning, /* HTTP_QUERY_WARNING = 67 */
3334 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3335 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3336 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3339 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3341 /***********************************************************************
3342 * HTTP_HttpQueryInfoW (internal)
3344 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3345 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3347 LPHTTPHEADERW lphttpHdr = NULL;
3348 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3349 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3350 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3351 INT index = -1;
3353 /* Find requested header structure */
3354 switch (level)
3356 case HTTP_QUERY_CUSTOM:
3357 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3358 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3359 break;
3360 case HTTP_QUERY_RAW_HEADERS_CRLF:
3362 LPWSTR headers;
3363 DWORD len = 0;
3364 DWORD res = ERROR_INVALID_PARAMETER;
3366 if (request_only)
3367 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3368 else
3369 headers = request->rawHeaders;
3371 if (headers)
3372 len = strlenW(headers) * sizeof(WCHAR);
3374 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3376 len += sizeof(WCHAR);
3377 res = ERROR_INSUFFICIENT_BUFFER;
3379 else if (lpBuffer)
3381 if (headers)
3382 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3383 else
3385 len = strlenW(szCrLf) * sizeof(WCHAR);
3386 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3388 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3389 res = ERROR_SUCCESS;
3391 *lpdwBufferLength = len;
3393 if (request_only) heap_free(headers);
3394 return res;
3396 case HTTP_QUERY_RAW_HEADERS:
3398 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3399 DWORD i, size = 0;
3400 LPWSTR pszString = lpBuffer;
3402 for (i = 0; ppszRawHeaderLines[i]; i++)
3403 size += strlenW(ppszRawHeaderLines[i]) + 1;
3405 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3407 HTTP_FreeTokens(ppszRawHeaderLines);
3408 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3409 return ERROR_INSUFFICIENT_BUFFER;
3411 if (pszString)
3413 for (i = 0; ppszRawHeaderLines[i]; i++)
3415 DWORD len = strlenW(ppszRawHeaderLines[i]);
3416 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3417 pszString += len+1;
3419 *pszString = '\0';
3420 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3422 *lpdwBufferLength = size * sizeof(WCHAR);
3423 HTTP_FreeTokens(ppszRawHeaderLines);
3425 return ERROR_SUCCESS;
3427 case HTTP_QUERY_STATUS_TEXT:
3428 if (request->statusText)
3430 DWORD len = strlenW(request->statusText);
3431 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3433 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3434 return ERROR_INSUFFICIENT_BUFFER;
3436 if (lpBuffer)
3438 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3439 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3441 *lpdwBufferLength = len * sizeof(WCHAR);
3442 return ERROR_SUCCESS;
3444 break;
3445 case HTTP_QUERY_VERSION:
3446 if (request->version)
3448 DWORD len = strlenW(request->version);
3449 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3451 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3452 return ERROR_INSUFFICIENT_BUFFER;
3454 if (lpBuffer)
3456 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3457 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3459 *lpdwBufferLength = len * sizeof(WCHAR);
3460 return ERROR_SUCCESS;
3462 break;
3463 case HTTP_QUERY_CONTENT_ENCODING:
3464 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3465 requested_index,request_only);
3466 break;
3467 case HTTP_QUERY_STATUS_CODE: {
3468 DWORD res = ERROR_SUCCESS;
3470 if(request_only)
3471 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3473 if(requested_index)
3474 break;
3476 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3477 if(*lpdwBufferLength >= sizeof(DWORD))
3478 *(DWORD*)lpBuffer = request->status_code;
3479 else
3480 res = ERROR_INSUFFICIENT_BUFFER;
3481 *lpdwBufferLength = sizeof(DWORD);
3482 }else {
3483 WCHAR buf[12];
3484 DWORD size;
3485 static const WCHAR formatW[] = {'%','u',0};
3487 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3489 if(size <= *lpdwBufferLength) {
3490 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3491 }else {
3492 size += sizeof(WCHAR);
3493 res = ERROR_INSUFFICIENT_BUFFER;
3496 *lpdwBufferLength = size;
3498 return res;
3500 default:
3501 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3503 if (level < LAST_TABLE_HEADER && header_lookup[level])
3504 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3505 requested_index,request_only);
3508 if (index >= 0)
3509 lphttpHdr = &request->custHeaders[index];
3511 /* Ensure header satisfies requested attributes */
3512 if (!lphttpHdr ||
3513 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3514 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3516 return ERROR_HTTP_HEADER_NOT_FOUND;
3519 if (lpdwIndex) (*lpdwIndex)++;
3521 /* coalesce value to requested type */
3522 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3524 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3525 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3527 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3529 time_t tmpTime;
3530 struct tm tmpTM;
3531 SYSTEMTIME *STHook;
3533 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3535 tmpTM = *gmtime(&tmpTime);
3536 STHook = (SYSTEMTIME *)lpBuffer;
3537 STHook->wDay = tmpTM.tm_mday;
3538 STHook->wHour = tmpTM.tm_hour;
3539 STHook->wMilliseconds = 0;
3540 STHook->wMinute = tmpTM.tm_min;
3541 STHook->wDayOfWeek = tmpTM.tm_wday;
3542 STHook->wMonth = tmpTM.tm_mon + 1;
3543 STHook->wSecond = tmpTM.tm_sec;
3544 STHook->wYear = 1900+tmpTM.tm_year;
3546 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3547 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3548 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3550 else if (lphttpHdr->lpszValue)
3552 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3554 if (len > *lpdwBufferLength)
3556 *lpdwBufferLength = len;
3557 return ERROR_INSUFFICIENT_BUFFER;
3559 if (lpBuffer)
3561 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3562 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3564 *lpdwBufferLength = len - sizeof(WCHAR);
3566 return ERROR_SUCCESS;
3569 /***********************************************************************
3570 * HttpQueryInfoW (WININET.@)
3572 * Queries for information about an HTTP request
3574 * RETURNS
3575 * TRUE on success
3576 * FALSE on failure
3579 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3580 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3582 http_request_t *request;
3583 DWORD res;
3585 if (TRACE_ON(wininet)) {
3586 #define FE(x) { x, #x }
3587 static const wininet_flag_info query_flags[] = {
3588 FE(HTTP_QUERY_MIME_VERSION),
3589 FE(HTTP_QUERY_CONTENT_TYPE),
3590 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3591 FE(HTTP_QUERY_CONTENT_ID),
3592 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3593 FE(HTTP_QUERY_CONTENT_LENGTH),
3594 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3595 FE(HTTP_QUERY_ALLOW),
3596 FE(HTTP_QUERY_PUBLIC),
3597 FE(HTTP_QUERY_DATE),
3598 FE(HTTP_QUERY_EXPIRES),
3599 FE(HTTP_QUERY_LAST_MODIFIED),
3600 FE(HTTP_QUERY_MESSAGE_ID),
3601 FE(HTTP_QUERY_URI),
3602 FE(HTTP_QUERY_DERIVED_FROM),
3603 FE(HTTP_QUERY_COST),
3604 FE(HTTP_QUERY_LINK),
3605 FE(HTTP_QUERY_PRAGMA),
3606 FE(HTTP_QUERY_VERSION),
3607 FE(HTTP_QUERY_STATUS_CODE),
3608 FE(HTTP_QUERY_STATUS_TEXT),
3609 FE(HTTP_QUERY_RAW_HEADERS),
3610 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3611 FE(HTTP_QUERY_CONNECTION),
3612 FE(HTTP_QUERY_ACCEPT),
3613 FE(HTTP_QUERY_ACCEPT_CHARSET),
3614 FE(HTTP_QUERY_ACCEPT_ENCODING),
3615 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3616 FE(HTTP_QUERY_AUTHORIZATION),
3617 FE(HTTP_QUERY_CONTENT_ENCODING),
3618 FE(HTTP_QUERY_FORWARDED),
3619 FE(HTTP_QUERY_FROM),
3620 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3621 FE(HTTP_QUERY_LOCATION),
3622 FE(HTTP_QUERY_ORIG_URI),
3623 FE(HTTP_QUERY_REFERER),
3624 FE(HTTP_QUERY_RETRY_AFTER),
3625 FE(HTTP_QUERY_SERVER),
3626 FE(HTTP_QUERY_TITLE),
3627 FE(HTTP_QUERY_USER_AGENT),
3628 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3629 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3630 FE(HTTP_QUERY_ACCEPT_RANGES),
3631 FE(HTTP_QUERY_SET_COOKIE),
3632 FE(HTTP_QUERY_COOKIE),
3633 FE(HTTP_QUERY_REQUEST_METHOD),
3634 FE(HTTP_QUERY_REFRESH),
3635 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3636 FE(HTTP_QUERY_AGE),
3637 FE(HTTP_QUERY_CACHE_CONTROL),
3638 FE(HTTP_QUERY_CONTENT_BASE),
3639 FE(HTTP_QUERY_CONTENT_LOCATION),
3640 FE(HTTP_QUERY_CONTENT_MD5),
3641 FE(HTTP_QUERY_CONTENT_RANGE),
3642 FE(HTTP_QUERY_ETAG),
3643 FE(HTTP_QUERY_HOST),
3644 FE(HTTP_QUERY_IF_MATCH),
3645 FE(HTTP_QUERY_IF_NONE_MATCH),
3646 FE(HTTP_QUERY_IF_RANGE),
3647 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3648 FE(HTTP_QUERY_MAX_FORWARDS),
3649 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3650 FE(HTTP_QUERY_RANGE),
3651 FE(HTTP_QUERY_TRANSFER_ENCODING),
3652 FE(HTTP_QUERY_UPGRADE),
3653 FE(HTTP_QUERY_VARY),
3654 FE(HTTP_QUERY_VIA),
3655 FE(HTTP_QUERY_WARNING),
3656 FE(HTTP_QUERY_CUSTOM)
3658 static const wininet_flag_info modifier_flags[] = {
3659 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3660 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3661 FE(HTTP_QUERY_FLAG_NUMBER),
3662 FE(HTTP_QUERY_FLAG_COALESCE)
3664 #undef FE
3665 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3666 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3667 DWORD i;
3669 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3670 TRACE(" Attribute:");
3671 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3672 if (query_flags[i].val == info) {
3673 TRACE(" %s", query_flags[i].name);
3674 break;
3677 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3678 TRACE(" Unknown (%08x)", info);
3681 TRACE(" Modifier:");
3682 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3683 if (modifier_flags[i].val & info_mod) {
3684 TRACE(" %s", modifier_flags[i].name);
3685 info_mod &= ~ modifier_flags[i].val;
3689 if (info_mod) {
3690 TRACE(" Unknown (%08x)", info_mod);
3692 TRACE("\n");
3695 request = (http_request_t*) get_handle_object( hHttpRequest );
3696 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3698 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3699 goto lend;
3702 if (lpBuffer == NULL)
3703 *lpdwBufferLength = 0;
3704 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3705 lpBuffer, lpdwBufferLength, lpdwIndex);
3707 lend:
3708 if( request )
3709 WININET_Release( &request->hdr );
3711 TRACE("%u <--\n", res);
3712 if(res != ERROR_SUCCESS)
3713 SetLastError(res);
3714 return res == ERROR_SUCCESS;
3717 /***********************************************************************
3718 * HttpQueryInfoA (WININET.@)
3720 * Queries for information about an HTTP request
3722 * RETURNS
3723 * TRUE on success
3724 * FALSE on failure
3727 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3728 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3730 BOOL result;
3731 DWORD len;
3732 WCHAR* bufferW;
3734 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3735 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3737 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3738 lpdwBufferLength, lpdwIndex );
3741 if (lpBuffer)
3743 DWORD alloclen;
3744 len = (*lpdwBufferLength)*sizeof(WCHAR);
3745 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3747 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3748 if (alloclen < len)
3749 alloclen = len;
3751 else
3752 alloclen = len;
3753 bufferW = heap_alloc(alloclen);
3754 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3755 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3756 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3757 } else
3759 bufferW = NULL;
3760 len = 0;
3763 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3764 &len, lpdwIndex );
3765 if( result )
3767 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3768 lpBuffer, *lpdwBufferLength, NULL, NULL );
3769 *lpdwBufferLength = len - 1;
3771 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3773 else
3774 /* since the strings being returned from HttpQueryInfoW should be
3775 * only ASCII characters, it is reasonable to assume that all of
3776 * the Unicode characters can be reduced to a single byte */
3777 *lpdwBufferLength = len / sizeof(WCHAR);
3779 heap_free( bufferW );
3780 return result;
3783 /***********************************************************************
3784 * HTTP_GetRedirectURL (internal)
3786 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3788 static WCHAR szHttp[] = {'h','t','t','p',0};
3789 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3790 http_session_t *session = request->session;
3791 URL_COMPONENTSW urlComponents;
3792 DWORD url_length = 0;
3793 LPWSTR orig_url;
3794 LPWSTR combined_url;
3796 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3797 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3798 urlComponents.dwSchemeLength = 0;
3799 urlComponents.lpszHostName = session->hostName;
3800 urlComponents.dwHostNameLength = 0;
3801 urlComponents.nPort = session->hostPort;
3802 urlComponents.lpszUserName = session->userName;
3803 urlComponents.dwUserNameLength = 0;
3804 urlComponents.lpszPassword = NULL;
3805 urlComponents.dwPasswordLength = 0;
3806 urlComponents.lpszUrlPath = request->path;
3807 urlComponents.dwUrlPathLength = 0;
3808 urlComponents.lpszExtraInfo = NULL;
3809 urlComponents.dwExtraInfoLength = 0;
3811 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3812 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3813 return NULL;
3815 orig_url = heap_alloc(url_length);
3817 /* convert from bytes to characters */
3818 url_length = url_length / sizeof(WCHAR) - 1;
3819 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3821 heap_free(orig_url);
3822 return NULL;
3825 url_length = 0;
3826 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3827 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3829 heap_free(orig_url);
3830 return NULL;
3832 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3834 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3836 heap_free(orig_url);
3837 heap_free(combined_url);
3838 return NULL;
3840 heap_free(orig_url);
3841 return combined_url;
3845 /***********************************************************************
3846 * HTTP_HandleRedirect (internal)
3848 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3850 http_session_t *session = request->session;
3851 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3852 int index;
3854 if(lpszUrl[0]=='/')
3856 /* if it's an absolute path, keep the same session info */
3857 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3859 else
3861 URL_COMPONENTSW urlComponents;
3862 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3863 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3864 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3865 BOOL custom_port = FALSE;
3867 static WCHAR httpW[] = {'h','t','t','p',0};
3868 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3870 userName[0] = 0;
3871 hostName[0] = 0;
3872 protocol[0] = 0;
3874 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3875 urlComponents.lpszScheme = protocol;
3876 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3877 urlComponents.lpszHostName = hostName;
3878 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3879 urlComponents.lpszUserName = userName;
3880 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3881 urlComponents.lpszPassword = NULL;
3882 urlComponents.dwPasswordLength = 0;
3883 urlComponents.lpszUrlPath = path;
3884 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3885 urlComponents.lpszExtraInfo = NULL;
3886 urlComponents.dwExtraInfoLength = 0;
3887 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3888 return INTERNET_GetLastError();
3890 if(!strcmpiW(protocol, httpW)) {
3891 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3892 TRACE("redirect from secure page to non-secure page\n");
3893 /* FIXME: warn about from secure redirect to non-secure page */
3894 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3897 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3898 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3899 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3900 custom_port = TRUE;
3901 }else if(!strcmpiW(protocol, httpsW)) {
3902 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3903 TRACE("redirect from non-secure page to secure page\n");
3904 /* FIXME: notify about redirect to secure page */
3905 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3908 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3909 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3910 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3911 custom_port = TRUE;
3914 heap_free(session->hostName);
3916 if(custom_port) {
3917 int len;
3918 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3919 len = lstrlenW(hostName);
3920 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3921 session->hostName = heap_alloc(len*sizeof(WCHAR));
3922 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3924 else
3925 session->hostName = heap_strdupW(hostName);
3927 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3929 heap_free(session->userName);
3930 session->userName = NULL;
3931 if (userName[0])
3932 session->userName = heap_strdupW(userName);
3934 reset_data_stream(request);
3936 if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
3937 server_t *new_server;
3939 new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
3940 server_release(request->server);
3941 request->server = new_server;
3944 heap_free(request->path);
3945 request->path=NULL;
3946 if (*path)
3948 DWORD needed = 0;
3949 HRESULT rc;
3951 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3952 if (rc != E_POINTER)
3953 needed = strlenW(path)+1;
3954 request->path = heap_alloc(needed*sizeof(WCHAR));
3955 rc = UrlEscapeW(path, request->path, &needed,
3956 URL_ESCAPE_SPACES_ONLY);
3957 if (rc != S_OK)
3959 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3960 strcpyW(request->path,path);
3964 /* Remove custom content-type/length headers on redirects. */
3965 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3966 if (0 <= index)
3967 HTTP_DeleteCustomHeader(request, index);
3968 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3969 if (0 <= index)
3970 HTTP_DeleteCustomHeader(request, index);
3972 return ERROR_SUCCESS;
3975 /***********************************************************************
3976 * HTTP_build_req (internal)
3978 * concatenate all the strings in the request together
3980 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3982 LPCWSTR *t;
3983 LPWSTR str;
3985 for( t = list; *t ; t++ )
3986 len += strlenW( *t );
3987 len++;
3989 str = heap_alloc(len*sizeof(WCHAR));
3990 *str = 0;
3992 for( t = list; *t ; t++ )
3993 strcatW( str, *t );
3995 return str;
3998 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
4000 server_t *server = request->server;
4001 LPWSTR requestString;
4002 INT len;
4003 INT cnt;
4004 INT responseLen;
4005 char *ascii_req;
4006 DWORD res;
4008 static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
4010 TRACE("\n");
4012 requestString = HTTP_BuildHeaderRequestString( request, connectW, server->host_port, g_szHttp1_1 );
4014 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4015 NULL, 0, NULL, NULL );
4016 len--; /* the nul terminator isn't needed */
4017 ascii_req = heap_alloc(len);
4018 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4019 heap_free( requestString );
4021 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4023 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4024 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4025 heap_free( ascii_req );
4026 if (res != ERROR_SUCCESS)
4027 return res;
4029 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4030 if (!responseLen)
4031 return ERROR_HTTP_INVALID_HEADER;
4033 return ERROR_SUCCESS;
4036 static void HTTP_InsertCookies(http_request_t *request)
4038 DWORD cookie_size, size, cnt = 0;
4039 HTTPHEADERW *host;
4040 WCHAR *cookies;
4042 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4044 host = HTTP_GetHeader(request, hostW);
4045 if(!host)
4046 return;
4048 if(get_cookie(host->lpszValue, request->path, NULL, &cookie_size) != ERROR_SUCCESS)
4049 return;
4051 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4052 if(!(cookies = heap_alloc(size)))
4053 return;
4055 cnt += sprintfW(cookies, cookieW);
4056 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4057 strcatW(cookies, szCrLf);
4059 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4061 heap_free(cookies);
4064 static WORD HTTP_ParseWkday(LPCWSTR day)
4066 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4067 { 'm','o','n',0 },
4068 { 't','u','e',0 },
4069 { 'w','e','d',0 },
4070 { 't','h','u',0 },
4071 { 'f','r','i',0 },
4072 { 's','a','t',0 }};
4073 unsigned int i;
4074 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4075 if (!strcmpiW(day, days[i]))
4076 return i;
4078 /* Invalid */
4079 return 7;
4082 static WORD HTTP_ParseMonth(LPCWSTR month)
4084 static const WCHAR jan[] = { 'j','a','n',0 };
4085 static const WCHAR feb[] = { 'f','e','b',0 };
4086 static const WCHAR mar[] = { 'm','a','r',0 };
4087 static const WCHAR apr[] = { 'a','p','r',0 };
4088 static const WCHAR may[] = { 'm','a','y',0 };
4089 static const WCHAR jun[] = { 'j','u','n',0 };
4090 static const WCHAR jul[] = { 'j','u','l',0 };
4091 static const WCHAR aug[] = { 'a','u','g',0 };
4092 static const WCHAR sep[] = { 's','e','p',0 };
4093 static const WCHAR oct[] = { 'o','c','t',0 };
4094 static const WCHAR nov[] = { 'n','o','v',0 };
4095 static const WCHAR dec[] = { 'd','e','c',0 };
4097 if (!strcmpiW(month, jan)) return 1;
4098 if (!strcmpiW(month, feb)) return 2;
4099 if (!strcmpiW(month, mar)) return 3;
4100 if (!strcmpiW(month, apr)) return 4;
4101 if (!strcmpiW(month, may)) return 5;
4102 if (!strcmpiW(month, jun)) return 6;
4103 if (!strcmpiW(month, jul)) return 7;
4104 if (!strcmpiW(month, aug)) return 8;
4105 if (!strcmpiW(month, sep)) return 9;
4106 if (!strcmpiW(month, oct)) return 10;
4107 if (!strcmpiW(month, nov)) return 11;
4108 if (!strcmpiW(month, dec)) return 12;
4109 /* Invalid */
4110 return 0;
4113 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4114 * optionally preceded by whitespace.
4115 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4116 * st, and sets *str to the first character after the time format.
4118 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4120 LPCWSTR ptr = *str;
4121 WCHAR *nextPtr;
4122 unsigned long num;
4124 while (isspaceW(*ptr))
4125 ptr++;
4127 num = strtoulW(ptr, &nextPtr, 10);
4128 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4130 ERR("unexpected time format %s\n", debugstr_w(ptr));
4131 return FALSE;
4133 if (num > 23)
4135 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4136 return FALSE;
4138 ptr = nextPtr + 1;
4139 st->wHour = (WORD)num;
4140 num = strtoulW(ptr, &nextPtr, 10);
4141 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4143 ERR("unexpected time format %s\n", debugstr_w(ptr));
4144 return FALSE;
4146 if (num > 59)
4148 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4149 return FALSE;
4151 ptr = nextPtr + 1;
4152 st->wMinute = (WORD)num;
4153 num = strtoulW(ptr, &nextPtr, 10);
4154 if (!nextPtr || nextPtr <= ptr)
4156 ERR("unexpected time format %s\n", debugstr_w(ptr));
4157 return FALSE;
4159 if (num > 59)
4161 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4162 return FALSE;
4164 ptr = nextPtr + 1;
4165 *str = ptr;
4166 st->wSecond = (WORD)num;
4167 return TRUE;
4170 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4172 static const WCHAR gmt[]= { 'G','M','T',0 };
4173 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4174 LPCWSTR ptr;
4175 SYSTEMTIME st = { 0 };
4176 unsigned long num;
4178 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4179 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4180 *dayPtr = *ptr;
4181 *dayPtr = 0;
4182 st.wDayOfWeek = HTTP_ParseWkday(day);
4183 if (st.wDayOfWeek >= 7)
4185 ERR("unexpected weekday %s\n", debugstr_w(day));
4186 return FALSE;
4189 while (isspaceW(*ptr))
4190 ptr++;
4192 for (monthPtr = month; !isspace(*ptr) &&
4193 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4194 monthPtr++, ptr++)
4195 *monthPtr = *ptr;
4196 *monthPtr = 0;
4197 st.wMonth = HTTP_ParseMonth(month);
4198 if (!st.wMonth || st.wMonth > 12)
4200 ERR("unexpected month %s\n", debugstr_w(month));
4201 return FALSE;
4204 while (isspaceW(*ptr))
4205 ptr++;
4207 num = strtoulW(ptr, &nextPtr, 10);
4208 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4210 ERR("unexpected day %s\n", debugstr_w(ptr));
4211 return FALSE;
4213 ptr = nextPtr;
4214 st.wDay = (WORD)num;
4216 while (isspaceW(*ptr))
4217 ptr++;
4219 if (!HTTP_ParseTime(&st, &ptr))
4220 return FALSE;
4222 while (isspaceW(*ptr))
4223 ptr++;
4225 num = strtoulW(ptr, &nextPtr, 10);
4226 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4228 ERR("unexpected year %s\n", debugstr_w(ptr));
4229 return FALSE;
4231 ptr = nextPtr;
4232 st.wYear = (WORD)num;
4234 while (isspaceW(*ptr))
4235 ptr++;
4237 /* asctime() doesn't report a timezone, but some web servers do, so accept
4238 * with or without GMT.
4240 if (*ptr && strcmpW(ptr, gmt))
4242 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4243 return FALSE;
4245 return SystemTimeToFileTime(&st, ft);
4248 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4250 static const WCHAR gmt[]= { 'G','M','T',0 };
4251 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4252 LPCWSTR ptr;
4253 unsigned long num;
4254 SYSTEMTIME st = { 0 };
4256 ptr = strchrW(value, ',');
4257 if (!ptr)
4258 return FALSE;
4259 if (ptr - value != 3)
4261 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4262 return FALSE;
4264 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4265 day[3] = 0;
4266 st.wDayOfWeek = HTTP_ParseWkday(day);
4267 if (st.wDayOfWeek > 6)
4269 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4270 return FALSE;
4272 ptr++;
4274 while (isspaceW(*ptr))
4275 ptr++;
4277 num = strtoulW(ptr, &nextPtr, 10);
4278 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4280 WARN("unexpected day %s\n", debugstr_w(value));
4281 return FALSE;
4283 ptr = nextPtr;
4284 st.wDay = (WORD)num;
4286 while (isspaceW(*ptr))
4287 ptr++;
4289 for (monthPtr = month; !isspace(*ptr) &&
4290 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4291 monthPtr++, ptr++)
4292 *monthPtr = *ptr;
4293 *monthPtr = 0;
4294 st.wMonth = HTTP_ParseMonth(month);
4295 if (!st.wMonth || st.wMonth > 12)
4297 WARN("unexpected month %s\n", debugstr_w(month));
4298 return FALSE;
4301 while (isspaceW(*ptr))
4302 ptr++;
4304 num = strtoulW(ptr, &nextPtr, 10);
4305 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4307 ERR("unexpected year %s\n", debugstr_w(value));
4308 return FALSE;
4310 ptr = nextPtr;
4311 st.wYear = (WORD)num;
4313 if (!HTTP_ParseTime(&st, &ptr))
4314 return FALSE;
4316 while (isspaceW(*ptr))
4317 ptr++;
4319 if (strcmpW(ptr, gmt))
4321 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4322 return FALSE;
4324 return SystemTimeToFileTime(&st, ft);
4327 static WORD HTTP_ParseWeekday(LPCWSTR day)
4329 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4330 { 'm','o','n','d','a','y',0 },
4331 { 't','u','e','s','d','a','y',0 },
4332 { 'w','e','d','n','e','s','d','a','y',0 },
4333 { 't','h','u','r','s','d','a','y',0 },
4334 { 'f','r','i','d','a','y',0 },
4335 { 's','a','t','u','r','d','a','y',0 }};
4336 unsigned int i;
4337 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4338 if (!strcmpiW(day, days[i]))
4339 return i;
4341 /* Invalid */
4342 return 7;
4345 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4347 static const WCHAR gmt[]= { 'G','M','T',0 };
4348 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4349 LPCWSTR ptr;
4350 unsigned long num;
4351 SYSTEMTIME st = { 0 };
4353 ptr = strchrW(value, ',');
4354 if (!ptr)
4355 return FALSE;
4356 if (ptr - value == 3)
4358 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4359 day[3] = 0;
4360 st.wDayOfWeek = HTTP_ParseWkday(day);
4361 if (st.wDayOfWeek > 6)
4363 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4364 return FALSE;
4367 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4369 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4370 day[ptr - value + 1] = 0;
4371 st.wDayOfWeek = HTTP_ParseWeekday(day);
4372 if (st.wDayOfWeek > 6)
4374 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4375 return FALSE;
4378 else
4380 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4381 return FALSE;
4383 ptr++;
4385 while (isspaceW(*ptr))
4386 ptr++;
4388 num = strtoulW(ptr, &nextPtr, 10);
4389 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4391 ERR("unexpected day %s\n", debugstr_w(value));
4392 return FALSE;
4394 ptr = nextPtr;
4395 st.wDay = (WORD)num;
4397 if (*ptr != '-')
4399 ERR("unexpected month format %s\n", debugstr_w(ptr));
4400 return FALSE;
4402 ptr++;
4404 for (monthPtr = month; *ptr != '-' &&
4405 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4406 monthPtr++, ptr++)
4407 *monthPtr = *ptr;
4408 *monthPtr = 0;
4409 st.wMonth = HTTP_ParseMonth(month);
4410 if (!st.wMonth || st.wMonth > 12)
4412 ERR("unexpected month %s\n", debugstr_w(month));
4413 return FALSE;
4416 if (*ptr != '-')
4418 ERR("unexpected year format %s\n", debugstr_w(ptr));
4419 return FALSE;
4421 ptr++;
4423 num = strtoulW(ptr, &nextPtr, 10);
4424 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4426 ERR("unexpected year %s\n", debugstr_w(value));
4427 return FALSE;
4429 ptr = nextPtr;
4430 st.wYear = (WORD)num;
4432 if (!HTTP_ParseTime(&st, &ptr))
4433 return FALSE;
4435 while (isspaceW(*ptr))
4436 ptr++;
4438 if (strcmpW(ptr, gmt))
4440 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4441 return FALSE;
4443 return SystemTimeToFileTime(&st, ft);
4446 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4448 static const WCHAR zero[] = { '0',0 };
4449 BOOL ret;
4451 if (!strcmpW(value, zero))
4453 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4454 ret = TRUE;
4456 else if (strchrW(value, ','))
4458 ret = HTTP_ParseRfc1123Date(value, ft);
4459 if (!ret)
4461 ret = HTTP_ParseRfc850Date(value, ft);
4462 if (!ret)
4463 ERR("unexpected date format %s\n", debugstr_w(value));
4466 else
4468 ret = HTTP_ParseDateAsAsctime(value, ft);
4469 if (!ret)
4470 ERR("unexpected date format %s\n", debugstr_w(value));
4472 return ret;
4475 static void HTTP_ProcessExpires(http_request_t *request)
4477 BOOL expirationFound = FALSE;
4478 int headerIndex;
4480 /* Look for a Cache-Control header with a max-age directive, as it takes
4481 * precedence over the Expires header.
4483 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4484 if (headerIndex != -1)
4486 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4487 LPWSTR ptr;
4489 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4491 LPWSTR comma = strchrW(ptr, ','), end, equal;
4493 if (comma)
4494 end = comma;
4495 else
4496 end = ptr + strlenW(ptr);
4497 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4499 if (*equal == '=')
4501 static const WCHAR max_age[] = {
4502 'm','a','x','-','a','g','e',0 };
4504 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4506 LPWSTR nextPtr;
4507 unsigned long age;
4509 age = strtoulW(equal + 1, &nextPtr, 10);
4510 if (nextPtr > equal + 1)
4512 LARGE_INTEGER ft;
4514 NtQuerySystemTime( &ft );
4515 /* Age is in seconds, FILETIME resolution is in
4516 * 100 nanosecond intervals.
4518 ft.QuadPart += age * (ULONGLONG)1000000;
4519 request->expires.dwLowDateTime = ft.u.LowPart;
4520 request->expires.dwHighDateTime = ft.u.HighPart;
4521 expirationFound = TRUE;
4525 if (comma)
4527 ptr = comma + 1;
4528 while (isspaceW(*ptr))
4529 ptr++;
4531 else
4532 ptr = NULL;
4535 if (!expirationFound)
4537 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4538 if (headerIndex != -1)
4540 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4541 FILETIME ft;
4543 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4545 expirationFound = TRUE;
4546 request->expires = ft;
4550 if (!expirationFound)
4552 LARGE_INTEGER t;
4554 /* With no known age, default to 10 minutes until expiration. */
4555 NtQuerySystemTime( &t );
4556 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4557 request->expires.dwLowDateTime = t.u.LowPart;
4558 request->expires.dwHighDateTime = t.u.HighPart;
4562 static void HTTP_ProcessLastModified(http_request_t *request)
4564 int headerIndex;
4566 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4567 if (headerIndex != -1)
4569 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4570 FILETIME ft;
4572 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4573 request->last_modified = ft;
4577 static void http_process_keep_alive(http_request_t *req)
4579 int index;
4581 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4582 if(index != -1)
4583 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4584 else
4585 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4588 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4590 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4591 netconn_t *netconn = NULL;
4592 DWORD res;
4594 assert(!request->netconn);
4595 reset_data_stream(request);
4597 res = HTTP_ResolveName(request);
4598 if(res != ERROR_SUCCESS)
4599 return res;
4601 EnterCriticalSection(&connection_pool_cs);
4603 while(!list_empty(&request->server->conn_pool)) {
4604 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4605 list_remove(&netconn->pool_entry);
4607 if(NETCON_is_alive(netconn))
4608 break;
4610 TRACE("connection %p closed during idle\n", netconn);
4611 free_netconn(netconn);
4612 netconn = NULL;
4615 LeaveCriticalSection(&connection_pool_cs);
4617 if(netconn) {
4618 TRACE("<-- reusing %p netconn\n", netconn);
4619 request->netconn = netconn;
4620 *reusing = TRUE;
4621 return ERROR_SUCCESS;
4624 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4625 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4627 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4628 INTERNET_STATUS_CONNECTING_TO_SERVER,
4629 request->server->addr_str,
4630 strlen(request->server->addr_str)+1);
4632 res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4633 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4634 request->connect_timeout, &netconn);
4635 if(res != ERROR_SUCCESS) {
4636 ERR("create_netconn failed: %u\n", res);
4637 return res;
4640 request->netconn = netconn;
4642 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4643 INTERNET_STATUS_CONNECTED_TO_SERVER,
4644 request->server->addr_str, strlen(request->server->addr_str)+1);
4646 if(is_https) {
4647 /* Note: we differ from Microsoft's WinINet here. they seem to have
4648 * a bug that causes no status callbacks to be sent when starting
4649 * a tunnel to a proxy server using the CONNECT verb. i believe our
4650 * behaviour to be more correct and to not cause any incompatibilities
4651 * because using a secure connection through a proxy server is a rare
4652 * case that would be hard for anyone to depend on */
4653 if(request->proxy)
4654 res = HTTP_SecureProxyConnect(request);
4655 if(res == ERROR_SUCCESS)
4656 res = NETCON_secure_connect(request->netconn, request->server);
4659 if(res != ERROR_SUCCESS) {
4660 http_release_netconn(request, FALSE);
4661 return res;
4664 *reusing = FALSE;
4665 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4666 return ERROR_SUCCESS;
4669 /***********************************************************************
4670 * HTTP_HttpSendRequestW (internal)
4672 * Sends the specified request to the HTTP server
4674 * RETURNS
4675 * ERROR_SUCCESS on success
4676 * win32 error code on failure
4679 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4680 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4681 DWORD dwContentLength, BOOL bEndRequest)
4683 INT cnt;
4684 BOOL redirected = FALSE;
4685 LPWSTR requestString = NULL;
4686 INT responseLen;
4687 BOOL loop_next;
4688 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4689 static const WCHAR szContentLength[] =
4690 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4691 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4692 DWORD res;
4694 TRACE("--> %p\n", request);
4696 assert(request->hdr.htype == WH_HHTTPREQ);
4698 /* if the verb is NULL default to GET */
4699 if (!request->verb)
4700 request->verb = heap_strdupW(szGET);
4702 if (dwContentLength || strcmpW(request->verb, szGET))
4704 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4705 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4706 request->bytesToWrite = dwContentLength;
4708 if (request->session->appInfo->agent)
4710 WCHAR *agent_header;
4711 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4712 int len;
4714 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4715 agent_header = heap_alloc(len * sizeof(WCHAR));
4716 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4718 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4719 heap_free(agent_header);
4721 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4723 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4724 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4726 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4728 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4729 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4730 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4733 /* add the headers the caller supplied */
4734 if( lpszHeaders && dwHeaderLength )
4735 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4739 DWORD len;
4740 BOOL reusing_connection;
4741 char *ascii_req;
4743 loop_next = FALSE;
4744 reusing_connection = request->netconn != NULL;
4746 if(redirected) {
4747 request->contentLength = ~0u;
4748 request->bytesToWrite = 0;
4751 if (TRACE_ON(wininet))
4753 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4754 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4757 HTTP_FixURL(request);
4758 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4760 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4762 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4763 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4765 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4766 HTTP_InsertCookies(request);
4768 if (request->proxy)
4770 WCHAR *url = build_proxy_path_url(request);
4771 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4772 heap_free(url);
4774 else
4775 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4778 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4780 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4781 break;
4783 /* send the request as ASCII, tack on the optional data */
4784 if (!lpOptional || redirected)
4785 dwOptionalLength = 0;
4786 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4787 NULL, 0, NULL, NULL );
4788 ascii_req = heap_alloc(len + dwOptionalLength);
4789 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4790 ascii_req, len, NULL, NULL );
4791 if( lpOptional )
4792 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4793 len = (len + dwOptionalLength - 1);
4794 ascii_req[len] = 0;
4795 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4797 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4798 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4800 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4801 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4802 heap_free( ascii_req );
4803 if(res != ERROR_SUCCESS) {
4804 TRACE("send failed: %u\n", res);
4805 if(!reusing_connection)
4806 break;
4807 http_release_netconn(request, FALSE);
4808 loop_next = TRUE;
4809 continue;
4812 request->bytesWritten = dwOptionalLength;
4814 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4815 INTERNET_STATUS_REQUEST_SENT,
4816 &len, sizeof(DWORD));
4818 if (bEndRequest)
4820 DWORD dwBufferSize;
4822 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4823 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4825 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4826 /* FIXME: We should know that connection is closed before sending
4827 * headers. Otherwise wrong callbacks are executed */
4828 if(!responseLen && reusing_connection) {
4829 TRACE("Connection closed by server, reconnecting\n");
4830 http_release_netconn(request, FALSE);
4831 loop_next = TRUE;
4832 continue;
4835 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4836 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4837 sizeof(DWORD));
4839 http_process_keep_alive(request);
4840 HTTP_ProcessCookies(request);
4841 HTTP_ProcessExpires(request);
4842 HTTP_ProcessLastModified(request);
4844 res = set_content_length(request);
4845 if(res != ERROR_SUCCESS)
4846 goto lend;
4847 if(!request->contentLength)
4848 http_release_netconn(request, TRUE);
4850 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4852 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4853 dwBufferSize=sizeof(szNewLocation);
4854 switch(request->status_code) {
4855 case HTTP_STATUS_REDIRECT:
4856 case HTTP_STATUS_MOVED:
4857 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4858 case HTTP_STATUS_REDIRECT_METHOD:
4859 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4860 break;
4862 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4863 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4865 heap_free(request->verb);
4866 request->verb = heap_strdupW(szGET);
4868 http_release_netconn(request, drain_content(request, FALSE));
4869 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4871 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4872 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4873 res = HTTP_HandleRedirect(request, new_url);
4874 if (res == ERROR_SUCCESS)
4876 heap_free(requestString);
4877 loop_next = TRUE;
4879 heap_free( new_url );
4881 redirected = TRUE;
4884 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4886 WCHAR szAuthValue[2048];
4887 dwBufferSize=2048;
4888 if (request->status_code == HTTP_STATUS_DENIED)
4890 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4891 DWORD dwIndex = 0;
4892 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4894 if (HTTP_DoAuthorization(request, szAuthValue,
4895 &request->authInfo,
4896 request->session->userName,
4897 request->session->password,
4898 Host->lpszValue))
4900 heap_free(requestString);
4901 if(!drain_content(request, TRUE)) {
4902 FIXME("Could not drain content\n");
4903 http_release_netconn(request, FALSE);
4905 loop_next = TRUE;
4906 break;
4910 if(!loop_next) {
4911 TRACE("Cleaning wrong authorization data\n");
4912 destroy_authinfo(request->authInfo);
4913 request->authInfo = NULL;
4916 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4918 DWORD dwIndex = 0;
4919 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4921 if (HTTP_DoAuthorization(request, szAuthValue,
4922 &request->proxyAuthInfo,
4923 request->session->appInfo->proxyUsername,
4924 request->session->appInfo->proxyPassword,
4925 NULL))
4927 heap_free(requestString);
4928 if(!drain_content(request, TRUE)) {
4929 FIXME("Could not drain content\n");
4930 http_release_netconn(request, FALSE);
4932 loop_next = TRUE;
4933 break;
4937 if(!loop_next) {
4938 TRACE("Cleaning wrong proxy authorization data\n");
4939 destroy_authinfo(request->proxyAuthInfo);
4940 request->proxyAuthInfo = NULL;
4945 else
4946 res = ERROR_SUCCESS;
4948 while (loop_next);
4950 lend:
4951 heap_free(requestString);
4953 /* TODO: send notification for P3P header */
4955 if(res == ERROR_SUCCESS)
4956 create_cache_entry(request);
4958 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4960 if (res == ERROR_SUCCESS) {
4961 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4962 HTTP_ReceiveRequestData(request, TRUE, NULL);
4963 else
4964 send_request_complete(request,
4965 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4966 }else {
4967 send_request_complete(request, 0, res);
4971 TRACE("<--\n");
4972 return res;
4975 typedef struct {
4976 task_header_t hdr;
4977 WCHAR *headers;
4978 DWORD headers_len;
4979 void *optional;
4980 DWORD optional_len;
4981 DWORD content_len;
4982 BOOL end_request;
4983 } send_request_task_t;
4985 /***********************************************************************
4987 * Helper functions for the HttpSendRequest(Ex) functions
4990 static void AsyncHttpSendRequestProc(task_header_t *hdr)
4992 send_request_task_t *task = (send_request_task_t*)hdr;
4993 http_request_t *request = (http_request_t*)task->hdr.hdr;
4995 TRACE("%p\n", request);
4997 HTTP_HttpSendRequestW(request, task->headers, task->headers_len, task->optional,
4998 task->optional_len, task->content_len, task->end_request);
5000 heap_free(task->headers);
5004 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5006 DWORD dwBufferSize;
5007 INT responseLen;
5008 DWORD res = ERROR_SUCCESS;
5010 if(!request->netconn) {
5011 WARN("Not connected\n");
5012 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5013 return ERROR_INTERNET_OPERATION_CANCELLED;
5016 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5017 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5019 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5020 if (!responseLen)
5021 res = ERROR_HTTP_HEADER_NOT_FOUND;
5023 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5024 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5026 /* process cookies here. Is this right? */
5027 http_process_keep_alive(request);
5028 HTTP_ProcessCookies(request);
5029 HTTP_ProcessExpires(request);
5030 HTTP_ProcessLastModified(request);
5032 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5033 if(!request->contentLength)
5034 http_release_netconn(request, TRUE);
5037 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5039 switch(request->status_code) {
5040 case HTTP_STATUS_REDIRECT:
5041 case HTTP_STATUS_MOVED:
5042 case HTTP_STATUS_REDIRECT_METHOD:
5043 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5044 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5045 dwBufferSize=sizeof(szNewLocation);
5046 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5047 break;
5049 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5050 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5052 heap_free(request->verb);
5053 request->verb = heap_strdupW(szGET);
5055 http_release_netconn(request, drain_content(request, FALSE));
5056 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5058 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5059 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5060 res = HTTP_HandleRedirect(request, new_url);
5061 if (res == ERROR_SUCCESS)
5062 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5063 heap_free( new_url );
5069 if(res == ERROR_SUCCESS)
5070 create_cache_entry(request);
5072 if (res == ERROR_SUCCESS && request->contentLength)
5073 HTTP_ReceiveRequestData(request, TRUE, NULL);
5074 else
5075 send_request_complete(request, res == ERROR_SUCCESS, res);
5077 return res;
5080 /***********************************************************************
5081 * HttpEndRequestA (WININET.@)
5083 * Ends an HTTP request that was started by HttpSendRequestEx
5085 * RETURNS
5086 * TRUE if successful
5087 * FALSE on failure
5090 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5091 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5093 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5095 if (lpBuffersOut)
5097 SetLastError(ERROR_INVALID_PARAMETER);
5098 return FALSE;
5101 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5104 typedef struct {
5105 task_header_t hdr;
5106 DWORD flags;
5107 DWORD context;
5108 } end_request_task_t;
5110 static void AsyncHttpEndRequestProc(task_header_t *hdr)
5112 end_request_task_t *task = (end_request_task_t*)hdr;
5113 http_request_t *req = (http_request_t*)task->hdr.hdr;
5115 TRACE("%p\n", req);
5117 HTTP_HttpEndRequestW(req, task->flags, task->context);
5120 /***********************************************************************
5121 * HttpEndRequestW (WININET.@)
5123 * Ends an HTTP request that was started by HttpSendRequestEx
5125 * RETURNS
5126 * TRUE if successful
5127 * FALSE on failure
5130 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5131 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5133 http_request_t *request;
5134 DWORD res;
5136 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5138 if (lpBuffersOut)
5140 SetLastError(ERROR_INVALID_PARAMETER);
5141 return FALSE;
5144 request = (http_request_t*) get_handle_object( hRequest );
5146 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5148 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5149 if (request)
5150 WININET_Release( &request->hdr );
5151 return FALSE;
5153 request->hdr.dwFlags |= dwFlags;
5155 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5157 end_request_task_t *task;
5159 task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task));
5160 task->flags = dwFlags;
5161 task->context = dwContext;
5163 INTERNET_AsyncCall(&task->hdr);
5164 res = ERROR_IO_PENDING;
5166 else
5167 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5169 WININET_Release( &request->hdr );
5170 TRACE("%u <--\n", res);
5171 if(res != ERROR_SUCCESS)
5172 SetLastError(res);
5173 return res == ERROR_SUCCESS;
5176 /***********************************************************************
5177 * HttpSendRequestExA (WININET.@)
5179 * Sends the specified request to the HTTP server and allows chunked
5180 * transfers.
5182 * RETURNS
5183 * Success: TRUE
5184 * Failure: FALSE, call GetLastError() for more information.
5186 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5187 LPINTERNET_BUFFERSA lpBuffersIn,
5188 LPINTERNET_BUFFERSA lpBuffersOut,
5189 DWORD dwFlags, DWORD_PTR dwContext)
5191 INTERNET_BUFFERSW BuffersInW;
5192 BOOL rc = FALSE;
5193 DWORD headerlen;
5194 LPWSTR header = NULL;
5196 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5197 lpBuffersOut, dwFlags, dwContext);
5199 if (lpBuffersIn)
5201 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5202 if (lpBuffersIn->lpcszHeader)
5204 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5205 lpBuffersIn->dwHeadersLength,0,0);
5206 header = heap_alloc(headerlen*sizeof(WCHAR));
5207 if (!(BuffersInW.lpcszHeader = header))
5209 SetLastError(ERROR_OUTOFMEMORY);
5210 return FALSE;
5212 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5213 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5214 header, headerlen);
5216 else
5217 BuffersInW.lpcszHeader = NULL;
5218 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5219 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5220 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5221 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5222 BuffersInW.Next = NULL;
5225 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5227 heap_free(header);
5228 return rc;
5231 /***********************************************************************
5232 * HttpSendRequestExW (WININET.@)
5234 * Sends the specified request to the HTTP server and allows chunked
5235 * transfers
5237 * RETURNS
5238 * Success: TRUE
5239 * Failure: FALSE, call GetLastError() for more information.
5241 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5242 LPINTERNET_BUFFERSW lpBuffersIn,
5243 LPINTERNET_BUFFERSW lpBuffersOut,
5244 DWORD dwFlags, DWORD_PTR dwContext)
5246 http_request_t *request;
5247 http_session_t *session;
5248 appinfo_t *hIC;
5249 DWORD res;
5251 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5252 lpBuffersOut, dwFlags, dwContext);
5254 request = (http_request_t*) get_handle_object( hRequest );
5256 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5258 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5259 goto lend;
5262 session = request->session;
5263 assert(session->hdr.htype == WH_HHTTPSESSION);
5264 hIC = session->appInfo;
5265 assert(hIC->hdr.htype == WH_HINIT);
5267 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5269 send_request_task_t *task;
5271 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5272 if (lpBuffersIn)
5274 DWORD size = 0;
5276 if (lpBuffersIn->lpcszHeader)
5278 if (lpBuffersIn->dwHeadersLength == ~0u)
5279 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5280 else
5281 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5283 task->headers = heap_alloc(size);
5284 memcpy(task->headers, lpBuffersIn->lpcszHeader, size);
5286 else task->headers = NULL;
5288 task->headers_len = size / sizeof(WCHAR);
5289 task->optional = lpBuffersIn->lpvBuffer;
5290 task->optional_len = lpBuffersIn->dwBufferLength;
5291 task->content_len = lpBuffersIn->dwBufferTotal;
5293 else
5295 task->headers = NULL;
5296 task->headers_len = 0;
5297 task->optional = NULL;
5298 task->optional_len = 0;
5299 task->content_len = 0;
5302 task->end_request = FALSE;
5304 INTERNET_AsyncCall(&task->hdr);
5305 res = ERROR_IO_PENDING;
5307 else
5309 if (lpBuffersIn)
5310 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5311 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5312 lpBuffersIn->dwBufferTotal, FALSE);
5313 else
5314 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5317 lend:
5318 if ( request )
5319 WININET_Release( &request->hdr );
5321 TRACE("<---\n");
5322 SetLastError(res);
5323 return res == ERROR_SUCCESS;
5326 /***********************************************************************
5327 * HttpSendRequestW (WININET.@)
5329 * Sends the specified request to the HTTP server
5331 * RETURNS
5332 * TRUE on success
5333 * FALSE on failure
5336 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5337 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5339 http_request_t *request;
5340 http_session_t *session = NULL;
5341 appinfo_t *hIC = NULL;
5342 DWORD res = ERROR_SUCCESS;
5344 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5345 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5347 request = (http_request_t*) get_handle_object( hHttpRequest );
5348 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5350 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5351 goto lend;
5354 session = request->session;
5355 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5357 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5358 goto lend;
5361 hIC = session->appInfo;
5362 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5364 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5365 goto lend;
5368 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5370 send_request_task_t *task;
5372 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5373 if (lpszHeaders)
5375 DWORD size;
5377 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5378 else size = dwHeaderLength * sizeof(WCHAR);
5380 task->headers = heap_alloc(size);
5381 memcpy(task->headers, lpszHeaders, size);
5383 else
5384 task->headers = NULL;
5385 task->headers_len = dwHeaderLength;
5386 task->optional = lpOptional;
5387 task->optional_len = dwOptionalLength;
5388 task->content_len = dwOptionalLength;
5389 task->end_request = TRUE;
5391 INTERNET_AsyncCall(&task->hdr);
5392 res = ERROR_IO_PENDING;
5394 else
5396 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5397 dwHeaderLength, lpOptional, dwOptionalLength,
5398 dwOptionalLength, TRUE);
5400 lend:
5401 if( request )
5402 WININET_Release( &request->hdr );
5404 SetLastError(res);
5405 return res == ERROR_SUCCESS;
5408 /***********************************************************************
5409 * HttpSendRequestA (WININET.@)
5411 * Sends the specified request to the HTTP server
5413 * RETURNS
5414 * TRUE on success
5415 * FALSE on failure
5418 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5419 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5421 BOOL result;
5422 LPWSTR szHeaders=NULL;
5423 DWORD nLen=dwHeaderLength;
5424 if(lpszHeaders!=NULL)
5426 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5427 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5428 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5430 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5431 heap_free(szHeaders);
5432 return result;
5435 /***********************************************************************
5436 * HTTPSESSION_Destroy (internal)
5438 * Deallocate session handle
5441 static void HTTPSESSION_Destroy(object_header_t *hdr)
5443 http_session_t *session = (http_session_t*) hdr;
5445 TRACE("%p\n", session);
5447 WININET_Release(&session->appInfo->hdr);
5449 heap_free(session->hostName);
5450 heap_free(session->password);
5451 heap_free(session->userName);
5454 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5456 http_session_t *ses = (http_session_t *)hdr;
5458 switch(option) {
5459 case INTERNET_OPTION_HANDLE_TYPE:
5460 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5462 if (*size < sizeof(ULONG))
5463 return ERROR_INSUFFICIENT_BUFFER;
5465 *size = sizeof(DWORD);
5466 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5467 return ERROR_SUCCESS;
5468 case INTERNET_OPTION_CONNECT_TIMEOUT:
5469 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5471 if (*size < sizeof(DWORD))
5472 return ERROR_INSUFFICIENT_BUFFER;
5474 *size = sizeof(DWORD);
5475 *(DWORD *)buffer = ses->connect_timeout;
5476 return ERROR_SUCCESS;
5478 case INTERNET_OPTION_SEND_TIMEOUT:
5479 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5481 if (*size < sizeof(DWORD))
5482 return ERROR_INSUFFICIENT_BUFFER;
5484 *size = sizeof(DWORD);
5485 *(DWORD *)buffer = ses->send_timeout;
5486 return ERROR_SUCCESS;
5488 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5489 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5491 if (*size < sizeof(DWORD))
5492 return ERROR_INSUFFICIENT_BUFFER;
5494 *size = sizeof(DWORD);
5495 *(DWORD *)buffer = ses->receive_timeout;
5496 return ERROR_SUCCESS;
5499 return INET_QueryOption(hdr, option, buffer, size, unicode);
5502 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5504 http_session_t *ses = (http_session_t*)hdr;
5506 switch(option) {
5507 case INTERNET_OPTION_USERNAME:
5509 heap_free(ses->userName);
5510 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5511 return ERROR_SUCCESS;
5513 case INTERNET_OPTION_PASSWORD:
5515 heap_free(ses->password);
5516 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5517 return ERROR_SUCCESS;
5519 case INTERNET_OPTION_CONNECT_TIMEOUT:
5521 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5522 ses->connect_timeout = *(DWORD *)buffer;
5523 return ERROR_SUCCESS;
5525 case INTERNET_OPTION_SEND_TIMEOUT:
5527 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5528 ses->send_timeout = *(DWORD *)buffer;
5529 return ERROR_SUCCESS;
5531 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5533 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5534 ses->receive_timeout = *(DWORD *)buffer;
5535 return ERROR_SUCCESS;
5537 default: break;
5540 return INET_SetOption(hdr, option, buffer, size);
5543 static const object_vtbl_t HTTPSESSIONVtbl = {
5544 HTTPSESSION_Destroy,
5545 NULL,
5546 HTTPSESSION_QueryOption,
5547 HTTPSESSION_SetOption,
5548 NULL,
5549 NULL,
5550 NULL,
5551 NULL,
5552 NULL
5556 /***********************************************************************
5557 * HTTP_Connect (internal)
5559 * Create http session handle
5561 * RETURNS
5562 * HINTERNET a session handle on success
5563 * NULL on failure
5566 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5567 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5568 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5569 DWORD dwInternalFlags, HINTERNET *ret)
5571 http_session_t *session = NULL;
5573 TRACE("-->\n");
5575 if (!lpszServerName || !lpszServerName[0])
5576 return ERROR_INVALID_PARAMETER;
5578 assert( hIC->hdr.htype == WH_HINIT );
5580 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5581 if (!session)
5582 return ERROR_OUTOFMEMORY;
5585 * According to my tests. The name is not resolved until a request is sent
5588 session->hdr.htype = WH_HHTTPSESSION;
5589 session->hdr.dwFlags = dwFlags;
5590 session->hdr.dwContext = dwContext;
5591 session->hdr.dwInternalFlags |= dwInternalFlags;
5593 WININET_AddRef( &hIC->hdr );
5594 session->appInfo = hIC;
5595 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5597 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5598 if(hIC->proxyBypass)
5599 FIXME("Proxy bypass is ignored.\n");
5601 session->hostName = heap_strdupW(lpszServerName);
5602 if (lpszUserName && lpszUserName[0])
5603 session->userName = heap_strdupW(lpszUserName);
5604 if (lpszPassword && lpszPassword[0])
5605 session->password = heap_strdupW(lpszPassword);
5606 session->hostPort = serverPort;
5607 session->connect_timeout = hIC->connect_timeout;
5608 session->send_timeout = INFINITE;
5609 session->receive_timeout = INFINITE;
5611 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5612 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5614 INTERNET_SendCallback(&hIC->hdr, dwContext,
5615 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5616 sizeof(HINTERNET));
5620 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5621 * windows
5624 TRACE("%p --> %p\n", hIC, session);
5626 *ret = session->hdr.hInternet;
5627 return ERROR_SUCCESS;
5630 /***********************************************************************
5631 * HTTP_clear_response_headers (internal)
5633 * clear out any old response headers
5635 static void HTTP_clear_response_headers( http_request_t *request )
5637 DWORD i;
5639 for( i=0; i<request->nCustHeaders; i++)
5641 if( !request->custHeaders[i].lpszField )
5642 continue;
5643 if( !request->custHeaders[i].lpszValue )
5644 continue;
5645 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5646 continue;
5647 HTTP_DeleteCustomHeader( request, i );
5648 i--;
5652 /***********************************************************************
5653 * HTTP_GetResponseHeaders (internal)
5655 * Read server response
5657 * RETURNS
5659 * TRUE on success
5660 * FALSE on error
5662 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5664 INT cbreaks = 0;
5665 WCHAR buffer[MAX_REPLY_LEN];
5666 DWORD buflen = MAX_REPLY_LEN;
5667 BOOL bSuccess = FALSE;
5668 INT rc = 0;
5669 char bufferA[MAX_REPLY_LEN];
5670 LPWSTR status_code = NULL, status_text = NULL;
5671 DWORD cchMaxRawHeaders = 1024;
5672 LPWSTR lpszRawHeaders = NULL;
5673 LPWSTR temp;
5674 DWORD cchRawHeaders = 0;
5675 BOOL codeHundred = FALSE;
5677 TRACE("-->\n");
5679 if(!request->netconn)
5680 goto lend;
5682 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5683 do {
5685 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5687 buflen = MAX_REPLY_LEN;
5688 if (!read_line(request, bufferA, &buflen))
5689 goto lend;
5691 /* clear old response headers (eg. from a redirect response) */
5692 if (clear) {
5693 HTTP_clear_response_headers( request );
5694 clear = FALSE;
5697 rc += buflen;
5698 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5699 /* check is this a status code line? */
5700 if (!strncmpW(buffer, g_szHttp1_0, 4))
5702 /* split the version from the status code */
5703 status_code = strchrW( buffer, ' ' );
5704 if( !status_code )
5705 goto lend;
5706 *status_code++=0;
5708 /* split the status code from the status text */
5709 status_text = strchrW( status_code, ' ' );
5710 if( !status_text )
5711 goto lend;
5712 *status_text++=0;
5714 request->status_code = atoiW(status_code);
5716 TRACE("version [%s] status code [%s] status text [%s]\n",
5717 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5719 codeHundred = request->status_code == HTTP_STATUS_CONTINUE;
5721 else if (!codeHundred)
5723 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5725 heap_free(request->version);
5726 heap_free(request->statusText);
5728 request->status_code = HTTP_STATUS_OK;
5729 request->version = heap_strdupW(g_szHttp1_0);
5730 request->statusText = heap_strdupW(szOK);
5732 heap_free(request->rawHeaders);
5733 request->rawHeaders = heap_strdupW(szDefaultHeader);
5735 bSuccess = TRUE;
5736 goto lend;
5738 } while (codeHundred);
5740 /* Add status code */
5741 HTTP_ProcessHeader(request, szStatus, status_code,
5742 HTTP_ADDHDR_FLAG_REPLACE);
5744 heap_free(request->version);
5745 heap_free(request->statusText);
5747 request->version = heap_strdupW(buffer);
5748 request->statusText = heap_strdupW(status_text);
5750 /* Restore the spaces */
5751 *(status_code-1) = ' ';
5752 *(status_text-1) = ' ';
5754 /* regenerate raw headers */
5755 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5756 if (!lpszRawHeaders) goto lend;
5758 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5759 cchMaxRawHeaders *= 2;
5760 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5761 if (temp == NULL) goto lend;
5762 lpszRawHeaders = temp;
5763 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5764 cchRawHeaders += (buflen-1);
5765 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5766 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5767 lpszRawHeaders[cchRawHeaders] = '\0';
5769 /* Parse each response line */
5772 buflen = MAX_REPLY_LEN;
5773 if (read_line(request, bufferA, &buflen))
5775 LPWSTR * pFieldAndValue;
5777 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5779 if (!bufferA[0]) break;
5780 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5782 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5783 if (pFieldAndValue)
5785 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5786 cchMaxRawHeaders *= 2;
5787 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5788 if (temp == NULL) goto lend;
5789 lpszRawHeaders = temp;
5790 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5791 cchRawHeaders += (buflen-1);
5792 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5793 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5794 lpszRawHeaders[cchRawHeaders] = '\0';
5796 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5797 HTTP_ADDREQ_FLAG_ADD );
5799 HTTP_FreeTokens(pFieldAndValue);
5802 else
5804 cbreaks++;
5805 if (cbreaks >= 2)
5806 break;
5808 }while(1);
5810 /* make sure the response header is terminated with an empty line. Some apps really
5811 truly care about that empty line being there for some reason. Just add it to the
5812 header. */
5813 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5815 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5816 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5817 if (temp == NULL) goto lend;
5818 lpszRawHeaders = temp;
5821 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5823 heap_free(request->rawHeaders);
5824 request->rawHeaders = lpszRawHeaders;
5825 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5826 bSuccess = TRUE;
5828 lend:
5830 TRACE("<--\n");
5831 if (bSuccess)
5832 return rc;
5833 else
5835 heap_free(lpszRawHeaders);
5836 return 0;
5840 /***********************************************************************
5841 * HTTP_InterpretHttpHeader (internal)
5843 * Parse server response
5845 * RETURNS
5847 * Pointer to array of field, value, NULL on success.
5848 * NULL on error.
5850 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5852 LPWSTR * pTokenPair;
5853 LPWSTR pszColon;
5854 INT len;
5856 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5858 pszColon = strchrW(buffer, ':');
5859 /* must have two tokens */
5860 if (!pszColon)
5862 HTTP_FreeTokens(pTokenPair);
5863 if (buffer[0])
5864 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5865 return NULL;
5868 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5869 if (!pTokenPair[0])
5871 HTTP_FreeTokens(pTokenPair);
5872 return NULL;
5874 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5875 pTokenPair[0][pszColon - buffer] = '\0';
5877 /* skip colon */
5878 pszColon++;
5879 len = strlenW(pszColon);
5880 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5881 if (!pTokenPair[1])
5883 HTTP_FreeTokens(pTokenPair);
5884 return NULL;
5886 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5888 strip_spaces(pTokenPair[0]);
5889 strip_spaces(pTokenPair[1]);
5891 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5892 return pTokenPair;
5895 /***********************************************************************
5896 * HTTP_ProcessHeader (internal)
5898 * Stuff header into header tables according to <dwModifier>
5902 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5904 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5906 LPHTTPHEADERW lphttpHdr = NULL;
5907 INT index = -1;
5908 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5909 DWORD res = ERROR_HTTP_INVALID_HEADER;
5911 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5913 /* REPLACE wins out over ADD */
5914 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5915 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5917 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5918 index = -1;
5919 else
5920 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5922 if (index >= 0)
5924 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5925 return ERROR_HTTP_INVALID_HEADER;
5926 lphttpHdr = &request->custHeaders[index];
5928 else if (value)
5930 HTTPHEADERW hdr;
5932 hdr.lpszField = (LPWSTR)field;
5933 hdr.lpszValue = (LPWSTR)value;
5934 hdr.wFlags = hdr.wCount = 0;
5936 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5937 hdr.wFlags |= HDR_ISREQUEST;
5939 return HTTP_InsertCustomHeader(request, &hdr);
5941 /* no value to delete */
5942 else return ERROR_SUCCESS;
5944 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5945 lphttpHdr->wFlags |= HDR_ISREQUEST;
5946 else
5947 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5949 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5951 HTTP_DeleteCustomHeader( request, index );
5953 if (value)
5955 HTTPHEADERW hdr;
5957 hdr.lpszField = (LPWSTR)field;
5958 hdr.lpszValue = (LPWSTR)value;
5959 hdr.wFlags = hdr.wCount = 0;
5961 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5962 hdr.wFlags |= HDR_ISREQUEST;
5964 return HTTP_InsertCustomHeader(request, &hdr);
5967 return ERROR_SUCCESS;
5969 else if (dwModifier & COALESCEFLAGS)
5971 LPWSTR lpsztmp;
5972 WCHAR ch = 0;
5973 INT len = 0;
5974 INT origlen = strlenW(lphttpHdr->lpszValue);
5975 INT valuelen = strlenW(value);
5977 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5979 ch = ',';
5980 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5982 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5984 ch = ';';
5985 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5988 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5990 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5991 if (lpsztmp)
5993 lphttpHdr->lpszValue = lpsztmp;
5994 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5995 if (ch > 0)
5997 lphttpHdr->lpszValue[origlen] = ch;
5998 origlen++;
5999 lphttpHdr->lpszValue[origlen] = ' ';
6000 origlen++;
6003 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6004 lphttpHdr->lpszValue[len] = '\0';
6005 res = ERROR_SUCCESS;
6007 else
6009 WARN("heap_realloc (%d bytes) failed\n",len+1);
6010 res = ERROR_OUTOFMEMORY;
6013 TRACE("<-- %d\n", res);
6014 return res;
6017 /***********************************************************************
6018 * HTTP_GetCustomHeaderIndex (internal)
6020 * Return index of custom header from header array
6023 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6024 int requested_index, BOOL request_only)
6026 DWORD index;
6028 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6030 for (index = 0; index < request->nCustHeaders; index++)
6032 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6033 continue;
6035 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6036 continue;
6038 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6039 continue;
6041 if (requested_index == 0)
6042 break;
6043 requested_index --;
6046 if (index >= request->nCustHeaders)
6047 index = -1;
6049 TRACE("Return: %d\n", index);
6050 return index;
6054 /***********************************************************************
6055 * HTTP_InsertCustomHeader (internal)
6057 * Insert header into array
6060 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6062 INT count;
6063 LPHTTPHEADERW lph = NULL;
6065 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6066 count = request->nCustHeaders + 1;
6067 if (count > 1)
6068 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6069 else
6070 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6072 if (!lph)
6073 return ERROR_OUTOFMEMORY;
6075 request->custHeaders = lph;
6076 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6077 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6078 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6079 request->custHeaders[count-1].wCount= lpHdr->wCount;
6080 request->nCustHeaders++;
6082 return ERROR_SUCCESS;
6086 /***********************************************************************
6087 * HTTP_DeleteCustomHeader (internal)
6089 * Delete header from array
6090 * If this function is called, the indexs may change.
6092 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6094 if( request->nCustHeaders <= 0 )
6095 return FALSE;
6096 if( index >= request->nCustHeaders )
6097 return FALSE;
6098 request->nCustHeaders--;
6100 heap_free(request->custHeaders[index].lpszField);
6101 heap_free(request->custHeaders[index].lpszValue);
6103 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6104 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6105 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6107 return TRUE;
6111 /***********************************************************************
6112 * HTTP_VerifyValidHeader (internal)
6114 * Verify the given header is not invalid for the given http request
6117 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6119 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6120 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6121 return ERROR_HTTP_INVALID_HEADER;
6123 return ERROR_SUCCESS;
6126 /***********************************************************************
6127 * IsHostInProxyBypassList (@)
6129 * Undocumented
6132 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6134 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6135 return FALSE;