wininet: Don't strip the port from URLs returned by InternetQueryOption(INTERNET_OPTI...
[wine/multimedia.git] / dlls / wininet / http.c
blobdab5dc45010f160c6d4db6fe230e5fee10ea9e9b
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 strcatW(url, req->path);
2025 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2027 if(unicode) {
2028 len = (strlenW(url)+1) * sizeof(WCHAR);
2029 if(*size < len)
2030 return ERROR_INSUFFICIENT_BUFFER;
2032 *size = len;
2033 strcpyW(buffer, url);
2034 return ERROR_SUCCESS;
2035 }else {
2036 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2037 if(len > *size)
2038 return ERROR_INSUFFICIENT_BUFFER;
2040 *size = len;
2041 return ERROR_SUCCESS;
2045 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2046 INTERNET_CACHE_ENTRY_INFOW *info;
2047 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2048 WCHAR url[INTERNET_MAX_URL_LENGTH];
2049 DWORD nbytes, error;
2050 BOOL ret;
2052 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2054 if (*size < sizeof(*ts))
2056 *size = sizeof(*ts);
2057 return ERROR_INSUFFICIENT_BUFFER;
2059 nbytes = 0;
2060 HTTP_GetRequestURL(req, url);
2061 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2062 error = GetLastError();
2063 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2065 if (!(info = heap_alloc(nbytes)))
2066 return ERROR_OUTOFMEMORY;
2068 GetUrlCacheEntryInfoW(url, info, &nbytes);
2070 ts->ftExpires = info->ExpireTime;
2071 ts->ftLastModified = info->LastModifiedTime;
2073 heap_free(info);
2074 *size = sizeof(*ts);
2075 return ERROR_SUCCESS;
2077 return error;
2080 case INTERNET_OPTION_DATAFILE_NAME: {
2081 DWORD req_size;
2083 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2085 if(!req->cacheFile) {
2086 *size = 0;
2087 return ERROR_INTERNET_ITEM_NOT_FOUND;
2090 if(unicode) {
2091 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2092 if(*size < req_size)
2093 return ERROR_INSUFFICIENT_BUFFER;
2095 *size = req_size;
2096 memcpy(buffer, req->cacheFile, *size);
2097 return ERROR_SUCCESS;
2098 }else {
2099 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2100 if (req_size > *size)
2101 return ERROR_INSUFFICIENT_BUFFER;
2103 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2104 -1, buffer, *size, NULL, NULL);
2105 return ERROR_SUCCESS;
2109 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2110 PCCERT_CONTEXT context;
2112 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2113 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2114 return ERROR_INSUFFICIENT_BUFFER;
2117 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2118 if(context) {
2119 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2120 DWORD len;
2122 memset(info, 0, sizeof(*info));
2123 info->ftExpiry = context->pCertInfo->NotAfter;
2124 info->ftStart = context->pCertInfo->NotBefore;
2125 len = CertNameToStrA(context->dwCertEncodingType,
2126 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2127 info->lpszSubjectInfo = LocalAlloc(0, len);
2128 if(info->lpszSubjectInfo)
2129 CertNameToStrA(context->dwCertEncodingType,
2130 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2131 info->lpszSubjectInfo, len);
2132 len = CertNameToStrA(context->dwCertEncodingType,
2133 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2134 info->lpszIssuerInfo = LocalAlloc(0, len);
2135 if(info->lpszIssuerInfo)
2136 CertNameToStrA(context->dwCertEncodingType,
2137 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2138 info->lpszIssuerInfo, len);
2139 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2140 CertFreeCertificateContext(context);
2141 return ERROR_SUCCESS;
2143 return ERROR_NOT_SUPPORTED;
2145 case INTERNET_OPTION_CONNECT_TIMEOUT:
2146 if (*size < sizeof(DWORD))
2147 return ERROR_INSUFFICIENT_BUFFER;
2149 *size = sizeof(DWORD);
2150 *(DWORD *)buffer = req->connect_timeout;
2151 return ERROR_SUCCESS;
2152 case INTERNET_OPTION_REQUEST_FLAGS: {
2153 DWORD flags = 0;
2155 if (*size < sizeof(DWORD))
2156 return ERROR_INSUFFICIENT_BUFFER;
2158 /* FIXME: Add support for:
2159 * INTERNET_REQFLAG_FROM_CACHE
2160 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2163 if(req->proxy)
2164 flags |= INTERNET_REQFLAG_VIA_PROXY;
2165 if(!req->rawHeaders)
2166 flags |= INTERNET_REQFLAG_NO_HEADERS;
2168 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2170 *size = sizeof(DWORD);
2171 *(DWORD*)buffer = flags;
2172 return ERROR_SUCCESS;
2176 return INET_QueryOption(hdr, option, buffer, size, unicode);
2179 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2181 http_request_t *req = (http_request_t*)hdr;
2183 switch(option) {
2184 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2185 TRACE("Undocumented option 99\n");
2187 if (!buffer || size != sizeof(DWORD))
2188 return ERROR_INVALID_PARAMETER;
2189 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2190 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2192 /* fall through */
2193 case INTERNET_OPTION_SECURITY_FLAGS:
2195 DWORD flags;
2197 if (!buffer || size != sizeof(DWORD))
2198 return ERROR_INVALID_PARAMETER;
2199 flags = *(DWORD *)buffer;
2200 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2201 flags &= SECURITY_SET_MASK;
2202 req->security_flags |= flags;
2203 if(req->netconn)
2204 req->netconn->security_flags |= flags;
2205 return ERROR_SUCCESS;
2207 case INTERNET_OPTION_CONNECT_TIMEOUT:
2208 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2209 req->connect_timeout = *(DWORD *)buffer;
2210 return ERROR_SUCCESS;
2212 case INTERNET_OPTION_SEND_TIMEOUT:
2213 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2214 req->send_timeout = *(DWORD *)buffer;
2215 return ERROR_SUCCESS;
2217 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2218 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2219 req->receive_timeout = *(DWORD *)buffer;
2220 return ERROR_SUCCESS;
2222 case INTERNET_OPTION_USERNAME:
2223 heap_free(req->session->userName);
2224 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2225 return ERROR_SUCCESS;
2227 case INTERNET_OPTION_PASSWORD:
2228 heap_free(req->session->password);
2229 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2230 return ERROR_SUCCESS;
2231 case INTERNET_OPTION_HTTP_DECODING:
2232 if(size != sizeof(BOOL))
2233 return ERROR_INVALID_PARAMETER;
2234 req->decoding = *(BOOL*)buffer;
2235 return ERROR_SUCCESS;
2238 return INET_SetOption(hdr, option, buffer, size);
2241 static void commit_cache_entry(http_request_t *req)
2243 WCHAR url[INTERNET_MAX_URL_LENGTH];
2245 TRACE("%p\n", req);
2247 CloseHandle(req->hCacheFile);
2248 req->hCacheFile = NULL;
2250 if(HTTP_GetRequestURL(req, url)) {
2251 DWORD headersLen;
2253 headersLen = req->rawHeaders ? strlenW(req->rawHeaders) : 0;
2254 CommitUrlCacheEntryW(url, req->cacheFile, req->expires,
2255 req->last_modified, NORMAL_CACHE_ENTRY,
2256 req->rawHeaders, headersLen, NULL, 0);
2260 static void create_cache_entry(http_request_t *req)
2262 static const WCHAR no_cacheW[] = {'n','o','-','c','a','c','h','e',0};
2263 static const WCHAR no_storeW[] = {'n','o','-','s','t','o','r','e',0};
2265 WCHAR url[INTERNET_MAX_URL_LENGTH];
2266 WCHAR file_name[MAX_PATH+1];
2267 BOOL b = TRUE;
2269 /* FIXME: We should free previous cache file earlier */
2270 heap_free(req->cacheFile);
2271 CloseHandle(req->hCacheFile);
2272 req->hCacheFile = NULL;
2274 if(req->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE)
2275 b = FALSE;
2277 if(b) {
2278 int header_idx = HTTP_GetCustomHeaderIndex(req, szCache_Control, 0, FALSE);
2279 if(header_idx!=-1 && (!strcmpiW(req->custHeaders[header_idx].lpszValue, no_cacheW)
2280 || !strcmpiW(req->custHeaders[header_idx].lpszValue, no_storeW)))
2281 b = FALSE;
2284 if(!b) {
2285 if(!(req->hdr.dwFlags & INTERNET_FLAG_NEED_FILE))
2286 return;
2288 FIXME("INTERNET_FLAG_NEED_FILE is not supported correctly\n");
2291 b = HTTP_GetRequestURL(req, url);
2292 if(!b) {
2293 WARN("Could not get URL\n");
2294 return;
2297 b = CreateUrlCacheEntryW(url, req->contentLength == ~0u ? 0 : req->contentLength, NULL, file_name, 0);
2298 if(!b) {
2299 WARN("Could not create cache entry: %08x\n", GetLastError());
2300 return;
2303 req->cacheFile = heap_strdupW(file_name);
2304 req->hCacheFile = CreateFileW(req->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2305 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2306 if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2307 WARN("Could not create file: %u\n", GetLastError());
2308 req->hCacheFile = NULL;
2309 return;
2312 if(req->read_size) {
2313 DWORD written;
2315 b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2316 if(!b)
2317 FIXME("WriteFile failed: %u\n", GetLastError());
2319 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2320 commit_cache_entry(req);
2324 /* read some more data into the read buffer (the read section must be held) */
2325 static DWORD read_more_data( http_request_t *req, int maxlen )
2327 DWORD res;
2328 int len;
2330 if (req->read_pos)
2332 /* move existing data to the start of the buffer */
2333 if(req->read_size)
2334 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2335 req->read_pos = 0;
2338 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2340 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2341 maxlen - req->read_size, 0, &len );
2342 if(res == ERROR_SUCCESS)
2343 req->read_size += len;
2345 return res;
2348 /* remove some amount of data from the read buffer (the read section must be held) */
2349 static void remove_data( http_request_t *req, int count )
2351 if (!(req->read_size -= count)) req->read_pos = 0;
2352 else req->read_pos += count;
2355 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2357 int count, bytes_read, pos = 0;
2358 DWORD res;
2360 EnterCriticalSection( &req->read_section );
2361 for (;;)
2363 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2365 if (eol)
2367 count = eol - (req->read_buf + req->read_pos);
2368 bytes_read = count + 1;
2370 else count = bytes_read = req->read_size;
2372 count = min( count, *len - pos );
2373 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2374 pos += count;
2375 remove_data( req, bytes_read );
2376 if (eol) break;
2378 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2380 *len = 0;
2381 TRACE( "returning empty string %u\n", res);
2382 LeaveCriticalSection( &req->read_section );
2383 INTERNET_SetLastError(res);
2384 return FALSE;
2387 LeaveCriticalSection( &req->read_section );
2389 if (pos < *len)
2391 if (pos && buffer[pos - 1] == '\r') pos--;
2392 *len = pos + 1;
2394 buffer[*len - 1] = 0;
2395 TRACE( "returning %s\n", debugstr_a(buffer));
2396 return TRUE;
2399 /* check if we have reached the end of the data to read (the read section must be held) */
2400 static BOOL end_of_read_data( http_request_t *req )
2402 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2405 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
2407 DWORD res;
2409 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
2410 assert(*read <= size);
2412 if(req->hCacheFile) {
2413 if(*read) {
2414 BOOL bres;
2415 DWORD written;
2417 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2418 if(!bres)
2419 FIXME("WriteFile failed: %u\n", GetLastError());
2422 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2423 commit_cache_entry(req);
2426 return res;
2429 /* fetch some more data into the read buffer (the read section must be held) */
2430 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2432 DWORD res, read=0;
2434 if(req->read_size == sizeof(req->read_buf))
2435 return ERROR_SUCCESS;
2437 if(req->read_pos) {
2438 if(req->read_size)
2439 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2440 req->read_pos = 0;
2443 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2444 &read, read_mode);
2445 req->read_size += read;
2447 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2448 if(read_bytes)
2449 *read_bytes = read;
2450 return res;
2453 /* return the size of data available to be read immediately (the read section must be held) */
2454 static DWORD get_avail_data( http_request_t *req )
2456 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2459 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2461 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2462 DWORD avail = 0;
2464 if(req->netconn)
2465 NETCON_query_data_available(req->netconn, &avail);
2466 return netconn_stream->content_length == ~0u
2467 ? avail
2468 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2471 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2473 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2474 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2477 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2478 DWORD *read, read_mode_t read_mode)
2480 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2481 int len = 0;
2483 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2485 if(read_mode == READMODE_NOBLOCK) {
2486 DWORD avail = netconn_get_avail_data(stream, req);
2487 if (size > avail)
2488 size = avail;
2491 if(size && req->netconn) {
2492 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2493 len = 0;
2494 if(!len)
2495 netconn_stream->content_length = netconn_stream->content_read;
2498 netconn_stream->content_read += *read = len;
2499 TRACE("read %u bytes\n", len);
2500 return ERROR_SUCCESS;
2503 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2505 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2506 BYTE buf[1024];
2507 DWORD avail;
2508 int len;
2510 if(netconn_end_of_data(stream, req))
2511 return TRUE;
2513 do {
2514 avail = netconn_get_avail_data(stream, req);
2515 if(!avail)
2516 return FALSE;
2518 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2519 return FALSE;
2521 netconn_stream->content_read += len;
2522 }while(netconn_stream->content_read < netconn_stream->content_length);
2524 return TRUE;
2527 static void netconn_destroy(data_stream_t *stream)
2531 static const data_stream_vtbl_t netconn_stream_vtbl = {
2532 netconn_get_avail_data,
2533 netconn_end_of_data,
2534 netconn_read,
2535 netconn_drain_content,
2536 netconn_destroy
2539 /* read some more data into the read buffer (the read section must be held) */
2540 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2542 DWORD res;
2543 int len;
2545 if (stream->buf_pos)
2547 /* move existing data to the start of the buffer */
2548 if(stream->buf_size)
2549 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2550 stream->buf_pos = 0;
2553 if (maxlen == -1) maxlen = sizeof(stream->buf);
2555 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2556 maxlen - stream->buf_size, 0, &len );
2557 if(res == ERROR_SUCCESS)
2558 stream->buf_size += len;
2560 return res;
2563 /* remove some amount of data from the read buffer (the read section must be held) */
2564 static void remove_chunked_data(chunked_stream_t *stream, int count)
2566 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2567 else stream->buf_pos += count;
2570 /* discard data contents until we reach end of line (the read section must be held) */
2571 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2573 DWORD res;
2577 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2578 if (eol)
2580 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2581 break;
2583 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2584 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2585 } while (stream->buf_size);
2586 return ERROR_SUCCESS;
2589 /* read the size of the next chunk (the read section must be held) */
2590 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2592 /* TODOO */
2593 DWORD chunk_size = 0, res;
2595 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2596 return res;
2598 for (;;)
2600 while (stream->buf_size)
2602 char ch = stream->buf[stream->buf_pos];
2603 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2604 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2605 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2606 else if (ch == ';' || ch == '\r' || ch == '\n')
2608 TRACE( "reading %u byte chunk\n", chunk_size );
2609 stream->chunk_size = chunk_size;
2610 req->contentLength += chunk_size;
2611 return discard_chunked_eol(stream, req);
2613 remove_chunked_data(stream, 1);
2615 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2616 if (!stream->buf_size)
2618 stream->chunk_size = 0;
2619 return ERROR_SUCCESS;
2624 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2626 /* Allow reading only from read buffer */
2627 return 0;
2630 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2632 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2633 return !chunked_stream->chunk_size;
2636 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2637 DWORD *read, read_mode_t read_mode)
2639 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2640 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2642 if(chunked_stream->chunk_size == ~0u) {
2643 res = start_next_chunk(chunked_stream, req);
2644 if(res != ERROR_SUCCESS)
2645 return res;
2648 while(size && chunked_stream->chunk_size) {
2649 if(chunked_stream->buf_size) {
2650 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2652 /* this could block */
2653 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2654 break;
2656 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2657 remove_chunked_data(chunked_stream, read_bytes);
2658 }else {
2659 read_bytes = min(size, chunked_stream->chunk_size);
2661 if(read_mode == READMODE_NOBLOCK) {
2662 DWORD avail;
2664 if(!req->netconn || !NETCON_query_data_available(req->netconn, &avail) || !avail)
2665 break;
2666 if(read_bytes > avail)
2667 read_bytes = avail;
2669 /* this could block */
2670 if(read_bytes == chunked_stream->chunk_size)
2671 break;
2674 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2675 if(res != ERROR_SUCCESS)
2676 break;
2679 chunked_stream->chunk_size -= read_bytes;
2680 size -= read_bytes;
2681 ret_read += read_bytes;
2682 if(!chunked_stream->chunk_size) {
2683 assert(read_mode != READMODE_NOBLOCK);
2684 res = start_next_chunk(chunked_stream, req);
2685 if(res != ERROR_SUCCESS)
2686 break;
2689 if(read_mode == READMODE_ASYNC)
2690 read_mode = READMODE_NOBLOCK;
2693 TRACE("read %u bytes\n", ret_read);
2694 *read = ret_read;
2695 return res;
2698 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2700 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2702 /* FIXME: we can do better */
2703 return !chunked_stream->chunk_size;
2706 static void chunked_destroy(data_stream_t *stream)
2708 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2709 heap_free(chunked_stream);
2712 static const data_stream_vtbl_t chunked_stream_vtbl = {
2713 chunked_get_avail_data,
2714 chunked_end_of_data,
2715 chunked_read,
2716 chunked_drain_content,
2717 chunked_destroy
2720 /* set the request content length based on the headers */
2721 static DWORD set_content_length(http_request_t *request)
2723 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2724 WCHAR encoding[20];
2725 DWORD size;
2727 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2728 request->contentLength = request->netconn_stream.content_length = 0;
2729 return ERROR_SUCCESS;
2732 size = sizeof(request->contentLength);
2733 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2734 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2735 request->contentLength = ~0u;
2736 request->netconn_stream.content_length = request->contentLength;
2737 request->netconn_stream.content_read = request->read_size;
2739 size = sizeof(encoding);
2740 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2741 !strcmpiW(encoding, szChunked))
2743 chunked_stream_t *chunked_stream;
2745 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2746 if(!chunked_stream)
2747 return ERROR_OUTOFMEMORY;
2749 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2750 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2751 chunked_stream->chunk_size = ~0u;
2753 if(request->read_size) {
2754 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2755 chunked_stream->buf_size = request->read_size;
2756 request->read_size = request->read_pos = 0;
2759 request->data_stream = &chunked_stream->data_stream;
2760 request->contentLength = ~0u;
2761 request->read_chunked = TRUE;
2764 if(request->decoding) {
2765 int encoding_idx;
2767 static const WCHAR gzipW[] = {'g','z','i','p',0};
2769 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2770 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2771 return init_gzip_stream(request);
2774 return ERROR_SUCCESS;
2777 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2779 INTERNET_ASYNC_RESULT iar;
2781 iar.dwResult = result;
2782 iar.dwError = error;
2784 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2785 sizeof(INTERNET_ASYNC_RESULT));
2788 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif, DWORD *ret_size)
2790 DWORD res, read = 0, avail = 0;
2791 read_mode_t mode;
2793 TRACE("%p\n", req);
2795 EnterCriticalSection( &req->read_section );
2797 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2798 res = refill_read_buffer(req, mode, &read);
2799 if(res == ERROR_SUCCESS)
2800 avail = get_avail_data(req);
2802 LeaveCriticalSection( &req->read_section );
2804 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2805 WARN("res %u read %u, closing connection\n", res, read);
2806 http_release_netconn(req, FALSE);
2809 if(res != ERROR_SUCCESS) {
2810 send_request_complete(req, 0, res);
2811 return;
2814 if(ret_size)
2815 *ret_size = avail;
2816 if(first_notif)
2817 avail = 0;
2819 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2822 /* read data from the http connection (the read section must be held) */
2823 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2825 DWORD current_read = 0, ret_read = 0;
2826 read_mode_t read_mode;
2827 DWORD res = ERROR_SUCCESS;
2829 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2831 EnterCriticalSection( &req->read_section );
2833 if(req->read_size) {
2834 ret_read = min(size, req->read_size);
2835 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2836 req->read_size -= ret_read;
2837 req->read_pos += ret_read;
2838 if(read_mode == READMODE_ASYNC)
2839 read_mode = READMODE_NOBLOCK;
2842 if(ret_read < size) {
2843 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2844 ret_read += current_read;
2847 LeaveCriticalSection( &req->read_section );
2849 *read = ret_read;
2850 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2852 if(size && !ret_read)
2853 http_release_netconn(req, res == ERROR_SUCCESS);
2855 return res;
2858 static BOOL drain_content(http_request_t *req, BOOL blocking)
2860 BOOL ret;
2862 if(!req->netconn || req->contentLength == -1)
2863 return FALSE;
2865 if(!strcmpW(req->verb, szHEAD))
2866 return TRUE;
2868 if(!blocking)
2869 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2871 EnterCriticalSection( &req->read_section );
2873 while(1) {
2874 DWORD bytes_read, res;
2875 BYTE buf[4096];
2877 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2878 if(res != ERROR_SUCCESS) {
2879 ret = FALSE;
2880 break;
2882 if(!bytes_read) {
2883 ret = TRUE;
2884 break;
2888 LeaveCriticalSection( &req->read_section );
2889 return ret;
2892 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2894 http_request_t *req = (http_request_t*)hdr;
2895 DWORD res;
2897 EnterCriticalSection( &req->read_section );
2898 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2899 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2901 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2902 if(res == ERROR_SUCCESS)
2903 res = hdr->dwError;
2904 LeaveCriticalSection( &req->read_section );
2906 return res;
2909 typedef struct {
2910 task_header_t hdr;
2911 void *buf;
2912 DWORD size;
2913 DWORD *ret_read;
2914 } read_file_ex_task_t;
2916 static void AsyncReadFileExProc(task_header_t *hdr)
2918 read_file_ex_task_t *task = (read_file_ex_task_t*)hdr;
2919 http_request_t *req = (http_request_t*)task->hdr.hdr;
2920 DWORD res;
2922 TRACE("INTERNETREADFILEEXW %p\n", task->hdr.hdr);
2924 res = HTTPREQ_Read(req, task->buf, task->size, task->ret_read, TRUE);
2925 send_request_complete(req, res == ERROR_SUCCESS, res);
2928 static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
2929 DWORD flags, DWORD_PTR context)
2932 http_request_t *req = (http_request_t*)hdr;
2933 DWORD res, read, cread, error = ERROR_SUCCESS;
2935 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2936 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2938 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2940 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2942 read_file_ex_task_t *task;
2944 if (TryEnterCriticalSection( &req->read_section ))
2946 if (get_avail_data(req))
2948 res = HTTPREQ_Read(req, buf, size, &read, FALSE);
2949 LeaveCriticalSection( &req->read_section );
2950 goto done;
2952 LeaveCriticalSection( &req->read_section );
2955 task = alloc_async_task(&req->hdr, AsyncReadFileExProc, sizeof(*task));
2956 task->buf = buf;
2957 task->size = size;
2958 task->ret_read = ret_read;
2960 INTERNET_AsyncCall(&task->hdr);
2962 return ERROR_IO_PENDING;
2965 read = 0;
2967 EnterCriticalSection( &req->read_section );
2968 if(hdr->dwError == ERROR_SUCCESS)
2969 hdr->dwError = INTERNET_HANDLE_IN_USE;
2970 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2971 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2973 while(1) {
2974 res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, !(flags & IRF_NO_WAIT));
2975 if(res != ERROR_SUCCESS)
2976 break;
2978 read += cread;
2979 if(read == size || end_of_read_data(req))
2980 break;
2982 LeaveCriticalSection( &req->read_section );
2984 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2985 &cread, sizeof(cread));
2986 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2987 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2989 EnterCriticalSection( &req->read_section );
2992 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2993 hdr->dwError = ERROR_SUCCESS;
2994 else
2995 error = hdr->dwError;
2997 LeaveCriticalSection( &req->read_section );
2999 done:
3000 *ret_read = read;
3001 if (res == ERROR_SUCCESS) {
3002 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3003 &read, sizeof(read));
3006 return res==ERROR_SUCCESS ? error : res;
3009 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3011 DWORD res;
3012 http_request_t *request = (http_request_t*)hdr;
3014 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3016 *written = 0;
3017 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3018 if (res == ERROR_SUCCESS)
3019 request->bytesWritten += *written;
3021 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3022 return res;
3025 typedef struct {
3026 task_header_t hdr;
3027 DWORD *ret_size;
3028 } http_data_available_task_t;
3030 static void AsyncQueryDataAvailableProc(task_header_t *hdr)
3032 http_data_available_task_t *task = (http_data_available_task_t*)hdr;
3034 HTTP_ReceiveRequestData((http_request_t*)task->hdr.hdr, FALSE, task->ret_size);
3037 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3039 http_request_t *req = (http_request_t*)hdr;
3041 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3043 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3045 http_data_available_task_t *task;
3047 /* never wait, if we can't enter the section we queue an async request right away */
3048 if (TryEnterCriticalSection( &req->read_section ))
3050 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3051 if ((*available = get_avail_data( req ))) goto done;
3052 if (end_of_read_data( req )) goto done;
3053 LeaveCriticalSection( &req->read_section );
3056 task = alloc_async_task(&req->hdr, AsyncQueryDataAvailableProc, sizeof(*task));
3057 task->ret_size = available;
3058 INTERNET_AsyncCall(&task->hdr);
3059 return ERROR_IO_PENDING;
3062 EnterCriticalSection( &req->read_section );
3064 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3066 refill_read_buffer( req, READMODE_ASYNC, NULL );
3067 *available = get_avail_data( req );
3070 done:
3071 LeaveCriticalSection( &req->read_section );
3073 TRACE( "returning %u\n", *available );
3074 return ERROR_SUCCESS;
3077 static const object_vtbl_t HTTPREQVtbl = {
3078 HTTPREQ_Destroy,
3079 HTTPREQ_CloseConnection,
3080 HTTPREQ_QueryOption,
3081 HTTPREQ_SetOption,
3082 HTTPREQ_ReadFile,
3083 HTTPREQ_ReadFileEx,
3084 HTTPREQ_WriteFile,
3085 HTTPREQ_QueryDataAvailable,
3086 NULL
3089 /***********************************************************************
3090 * HTTP_HttpOpenRequestW (internal)
3092 * Open a HTTP request handle
3094 * RETURNS
3095 * HINTERNET a HTTP request handle on success
3096 * NULL on failure
3099 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3100 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3101 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3102 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3104 appinfo_t *hIC = session->appInfo;
3105 http_request_t *request;
3106 DWORD len, res = ERROR_SUCCESS;
3108 TRACE("-->\n");
3110 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3111 if(!request)
3112 return ERROR_OUTOFMEMORY;
3114 request->hdr.htype = WH_HHTTPREQ;
3115 request->hdr.dwFlags = dwFlags;
3116 request->hdr.dwContext = dwContext;
3117 request->contentLength = ~0u;
3119 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3120 request->data_stream = &request->netconn_stream.data_stream;
3121 request->connect_timeout = session->connect_timeout;
3122 request->send_timeout = session->send_timeout;
3123 request->receive_timeout = session->receive_timeout;
3125 InitializeCriticalSection( &request->read_section );
3126 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3128 WININET_AddRef( &session->hdr );
3129 request->session = session;
3130 list_add_head( &session->hdr.children, &request->hdr.entry );
3132 request->server = get_server(session->hostName, session->hostPort, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3133 if(!request->server) {
3134 WININET_Release(&request->hdr);
3135 return ERROR_OUTOFMEMORY;
3138 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3139 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3140 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3141 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3143 if (lpszObjectName && *lpszObjectName) {
3144 HRESULT rc;
3146 len = 0;
3147 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3148 if (rc != E_POINTER)
3149 len = strlenW(lpszObjectName)+1;
3150 request->path = heap_alloc(len*sizeof(WCHAR));
3151 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3152 URL_ESCAPE_SPACES_ONLY);
3153 if (rc != S_OK)
3155 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3156 strcpyW(request->path,lpszObjectName);
3158 }else {
3159 static const WCHAR slashW[] = {'/',0};
3161 request->path = heap_strdupW(slashW);
3164 if (lpszReferrer && *lpszReferrer)
3165 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3167 if (lpszAcceptTypes)
3169 int i;
3170 for (i = 0; lpszAcceptTypes[i]; i++)
3172 if (!*lpszAcceptTypes[i]) continue;
3173 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3174 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3175 HTTP_ADDHDR_FLAG_REQ |
3176 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3180 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3181 request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3183 HTTP_ProcessHeader(request, hostW, request->server->canon_host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3185 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3186 session->hostPort = INTERNET_DEFAULT_HTTP_PORT;
3188 if (hIC->proxy && hIC->proxy[0])
3189 HTTP_DealWithProxy( hIC, session, request );
3191 INTERNET_SendCallback(&session->hdr, dwContext,
3192 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3193 sizeof(HINTERNET));
3195 TRACE("<-- %u (%p)\n", res, request);
3197 if(res != ERROR_SUCCESS) {
3198 WININET_Release( &request->hdr );
3199 *ret = NULL;
3200 return res;
3203 *ret = request->hdr.hInternet;
3204 return ERROR_SUCCESS;
3207 /***********************************************************************
3208 * HttpOpenRequestW (WININET.@)
3210 * Open a HTTP request handle
3212 * RETURNS
3213 * HINTERNET a HTTP request handle on success
3214 * NULL on failure
3217 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3218 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3219 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3220 DWORD dwFlags, DWORD_PTR dwContext)
3222 http_session_t *session;
3223 HINTERNET handle = NULL;
3224 DWORD res;
3226 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3227 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3228 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3229 dwFlags, dwContext);
3230 if(lpszAcceptTypes!=NULL)
3232 int i;
3233 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3234 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3237 session = (http_session_t*) get_handle_object( hHttpSession );
3238 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3240 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3241 goto lend;
3245 * My tests seem to show that the windows version does not
3246 * become asynchronous until after this point. And anyhow
3247 * if this call was asynchronous then how would you get the
3248 * necessary HINTERNET pointer returned by this function.
3251 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3252 lpszVersion, lpszReferrer, lpszAcceptTypes,
3253 dwFlags, dwContext, &handle);
3254 lend:
3255 if( session )
3256 WININET_Release( &session->hdr );
3257 TRACE("returning %p\n", handle);
3258 if(res != ERROR_SUCCESS)
3259 SetLastError(res);
3260 return handle;
3263 static const LPCWSTR header_lookup[] = {
3264 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3265 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3266 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3267 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3268 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3269 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3270 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3271 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3272 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3273 szDate, /* HTTP_QUERY_DATE = 9 */
3274 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3275 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3276 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3277 szURI, /* HTTP_QUERY_URI = 13 */
3278 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3279 NULL, /* HTTP_QUERY_COST = 15 */
3280 NULL, /* HTTP_QUERY_LINK = 16 */
3281 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3282 NULL, /* HTTP_QUERY_VERSION = 18 */
3283 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3284 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3285 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3286 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3287 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3288 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3289 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3290 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3291 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3292 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3293 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3294 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3295 NULL, /* HTTP_QUERY_FROM = 31 */
3296 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3297 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3298 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3299 szReferer, /* HTTP_QUERY_REFERER = 35 */
3300 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3301 szServer, /* HTTP_QUERY_SERVER = 37 */
3302 NULL, /* HTTP_TITLE = 38 */
3303 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3304 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3305 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3306 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3307 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3308 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3309 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3310 NULL, /* HTTP_QUERY_REFRESH = 46 */
3311 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3312 szAge, /* HTTP_QUERY_AGE = 48 */
3313 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3314 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3315 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3316 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3317 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3318 szETag, /* HTTP_QUERY_ETAG = 54 */
3319 hostW, /* HTTP_QUERY_HOST = 55 */
3320 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3321 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3322 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3323 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3324 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3325 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3326 szRange, /* HTTP_QUERY_RANGE = 62 */
3327 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3328 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3329 szVary, /* HTTP_QUERY_VARY = 65 */
3330 szVia, /* HTTP_QUERY_VIA = 66 */
3331 szWarning, /* HTTP_QUERY_WARNING = 67 */
3332 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3333 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3334 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3337 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3339 /***********************************************************************
3340 * HTTP_HttpQueryInfoW (internal)
3342 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3343 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3345 LPHTTPHEADERW lphttpHdr = NULL;
3346 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3347 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3348 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3349 INT index = -1;
3351 /* Find requested header structure */
3352 switch (level)
3354 case HTTP_QUERY_CUSTOM:
3355 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3356 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3357 break;
3358 case HTTP_QUERY_RAW_HEADERS_CRLF:
3360 LPWSTR headers;
3361 DWORD len = 0;
3362 DWORD res = ERROR_INVALID_PARAMETER;
3364 if (request_only)
3365 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3366 else
3367 headers = request->rawHeaders;
3369 if (headers)
3370 len = strlenW(headers) * sizeof(WCHAR);
3372 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3374 len += sizeof(WCHAR);
3375 res = ERROR_INSUFFICIENT_BUFFER;
3377 else if (lpBuffer)
3379 if (headers)
3380 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3381 else
3383 len = strlenW(szCrLf) * sizeof(WCHAR);
3384 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3386 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3387 res = ERROR_SUCCESS;
3389 *lpdwBufferLength = len;
3391 if (request_only) heap_free(headers);
3392 return res;
3394 case HTTP_QUERY_RAW_HEADERS:
3396 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3397 DWORD i, size = 0;
3398 LPWSTR pszString = lpBuffer;
3400 for (i = 0; ppszRawHeaderLines[i]; i++)
3401 size += strlenW(ppszRawHeaderLines[i]) + 1;
3403 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3405 HTTP_FreeTokens(ppszRawHeaderLines);
3406 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3407 return ERROR_INSUFFICIENT_BUFFER;
3409 if (pszString)
3411 for (i = 0; ppszRawHeaderLines[i]; i++)
3413 DWORD len = strlenW(ppszRawHeaderLines[i]);
3414 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3415 pszString += len+1;
3417 *pszString = '\0';
3418 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3420 *lpdwBufferLength = size * sizeof(WCHAR);
3421 HTTP_FreeTokens(ppszRawHeaderLines);
3423 return ERROR_SUCCESS;
3425 case HTTP_QUERY_STATUS_TEXT:
3426 if (request->statusText)
3428 DWORD len = strlenW(request->statusText);
3429 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3431 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3432 return ERROR_INSUFFICIENT_BUFFER;
3434 if (lpBuffer)
3436 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3437 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3439 *lpdwBufferLength = len * sizeof(WCHAR);
3440 return ERROR_SUCCESS;
3442 break;
3443 case HTTP_QUERY_VERSION:
3444 if (request->version)
3446 DWORD len = strlenW(request->version);
3447 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3449 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3450 return ERROR_INSUFFICIENT_BUFFER;
3452 if (lpBuffer)
3454 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3455 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3457 *lpdwBufferLength = len * sizeof(WCHAR);
3458 return ERROR_SUCCESS;
3460 break;
3461 case HTTP_QUERY_CONTENT_ENCODING:
3462 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3463 requested_index,request_only);
3464 break;
3465 case HTTP_QUERY_STATUS_CODE: {
3466 DWORD res = ERROR_SUCCESS;
3468 if(request_only)
3469 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3471 if(requested_index)
3472 break;
3474 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3475 if(*lpdwBufferLength >= sizeof(DWORD))
3476 *(DWORD*)lpBuffer = request->status_code;
3477 else
3478 res = ERROR_INSUFFICIENT_BUFFER;
3479 *lpdwBufferLength = sizeof(DWORD);
3480 }else {
3481 WCHAR buf[12];
3482 DWORD size;
3483 static const WCHAR formatW[] = {'%','u',0};
3485 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3487 if(size <= *lpdwBufferLength) {
3488 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3489 }else {
3490 size += sizeof(WCHAR);
3491 res = ERROR_INSUFFICIENT_BUFFER;
3494 *lpdwBufferLength = size;
3496 return res;
3498 default:
3499 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3501 if (level < LAST_TABLE_HEADER && header_lookup[level])
3502 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3503 requested_index,request_only);
3506 if (index >= 0)
3507 lphttpHdr = &request->custHeaders[index];
3509 /* Ensure header satisfies requested attributes */
3510 if (!lphttpHdr ||
3511 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3512 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3514 return ERROR_HTTP_HEADER_NOT_FOUND;
3517 if (lpdwIndex) (*lpdwIndex)++;
3519 /* coalesce value to requested type */
3520 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3522 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3523 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3525 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3527 time_t tmpTime;
3528 struct tm tmpTM;
3529 SYSTEMTIME *STHook;
3531 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3533 tmpTM = *gmtime(&tmpTime);
3534 STHook = (SYSTEMTIME *)lpBuffer;
3535 STHook->wDay = tmpTM.tm_mday;
3536 STHook->wHour = tmpTM.tm_hour;
3537 STHook->wMilliseconds = 0;
3538 STHook->wMinute = tmpTM.tm_min;
3539 STHook->wDayOfWeek = tmpTM.tm_wday;
3540 STHook->wMonth = tmpTM.tm_mon + 1;
3541 STHook->wSecond = tmpTM.tm_sec;
3542 STHook->wYear = 1900+tmpTM.tm_year;
3544 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3545 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3546 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3548 else if (lphttpHdr->lpszValue)
3550 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3552 if (len > *lpdwBufferLength)
3554 *lpdwBufferLength = len;
3555 return ERROR_INSUFFICIENT_BUFFER;
3557 if (lpBuffer)
3559 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3560 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3562 *lpdwBufferLength = len - sizeof(WCHAR);
3564 return ERROR_SUCCESS;
3567 /***********************************************************************
3568 * HttpQueryInfoW (WININET.@)
3570 * Queries for information about an HTTP request
3572 * RETURNS
3573 * TRUE on success
3574 * FALSE on failure
3577 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3578 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3580 http_request_t *request;
3581 DWORD res;
3583 if (TRACE_ON(wininet)) {
3584 #define FE(x) { x, #x }
3585 static const wininet_flag_info query_flags[] = {
3586 FE(HTTP_QUERY_MIME_VERSION),
3587 FE(HTTP_QUERY_CONTENT_TYPE),
3588 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3589 FE(HTTP_QUERY_CONTENT_ID),
3590 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3591 FE(HTTP_QUERY_CONTENT_LENGTH),
3592 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3593 FE(HTTP_QUERY_ALLOW),
3594 FE(HTTP_QUERY_PUBLIC),
3595 FE(HTTP_QUERY_DATE),
3596 FE(HTTP_QUERY_EXPIRES),
3597 FE(HTTP_QUERY_LAST_MODIFIED),
3598 FE(HTTP_QUERY_MESSAGE_ID),
3599 FE(HTTP_QUERY_URI),
3600 FE(HTTP_QUERY_DERIVED_FROM),
3601 FE(HTTP_QUERY_COST),
3602 FE(HTTP_QUERY_LINK),
3603 FE(HTTP_QUERY_PRAGMA),
3604 FE(HTTP_QUERY_VERSION),
3605 FE(HTTP_QUERY_STATUS_CODE),
3606 FE(HTTP_QUERY_STATUS_TEXT),
3607 FE(HTTP_QUERY_RAW_HEADERS),
3608 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3609 FE(HTTP_QUERY_CONNECTION),
3610 FE(HTTP_QUERY_ACCEPT),
3611 FE(HTTP_QUERY_ACCEPT_CHARSET),
3612 FE(HTTP_QUERY_ACCEPT_ENCODING),
3613 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3614 FE(HTTP_QUERY_AUTHORIZATION),
3615 FE(HTTP_QUERY_CONTENT_ENCODING),
3616 FE(HTTP_QUERY_FORWARDED),
3617 FE(HTTP_QUERY_FROM),
3618 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3619 FE(HTTP_QUERY_LOCATION),
3620 FE(HTTP_QUERY_ORIG_URI),
3621 FE(HTTP_QUERY_REFERER),
3622 FE(HTTP_QUERY_RETRY_AFTER),
3623 FE(HTTP_QUERY_SERVER),
3624 FE(HTTP_QUERY_TITLE),
3625 FE(HTTP_QUERY_USER_AGENT),
3626 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3627 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3628 FE(HTTP_QUERY_ACCEPT_RANGES),
3629 FE(HTTP_QUERY_SET_COOKIE),
3630 FE(HTTP_QUERY_COOKIE),
3631 FE(HTTP_QUERY_REQUEST_METHOD),
3632 FE(HTTP_QUERY_REFRESH),
3633 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3634 FE(HTTP_QUERY_AGE),
3635 FE(HTTP_QUERY_CACHE_CONTROL),
3636 FE(HTTP_QUERY_CONTENT_BASE),
3637 FE(HTTP_QUERY_CONTENT_LOCATION),
3638 FE(HTTP_QUERY_CONTENT_MD5),
3639 FE(HTTP_QUERY_CONTENT_RANGE),
3640 FE(HTTP_QUERY_ETAG),
3641 FE(HTTP_QUERY_HOST),
3642 FE(HTTP_QUERY_IF_MATCH),
3643 FE(HTTP_QUERY_IF_NONE_MATCH),
3644 FE(HTTP_QUERY_IF_RANGE),
3645 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3646 FE(HTTP_QUERY_MAX_FORWARDS),
3647 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3648 FE(HTTP_QUERY_RANGE),
3649 FE(HTTP_QUERY_TRANSFER_ENCODING),
3650 FE(HTTP_QUERY_UPGRADE),
3651 FE(HTTP_QUERY_VARY),
3652 FE(HTTP_QUERY_VIA),
3653 FE(HTTP_QUERY_WARNING),
3654 FE(HTTP_QUERY_CUSTOM)
3656 static const wininet_flag_info modifier_flags[] = {
3657 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3658 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3659 FE(HTTP_QUERY_FLAG_NUMBER),
3660 FE(HTTP_QUERY_FLAG_COALESCE)
3662 #undef FE
3663 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3664 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3665 DWORD i;
3667 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3668 TRACE(" Attribute:");
3669 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3670 if (query_flags[i].val == info) {
3671 TRACE(" %s", query_flags[i].name);
3672 break;
3675 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3676 TRACE(" Unknown (%08x)", info);
3679 TRACE(" Modifier:");
3680 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3681 if (modifier_flags[i].val & info_mod) {
3682 TRACE(" %s", modifier_flags[i].name);
3683 info_mod &= ~ modifier_flags[i].val;
3687 if (info_mod) {
3688 TRACE(" Unknown (%08x)", info_mod);
3690 TRACE("\n");
3693 request = (http_request_t*) get_handle_object( hHttpRequest );
3694 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3696 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3697 goto lend;
3700 if (lpBuffer == NULL)
3701 *lpdwBufferLength = 0;
3702 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3703 lpBuffer, lpdwBufferLength, lpdwIndex);
3705 lend:
3706 if( request )
3707 WININET_Release( &request->hdr );
3709 TRACE("%u <--\n", res);
3710 if(res != ERROR_SUCCESS)
3711 SetLastError(res);
3712 return res == ERROR_SUCCESS;
3715 /***********************************************************************
3716 * HttpQueryInfoA (WININET.@)
3718 * Queries for information about an HTTP request
3720 * RETURNS
3721 * TRUE on success
3722 * FALSE on failure
3725 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3726 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3728 BOOL result;
3729 DWORD len;
3730 WCHAR* bufferW;
3732 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3733 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3735 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3736 lpdwBufferLength, lpdwIndex );
3739 if (lpBuffer)
3741 DWORD alloclen;
3742 len = (*lpdwBufferLength)*sizeof(WCHAR);
3743 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3745 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3746 if (alloclen < len)
3747 alloclen = len;
3749 else
3750 alloclen = len;
3751 bufferW = heap_alloc(alloclen);
3752 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3753 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3754 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3755 } else
3757 bufferW = NULL;
3758 len = 0;
3761 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3762 &len, lpdwIndex );
3763 if( result )
3765 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3766 lpBuffer, *lpdwBufferLength, NULL, NULL );
3767 *lpdwBufferLength = len - 1;
3769 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3771 else
3772 /* since the strings being returned from HttpQueryInfoW should be
3773 * only ASCII characters, it is reasonable to assume that all of
3774 * the Unicode characters can be reduced to a single byte */
3775 *lpdwBufferLength = len / sizeof(WCHAR);
3777 heap_free( bufferW );
3778 return result;
3781 /***********************************************************************
3782 * HTTP_GetRedirectURL (internal)
3784 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3786 static WCHAR szHttp[] = {'h','t','t','p',0};
3787 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3788 http_session_t *session = request->session;
3789 URL_COMPONENTSW urlComponents;
3790 DWORD url_length = 0;
3791 LPWSTR orig_url;
3792 LPWSTR combined_url;
3794 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3795 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3796 urlComponents.dwSchemeLength = 0;
3797 urlComponents.lpszHostName = session->hostName;
3798 urlComponents.dwHostNameLength = 0;
3799 urlComponents.nPort = session->hostPort;
3800 urlComponents.lpszUserName = session->userName;
3801 urlComponents.dwUserNameLength = 0;
3802 urlComponents.lpszPassword = NULL;
3803 urlComponents.dwPasswordLength = 0;
3804 urlComponents.lpszUrlPath = request->path;
3805 urlComponents.dwUrlPathLength = 0;
3806 urlComponents.lpszExtraInfo = NULL;
3807 urlComponents.dwExtraInfoLength = 0;
3809 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3810 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3811 return NULL;
3813 orig_url = heap_alloc(url_length);
3815 /* convert from bytes to characters */
3816 url_length = url_length / sizeof(WCHAR) - 1;
3817 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3819 heap_free(orig_url);
3820 return NULL;
3823 url_length = 0;
3824 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3825 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3827 heap_free(orig_url);
3828 return NULL;
3830 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3832 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3834 heap_free(orig_url);
3835 heap_free(combined_url);
3836 return NULL;
3838 heap_free(orig_url);
3839 return combined_url;
3843 /***********************************************************************
3844 * HTTP_HandleRedirect (internal)
3846 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3848 http_session_t *session = request->session;
3849 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3850 int index;
3852 if(lpszUrl[0]=='/')
3854 /* if it's an absolute path, keep the same session info */
3855 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3857 else
3859 URL_COMPONENTSW urlComponents;
3860 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3861 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3862 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3863 BOOL custom_port = FALSE;
3865 static WCHAR httpW[] = {'h','t','t','p',0};
3866 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3868 userName[0] = 0;
3869 hostName[0] = 0;
3870 protocol[0] = 0;
3872 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3873 urlComponents.lpszScheme = protocol;
3874 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3875 urlComponents.lpszHostName = hostName;
3876 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3877 urlComponents.lpszUserName = userName;
3878 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3879 urlComponents.lpszPassword = NULL;
3880 urlComponents.dwPasswordLength = 0;
3881 urlComponents.lpszUrlPath = path;
3882 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3883 urlComponents.lpszExtraInfo = NULL;
3884 urlComponents.dwExtraInfoLength = 0;
3885 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3886 return INTERNET_GetLastError();
3888 if(!strcmpiW(protocol, httpW)) {
3889 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3890 TRACE("redirect from secure page to non-secure page\n");
3891 /* FIXME: warn about from secure redirect to non-secure page */
3892 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3895 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3896 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3897 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3898 custom_port = TRUE;
3899 }else if(!strcmpiW(protocol, httpsW)) {
3900 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3901 TRACE("redirect from non-secure page to secure page\n");
3902 /* FIXME: notify about redirect to secure page */
3903 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3906 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3907 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3908 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3909 custom_port = TRUE;
3912 heap_free(session->hostName);
3914 if(custom_port) {
3915 int len;
3916 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3917 len = lstrlenW(hostName);
3918 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3919 session->hostName = heap_alloc(len*sizeof(WCHAR));
3920 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3922 else
3923 session->hostName = heap_strdupW(hostName);
3925 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3927 heap_free(session->userName);
3928 session->userName = NULL;
3929 if (userName[0])
3930 session->userName = heap_strdupW(userName);
3932 reset_data_stream(request);
3934 if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
3935 server_t *new_server;
3937 new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
3938 server_release(request->server);
3939 request->server = new_server;
3942 heap_free(request->path);
3943 request->path=NULL;
3944 if (*path)
3946 DWORD needed = 0;
3947 HRESULT rc;
3949 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3950 if (rc != E_POINTER)
3951 needed = strlenW(path)+1;
3952 request->path = heap_alloc(needed*sizeof(WCHAR));
3953 rc = UrlEscapeW(path, request->path, &needed,
3954 URL_ESCAPE_SPACES_ONLY);
3955 if (rc != S_OK)
3957 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3958 strcpyW(request->path,path);
3962 /* Remove custom content-type/length headers on redirects. */
3963 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3964 if (0 <= index)
3965 HTTP_DeleteCustomHeader(request, index);
3966 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3967 if (0 <= index)
3968 HTTP_DeleteCustomHeader(request, index);
3970 return ERROR_SUCCESS;
3973 /***********************************************************************
3974 * HTTP_build_req (internal)
3976 * concatenate all the strings in the request together
3978 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3980 LPCWSTR *t;
3981 LPWSTR str;
3983 for( t = list; *t ; t++ )
3984 len += strlenW( *t );
3985 len++;
3987 str = heap_alloc(len*sizeof(WCHAR));
3988 *str = 0;
3990 for( t = list; *t ; t++ )
3991 strcatW( str, *t );
3993 return str;
3996 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3998 server_t *server = request->server;
3999 LPWSTR requestString;
4000 INT len;
4001 INT cnt;
4002 INT responseLen;
4003 char *ascii_req;
4004 DWORD res;
4006 static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
4008 TRACE("\n");
4010 requestString = HTTP_BuildHeaderRequestString( request, connectW, server->host_port, g_szHttp1_1 );
4012 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4013 NULL, 0, NULL, NULL );
4014 len--; /* the nul terminator isn't needed */
4015 ascii_req = heap_alloc(len);
4016 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4017 heap_free( requestString );
4019 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4021 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4022 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4023 heap_free( ascii_req );
4024 if (res != ERROR_SUCCESS)
4025 return res;
4027 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4028 if (!responseLen)
4029 return ERROR_HTTP_INVALID_HEADER;
4031 return ERROR_SUCCESS;
4034 static void HTTP_InsertCookies(http_request_t *request)
4036 DWORD cookie_size, size, cnt = 0;
4037 HTTPHEADERW *host;
4038 WCHAR *cookies;
4040 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4042 host = HTTP_GetHeader(request, hostW);
4043 if(!host)
4044 return;
4046 if(get_cookie(host->lpszValue, request->path, NULL, &cookie_size) != ERROR_SUCCESS)
4047 return;
4049 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4050 if(!(cookies = heap_alloc(size)))
4051 return;
4053 cnt += sprintfW(cookies, cookieW);
4054 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4055 strcatW(cookies, szCrLf);
4057 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4059 heap_free(cookies);
4062 static WORD HTTP_ParseWkday(LPCWSTR day)
4064 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4065 { 'm','o','n',0 },
4066 { 't','u','e',0 },
4067 { 'w','e','d',0 },
4068 { 't','h','u',0 },
4069 { 'f','r','i',0 },
4070 { 's','a','t',0 }};
4071 unsigned int i;
4072 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4073 if (!strcmpiW(day, days[i]))
4074 return i;
4076 /* Invalid */
4077 return 7;
4080 static WORD HTTP_ParseMonth(LPCWSTR month)
4082 static const WCHAR jan[] = { 'j','a','n',0 };
4083 static const WCHAR feb[] = { 'f','e','b',0 };
4084 static const WCHAR mar[] = { 'm','a','r',0 };
4085 static const WCHAR apr[] = { 'a','p','r',0 };
4086 static const WCHAR may[] = { 'm','a','y',0 };
4087 static const WCHAR jun[] = { 'j','u','n',0 };
4088 static const WCHAR jul[] = { 'j','u','l',0 };
4089 static const WCHAR aug[] = { 'a','u','g',0 };
4090 static const WCHAR sep[] = { 's','e','p',0 };
4091 static const WCHAR oct[] = { 'o','c','t',0 };
4092 static const WCHAR nov[] = { 'n','o','v',0 };
4093 static const WCHAR dec[] = { 'd','e','c',0 };
4095 if (!strcmpiW(month, jan)) return 1;
4096 if (!strcmpiW(month, feb)) return 2;
4097 if (!strcmpiW(month, mar)) return 3;
4098 if (!strcmpiW(month, apr)) return 4;
4099 if (!strcmpiW(month, may)) return 5;
4100 if (!strcmpiW(month, jun)) return 6;
4101 if (!strcmpiW(month, jul)) return 7;
4102 if (!strcmpiW(month, aug)) return 8;
4103 if (!strcmpiW(month, sep)) return 9;
4104 if (!strcmpiW(month, oct)) return 10;
4105 if (!strcmpiW(month, nov)) return 11;
4106 if (!strcmpiW(month, dec)) return 12;
4107 /* Invalid */
4108 return 0;
4111 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4112 * optionally preceded by whitespace.
4113 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4114 * st, and sets *str to the first character after the time format.
4116 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4118 LPCWSTR ptr = *str;
4119 WCHAR *nextPtr;
4120 unsigned long num;
4122 while (isspaceW(*ptr))
4123 ptr++;
4125 num = strtoulW(ptr, &nextPtr, 10);
4126 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4128 ERR("unexpected time format %s\n", debugstr_w(ptr));
4129 return FALSE;
4131 if (num > 23)
4133 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4134 return FALSE;
4136 ptr = nextPtr + 1;
4137 st->wHour = (WORD)num;
4138 num = strtoulW(ptr, &nextPtr, 10);
4139 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4141 ERR("unexpected time format %s\n", debugstr_w(ptr));
4142 return FALSE;
4144 if (num > 59)
4146 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4147 return FALSE;
4149 ptr = nextPtr + 1;
4150 st->wMinute = (WORD)num;
4151 num = strtoulW(ptr, &nextPtr, 10);
4152 if (!nextPtr || nextPtr <= ptr)
4154 ERR("unexpected time format %s\n", debugstr_w(ptr));
4155 return FALSE;
4157 if (num > 59)
4159 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4160 return FALSE;
4162 ptr = nextPtr + 1;
4163 *str = ptr;
4164 st->wSecond = (WORD)num;
4165 return TRUE;
4168 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4170 static const WCHAR gmt[]= { 'G','M','T',0 };
4171 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4172 LPCWSTR ptr;
4173 SYSTEMTIME st = { 0 };
4174 unsigned long num;
4176 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4177 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4178 *dayPtr = *ptr;
4179 *dayPtr = 0;
4180 st.wDayOfWeek = HTTP_ParseWkday(day);
4181 if (st.wDayOfWeek >= 7)
4183 ERR("unexpected weekday %s\n", debugstr_w(day));
4184 return FALSE;
4187 while (isspaceW(*ptr))
4188 ptr++;
4190 for (monthPtr = month; !isspace(*ptr) &&
4191 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4192 monthPtr++, ptr++)
4193 *monthPtr = *ptr;
4194 *monthPtr = 0;
4195 st.wMonth = HTTP_ParseMonth(month);
4196 if (!st.wMonth || st.wMonth > 12)
4198 ERR("unexpected month %s\n", debugstr_w(month));
4199 return FALSE;
4202 while (isspaceW(*ptr))
4203 ptr++;
4205 num = strtoulW(ptr, &nextPtr, 10);
4206 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4208 ERR("unexpected day %s\n", debugstr_w(ptr));
4209 return FALSE;
4211 ptr = nextPtr;
4212 st.wDay = (WORD)num;
4214 while (isspaceW(*ptr))
4215 ptr++;
4217 if (!HTTP_ParseTime(&st, &ptr))
4218 return FALSE;
4220 while (isspaceW(*ptr))
4221 ptr++;
4223 num = strtoulW(ptr, &nextPtr, 10);
4224 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4226 ERR("unexpected year %s\n", debugstr_w(ptr));
4227 return FALSE;
4229 ptr = nextPtr;
4230 st.wYear = (WORD)num;
4232 while (isspaceW(*ptr))
4233 ptr++;
4235 /* asctime() doesn't report a timezone, but some web servers do, so accept
4236 * with or without GMT.
4238 if (*ptr && strcmpW(ptr, gmt))
4240 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4241 return FALSE;
4243 return SystemTimeToFileTime(&st, ft);
4246 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4248 static const WCHAR gmt[]= { 'G','M','T',0 };
4249 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4250 LPCWSTR ptr;
4251 unsigned long num;
4252 SYSTEMTIME st = { 0 };
4254 ptr = strchrW(value, ',');
4255 if (!ptr)
4256 return FALSE;
4257 if (ptr - value != 3)
4259 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4260 return FALSE;
4262 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4263 day[3] = 0;
4264 st.wDayOfWeek = HTTP_ParseWkday(day);
4265 if (st.wDayOfWeek > 6)
4267 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4268 return FALSE;
4270 ptr++;
4272 while (isspaceW(*ptr))
4273 ptr++;
4275 num = strtoulW(ptr, &nextPtr, 10);
4276 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4278 WARN("unexpected day %s\n", debugstr_w(value));
4279 return FALSE;
4281 ptr = nextPtr;
4282 st.wDay = (WORD)num;
4284 while (isspaceW(*ptr))
4285 ptr++;
4287 for (monthPtr = month; !isspace(*ptr) &&
4288 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4289 monthPtr++, ptr++)
4290 *monthPtr = *ptr;
4291 *monthPtr = 0;
4292 st.wMonth = HTTP_ParseMonth(month);
4293 if (!st.wMonth || st.wMonth > 12)
4295 WARN("unexpected month %s\n", debugstr_w(month));
4296 return FALSE;
4299 while (isspaceW(*ptr))
4300 ptr++;
4302 num = strtoulW(ptr, &nextPtr, 10);
4303 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4305 ERR("unexpected year %s\n", debugstr_w(value));
4306 return FALSE;
4308 ptr = nextPtr;
4309 st.wYear = (WORD)num;
4311 if (!HTTP_ParseTime(&st, &ptr))
4312 return FALSE;
4314 while (isspaceW(*ptr))
4315 ptr++;
4317 if (strcmpW(ptr, gmt))
4319 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4320 return FALSE;
4322 return SystemTimeToFileTime(&st, ft);
4325 static WORD HTTP_ParseWeekday(LPCWSTR day)
4327 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4328 { 'm','o','n','d','a','y',0 },
4329 { 't','u','e','s','d','a','y',0 },
4330 { 'w','e','d','n','e','s','d','a','y',0 },
4331 { 't','h','u','r','s','d','a','y',0 },
4332 { 'f','r','i','d','a','y',0 },
4333 { 's','a','t','u','r','d','a','y',0 }};
4334 unsigned int i;
4335 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4336 if (!strcmpiW(day, days[i]))
4337 return i;
4339 /* Invalid */
4340 return 7;
4343 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4345 static const WCHAR gmt[]= { 'G','M','T',0 };
4346 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4347 LPCWSTR ptr;
4348 unsigned long num;
4349 SYSTEMTIME st = { 0 };
4351 ptr = strchrW(value, ',');
4352 if (!ptr)
4353 return FALSE;
4354 if (ptr - value == 3)
4356 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4357 day[3] = 0;
4358 st.wDayOfWeek = HTTP_ParseWkday(day);
4359 if (st.wDayOfWeek > 6)
4361 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4362 return FALSE;
4365 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4367 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4368 day[ptr - value + 1] = 0;
4369 st.wDayOfWeek = HTTP_ParseWeekday(day);
4370 if (st.wDayOfWeek > 6)
4372 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4373 return FALSE;
4376 else
4378 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4379 return FALSE;
4381 ptr++;
4383 while (isspaceW(*ptr))
4384 ptr++;
4386 num = strtoulW(ptr, &nextPtr, 10);
4387 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4389 ERR("unexpected day %s\n", debugstr_w(value));
4390 return FALSE;
4392 ptr = nextPtr;
4393 st.wDay = (WORD)num;
4395 if (*ptr != '-')
4397 ERR("unexpected month format %s\n", debugstr_w(ptr));
4398 return FALSE;
4400 ptr++;
4402 for (monthPtr = month; *ptr != '-' &&
4403 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4404 monthPtr++, ptr++)
4405 *monthPtr = *ptr;
4406 *monthPtr = 0;
4407 st.wMonth = HTTP_ParseMonth(month);
4408 if (!st.wMonth || st.wMonth > 12)
4410 ERR("unexpected month %s\n", debugstr_w(month));
4411 return FALSE;
4414 if (*ptr != '-')
4416 ERR("unexpected year format %s\n", debugstr_w(ptr));
4417 return FALSE;
4419 ptr++;
4421 num = strtoulW(ptr, &nextPtr, 10);
4422 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4424 ERR("unexpected year %s\n", debugstr_w(value));
4425 return FALSE;
4427 ptr = nextPtr;
4428 st.wYear = (WORD)num;
4430 if (!HTTP_ParseTime(&st, &ptr))
4431 return FALSE;
4433 while (isspaceW(*ptr))
4434 ptr++;
4436 if (strcmpW(ptr, gmt))
4438 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4439 return FALSE;
4441 return SystemTimeToFileTime(&st, ft);
4444 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4446 static const WCHAR zero[] = { '0',0 };
4447 BOOL ret;
4449 if (!strcmpW(value, zero))
4451 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4452 ret = TRUE;
4454 else if (strchrW(value, ','))
4456 ret = HTTP_ParseRfc1123Date(value, ft);
4457 if (!ret)
4459 ret = HTTP_ParseRfc850Date(value, ft);
4460 if (!ret)
4461 ERR("unexpected date format %s\n", debugstr_w(value));
4464 else
4466 ret = HTTP_ParseDateAsAsctime(value, ft);
4467 if (!ret)
4468 ERR("unexpected date format %s\n", debugstr_w(value));
4470 return ret;
4473 static void HTTP_ProcessExpires(http_request_t *request)
4475 BOOL expirationFound = FALSE;
4476 int headerIndex;
4478 /* Look for a Cache-Control header with a max-age directive, as it takes
4479 * precedence over the Expires header.
4481 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4482 if (headerIndex != -1)
4484 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4485 LPWSTR ptr;
4487 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4489 LPWSTR comma = strchrW(ptr, ','), end, equal;
4491 if (comma)
4492 end = comma;
4493 else
4494 end = ptr + strlenW(ptr);
4495 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4497 if (*equal == '=')
4499 static const WCHAR max_age[] = {
4500 'm','a','x','-','a','g','e',0 };
4502 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4504 LPWSTR nextPtr;
4505 unsigned long age;
4507 age = strtoulW(equal + 1, &nextPtr, 10);
4508 if (nextPtr > equal + 1)
4510 LARGE_INTEGER ft;
4512 NtQuerySystemTime( &ft );
4513 /* Age is in seconds, FILETIME resolution is in
4514 * 100 nanosecond intervals.
4516 ft.QuadPart += age * (ULONGLONG)1000000;
4517 request->expires.dwLowDateTime = ft.u.LowPart;
4518 request->expires.dwHighDateTime = ft.u.HighPart;
4519 expirationFound = TRUE;
4523 if (comma)
4525 ptr = comma + 1;
4526 while (isspaceW(*ptr))
4527 ptr++;
4529 else
4530 ptr = NULL;
4533 if (!expirationFound)
4535 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4536 if (headerIndex != -1)
4538 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4539 FILETIME ft;
4541 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4543 expirationFound = TRUE;
4544 request->expires = ft;
4548 if (!expirationFound)
4550 LARGE_INTEGER t;
4552 /* With no known age, default to 10 minutes until expiration. */
4553 NtQuerySystemTime( &t );
4554 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4555 request->expires.dwLowDateTime = t.u.LowPart;
4556 request->expires.dwHighDateTime = t.u.HighPart;
4560 static void HTTP_ProcessLastModified(http_request_t *request)
4562 int headerIndex;
4564 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4565 if (headerIndex != -1)
4567 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4568 FILETIME ft;
4570 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4571 request->last_modified = ft;
4575 static void http_process_keep_alive(http_request_t *req)
4577 int index;
4579 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4580 if(index != -1)
4581 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4582 else
4583 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4586 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4588 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4589 netconn_t *netconn = NULL;
4590 DWORD res;
4592 assert(!request->netconn);
4593 reset_data_stream(request);
4595 res = HTTP_ResolveName(request);
4596 if(res != ERROR_SUCCESS)
4597 return res;
4599 EnterCriticalSection(&connection_pool_cs);
4601 while(!list_empty(&request->server->conn_pool)) {
4602 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4603 list_remove(&netconn->pool_entry);
4605 if(NETCON_is_alive(netconn))
4606 break;
4608 TRACE("connection %p closed during idle\n", netconn);
4609 free_netconn(netconn);
4610 netconn = NULL;
4613 LeaveCriticalSection(&connection_pool_cs);
4615 if(netconn) {
4616 TRACE("<-- reusing %p netconn\n", netconn);
4617 request->netconn = netconn;
4618 *reusing = TRUE;
4619 return ERROR_SUCCESS;
4622 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4623 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4625 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4626 INTERNET_STATUS_CONNECTING_TO_SERVER,
4627 request->server->addr_str,
4628 strlen(request->server->addr_str)+1);
4630 res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4631 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4632 request->connect_timeout, &netconn);
4633 if(res != ERROR_SUCCESS) {
4634 ERR("create_netconn failed: %u\n", res);
4635 return res;
4638 request->netconn = netconn;
4640 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4641 INTERNET_STATUS_CONNECTED_TO_SERVER,
4642 request->server->addr_str, strlen(request->server->addr_str)+1);
4644 if(is_https) {
4645 /* Note: we differ from Microsoft's WinINet here. they seem to have
4646 * a bug that causes no status callbacks to be sent when starting
4647 * a tunnel to a proxy server using the CONNECT verb. i believe our
4648 * behaviour to be more correct and to not cause any incompatibilities
4649 * because using a secure connection through a proxy server is a rare
4650 * case that would be hard for anyone to depend on */
4651 if(request->proxy)
4652 res = HTTP_SecureProxyConnect(request);
4653 if(res == ERROR_SUCCESS)
4654 res = NETCON_secure_connect(request->netconn, request->server);
4657 if(res != ERROR_SUCCESS) {
4658 http_release_netconn(request, FALSE);
4659 return res;
4662 *reusing = FALSE;
4663 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4664 return ERROR_SUCCESS;
4667 /***********************************************************************
4668 * HTTP_HttpSendRequestW (internal)
4670 * Sends the specified request to the HTTP server
4672 * RETURNS
4673 * ERROR_SUCCESS on success
4674 * win32 error code on failure
4677 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4678 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4679 DWORD dwContentLength, BOOL bEndRequest)
4681 INT cnt;
4682 BOOL redirected = FALSE;
4683 LPWSTR requestString = NULL;
4684 INT responseLen;
4685 BOOL loop_next;
4686 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4687 static const WCHAR szContentLength[] =
4688 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4689 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4690 DWORD res;
4692 TRACE("--> %p\n", request);
4694 assert(request->hdr.htype == WH_HHTTPREQ);
4696 /* if the verb is NULL default to GET */
4697 if (!request->verb)
4698 request->verb = heap_strdupW(szGET);
4700 if (dwContentLength || strcmpW(request->verb, szGET))
4702 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4703 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4704 request->bytesToWrite = dwContentLength;
4706 if (request->session->appInfo->agent)
4708 WCHAR *agent_header;
4709 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4710 int len;
4712 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4713 agent_header = heap_alloc(len * sizeof(WCHAR));
4714 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4716 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4717 heap_free(agent_header);
4719 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4721 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4722 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4724 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4726 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4727 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4728 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4731 /* add the headers the caller supplied */
4732 if( lpszHeaders && dwHeaderLength )
4733 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4737 DWORD len;
4738 BOOL reusing_connection;
4739 char *ascii_req;
4741 loop_next = FALSE;
4742 reusing_connection = request->netconn != NULL;
4744 if(redirected) {
4745 request->contentLength = ~0u;
4746 request->bytesToWrite = 0;
4749 if (TRACE_ON(wininet))
4751 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4752 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4755 HTTP_FixURL(request);
4756 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4758 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4760 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4761 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4763 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4764 HTTP_InsertCookies(request);
4766 if (request->proxy)
4768 WCHAR *url = build_proxy_path_url(request);
4769 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4770 heap_free(url);
4772 else
4773 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4776 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4778 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4779 break;
4781 /* send the request as ASCII, tack on the optional data */
4782 if (!lpOptional || redirected)
4783 dwOptionalLength = 0;
4784 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4785 NULL, 0, NULL, NULL );
4786 ascii_req = heap_alloc(len + dwOptionalLength);
4787 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4788 ascii_req, len, NULL, NULL );
4789 if( lpOptional )
4790 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4791 len = (len + dwOptionalLength - 1);
4792 ascii_req[len] = 0;
4793 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4795 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4796 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4798 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4799 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4800 heap_free( ascii_req );
4801 if(res != ERROR_SUCCESS) {
4802 TRACE("send failed: %u\n", res);
4803 if(!reusing_connection)
4804 break;
4805 http_release_netconn(request, FALSE);
4806 loop_next = TRUE;
4807 continue;
4810 request->bytesWritten = dwOptionalLength;
4812 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4813 INTERNET_STATUS_REQUEST_SENT,
4814 &len, sizeof(DWORD));
4816 if (bEndRequest)
4818 DWORD dwBufferSize;
4820 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4821 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4823 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4824 /* FIXME: We should know that connection is closed before sending
4825 * headers. Otherwise wrong callbacks are executed */
4826 if(!responseLen && reusing_connection) {
4827 TRACE("Connection closed by server, reconnecting\n");
4828 http_release_netconn(request, FALSE);
4829 loop_next = TRUE;
4830 continue;
4833 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4834 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4835 sizeof(DWORD));
4837 http_process_keep_alive(request);
4838 HTTP_ProcessCookies(request);
4839 HTTP_ProcessExpires(request);
4840 HTTP_ProcessLastModified(request);
4842 res = set_content_length(request);
4843 if(res != ERROR_SUCCESS)
4844 goto lend;
4845 if(!request->contentLength)
4846 http_release_netconn(request, TRUE);
4848 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4850 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4851 dwBufferSize=sizeof(szNewLocation);
4852 switch(request->status_code) {
4853 case HTTP_STATUS_REDIRECT:
4854 case HTTP_STATUS_MOVED:
4855 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4856 case HTTP_STATUS_REDIRECT_METHOD:
4857 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4858 break;
4860 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4861 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4863 heap_free(request->verb);
4864 request->verb = heap_strdupW(szGET);
4866 http_release_netconn(request, drain_content(request, FALSE));
4867 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4869 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4870 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4871 res = HTTP_HandleRedirect(request, new_url);
4872 if (res == ERROR_SUCCESS)
4874 heap_free(requestString);
4875 loop_next = TRUE;
4877 heap_free( new_url );
4879 redirected = TRUE;
4882 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4884 WCHAR szAuthValue[2048];
4885 dwBufferSize=2048;
4886 if (request->status_code == HTTP_STATUS_DENIED)
4888 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4889 DWORD dwIndex = 0;
4890 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4892 if (HTTP_DoAuthorization(request, szAuthValue,
4893 &request->authInfo,
4894 request->session->userName,
4895 request->session->password,
4896 Host->lpszValue))
4898 heap_free(requestString);
4899 if(!drain_content(request, TRUE)) {
4900 FIXME("Could not drain content\n");
4901 http_release_netconn(request, FALSE);
4903 loop_next = TRUE;
4904 break;
4908 if(!loop_next) {
4909 TRACE("Cleaning wrong authorization data\n");
4910 destroy_authinfo(request->authInfo);
4911 request->authInfo = NULL;
4914 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4916 DWORD dwIndex = 0;
4917 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4919 if (HTTP_DoAuthorization(request, szAuthValue,
4920 &request->proxyAuthInfo,
4921 request->session->appInfo->proxyUsername,
4922 request->session->appInfo->proxyPassword,
4923 NULL))
4925 heap_free(requestString);
4926 if(!drain_content(request, TRUE)) {
4927 FIXME("Could not drain content\n");
4928 http_release_netconn(request, FALSE);
4930 loop_next = TRUE;
4931 break;
4935 if(!loop_next) {
4936 TRACE("Cleaning wrong proxy authorization data\n");
4937 destroy_authinfo(request->proxyAuthInfo);
4938 request->proxyAuthInfo = NULL;
4943 else
4944 res = ERROR_SUCCESS;
4946 while (loop_next);
4948 lend:
4949 heap_free(requestString);
4951 /* TODO: send notification for P3P header */
4953 if(res == ERROR_SUCCESS)
4954 create_cache_entry(request);
4956 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4958 if (res == ERROR_SUCCESS) {
4959 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4960 HTTP_ReceiveRequestData(request, TRUE, NULL);
4961 else
4962 send_request_complete(request,
4963 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4964 }else {
4965 send_request_complete(request, 0, res);
4969 TRACE("<--\n");
4970 return res;
4973 typedef struct {
4974 task_header_t hdr;
4975 WCHAR *headers;
4976 DWORD headers_len;
4977 void *optional;
4978 DWORD optional_len;
4979 DWORD content_len;
4980 BOOL end_request;
4981 } send_request_task_t;
4983 /***********************************************************************
4985 * Helper functions for the HttpSendRequest(Ex) functions
4988 static void AsyncHttpSendRequestProc(task_header_t *hdr)
4990 send_request_task_t *task = (send_request_task_t*)hdr;
4991 http_request_t *request = (http_request_t*)task->hdr.hdr;
4993 TRACE("%p\n", request);
4995 HTTP_HttpSendRequestW(request, task->headers, task->headers_len, task->optional,
4996 task->optional_len, task->content_len, task->end_request);
4998 heap_free(task->headers);
5002 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5004 DWORD dwBufferSize;
5005 INT responseLen;
5006 DWORD res = ERROR_SUCCESS;
5008 if(!request->netconn) {
5009 WARN("Not connected\n");
5010 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5011 return ERROR_INTERNET_OPERATION_CANCELLED;
5014 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5015 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5017 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5018 if (!responseLen)
5019 res = ERROR_HTTP_HEADER_NOT_FOUND;
5021 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5022 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5024 /* process cookies here. Is this right? */
5025 http_process_keep_alive(request);
5026 HTTP_ProcessCookies(request);
5027 HTTP_ProcessExpires(request);
5028 HTTP_ProcessLastModified(request);
5030 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5031 if(!request->contentLength)
5032 http_release_netconn(request, TRUE);
5035 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5037 switch(request->status_code) {
5038 case HTTP_STATUS_REDIRECT:
5039 case HTTP_STATUS_MOVED:
5040 case HTTP_STATUS_REDIRECT_METHOD:
5041 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5042 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5043 dwBufferSize=sizeof(szNewLocation);
5044 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5045 break;
5047 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5048 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5050 heap_free(request->verb);
5051 request->verb = heap_strdupW(szGET);
5053 http_release_netconn(request, drain_content(request, FALSE));
5054 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5056 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5057 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5058 res = HTTP_HandleRedirect(request, new_url);
5059 if (res == ERROR_SUCCESS)
5060 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5061 heap_free( new_url );
5067 if(res == ERROR_SUCCESS)
5068 create_cache_entry(request);
5070 if (res == ERROR_SUCCESS && request->contentLength)
5071 HTTP_ReceiveRequestData(request, TRUE, NULL);
5072 else
5073 send_request_complete(request, res == ERROR_SUCCESS, res);
5075 return res;
5078 /***********************************************************************
5079 * HttpEndRequestA (WININET.@)
5081 * Ends an HTTP request that was started by HttpSendRequestEx
5083 * RETURNS
5084 * TRUE if successful
5085 * FALSE on failure
5088 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5089 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5091 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5093 if (lpBuffersOut)
5095 SetLastError(ERROR_INVALID_PARAMETER);
5096 return FALSE;
5099 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5102 typedef struct {
5103 task_header_t hdr;
5104 DWORD flags;
5105 DWORD context;
5106 } end_request_task_t;
5108 static void AsyncHttpEndRequestProc(task_header_t *hdr)
5110 end_request_task_t *task = (end_request_task_t*)hdr;
5111 http_request_t *req = (http_request_t*)task->hdr.hdr;
5113 TRACE("%p\n", req);
5115 HTTP_HttpEndRequestW(req, task->flags, task->context);
5118 /***********************************************************************
5119 * HttpEndRequestW (WININET.@)
5121 * Ends an HTTP request that was started by HttpSendRequestEx
5123 * RETURNS
5124 * TRUE if successful
5125 * FALSE on failure
5128 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5129 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5131 http_request_t *request;
5132 DWORD res;
5134 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5136 if (lpBuffersOut)
5138 SetLastError(ERROR_INVALID_PARAMETER);
5139 return FALSE;
5142 request = (http_request_t*) get_handle_object( hRequest );
5144 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5146 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5147 if (request)
5148 WININET_Release( &request->hdr );
5149 return FALSE;
5151 request->hdr.dwFlags |= dwFlags;
5153 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5155 end_request_task_t *task;
5157 task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task));
5158 task->flags = dwFlags;
5159 task->context = dwContext;
5161 INTERNET_AsyncCall(&task->hdr);
5162 res = ERROR_IO_PENDING;
5164 else
5165 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5167 WININET_Release( &request->hdr );
5168 TRACE("%u <--\n", res);
5169 if(res != ERROR_SUCCESS)
5170 SetLastError(res);
5171 return res == ERROR_SUCCESS;
5174 /***********************************************************************
5175 * HttpSendRequestExA (WININET.@)
5177 * Sends the specified request to the HTTP server and allows chunked
5178 * transfers.
5180 * RETURNS
5181 * Success: TRUE
5182 * Failure: FALSE, call GetLastError() for more information.
5184 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5185 LPINTERNET_BUFFERSA lpBuffersIn,
5186 LPINTERNET_BUFFERSA lpBuffersOut,
5187 DWORD dwFlags, DWORD_PTR dwContext)
5189 INTERNET_BUFFERSW BuffersInW;
5190 BOOL rc = FALSE;
5191 DWORD headerlen;
5192 LPWSTR header = NULL;
5194 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5195 lpBuffersOut, dwFlags, dwContext);
5197 if (lpBuffersIn)
5199 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5200 if (lpBuffersIn->lpcszHeader)
5202 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5203 lpBuffersIn->dwHeadersLength,0,0);
5204 header = heap_alloc(headerlen*sizeof(WCHAR));
5205 if (!(BuffersInW.lpcszHeader = header))
5207 SetLastError(ERROR_OUTOFMEMORY);
5208 return FALSE;
5210 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5211 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5212 header, headerlen);
5214 else
5215 BuffersInW.lpcszHeader = NULL;
5216 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5217 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5218 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5219 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5220 BuffersInW.Next = NULL;
5223 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5225 heap_free(header);
5226 return rc;
5229 /***********************************************************************
5230 * HttpSendRequestExW (WININET.@)
5232 * Sends the specified request to the HTTP server and allows chunked
5233 * transfers
5235 * RETURNS
5236 * Success: TRUE
5237 * Failure: FALSE, call GetLastError() for more information.
5239 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5240 LPINTERNET_BUFFERSW lpBuffersIn,
5241 LPINTERNET_BUFFERSW lpBuffersOut,
5242 DWORD dwFlags, DWORD_PTR dwContext)
5244 http_request_t *request;
5245 http_session_t *session;
5246 appinfo_t *hIC;
5247 DWORD res;
5249 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5250 lpBuffersOut, dwFlags, dwContext);
5252 request = (http_request_t*) get_handle_object( hRequest );
5254 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5256 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5257 goto lend;
5260 session = request->session;
5261 assert(session->hdr.htype == WH_HHTTPSESSION);
5262 hIC = session->appInfo;
5263 assert(hIC->hdr.htype == WH_HINIT);
5265 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5267 send_request_task_t *task;
5269 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5270 if (lpBuffersIn)
5272 DWORD size = 0;
5274 if (lpBuffersIn->lpcszHeader)
5276 if (lpBuffersIn->dwHeadersLength == ~0u)
5277 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5278 else
5279 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5281 task->headers = heap_alloc(size);
5282 memcpy(task->headers, lpBuffersIn->lpcszHeader, size);
5284 else task->headers = NULL;
5286 task->headers_len = size / sizeof(WCHAR);
5287 task->optional = lpBuffersIn->lpvBuffer;
5288 task->optional_len = lpBuffersIn->dwBufferLength;
5289 task->content_len = lpBuffersIn->dwBufferTotal;
5291 else
5293 task->headers = NULL;
5294 task->headers_len = 0;
5295 task->optional = NULL;
5296 task->optional_len = 0;
5297 task->content_len = 0;
5300 task->end_request = FALSE;
5302 INTERNET_AsyncCall(&task->hdr);
5303 res = ERROR_IO_PENDING;
5305 else
5307 if (lpBuffersIn)
5308 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5309 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5310 lpBuffersIn->dwBufferTotal, FALSE);
5311 else
5312 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5315 lend:
5316 if ( request )
5317 WININET_Release( &request->hdr );
5319 TRACE("<---\n");
5320 SetLastError(res);
5321 return res == ERROR_SUCCESS;
5324 /***********************************************************************
5325 * HttpSendRequestW (WININET.@)
5327 * Sends the specified request to the HTTP server
5329 * RETURNS
5330 * TRUE on success
5331 * FALSE on failure
5334 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5335 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5337 http_request_t *request;
5338 http_session_t *session = NULL;
5339 appinfo_t *hIC = NULL;
5340 DWORD res = ERROR_SUCCESS;
5342 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5343 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5345 request = (http_request_t*) get_handle_object( hHttpRequest );
5346 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5348 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5349 goto lend;
5352 session = request->session;
5353 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5355 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5356 goto lend;
5359 hIC = session->appInfo;
5360 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5362 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5363 goto lend;
5366 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5368 send_request_task_t *task;
5370 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5371 if (lpszHeaders)
5373 DWORD size;
5375 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5376 else size = dwHeaderLength * sizeof(WCHAR);
5378 task->headers = heap_alloc(size);
5379 memcpy(task->headers, lpszHeaders, size);
5381 else
5382 task->headers = NULL;
5383 task->headers_len = dwHeaderLength;
5384 task->optional = lpOptional;
5385 task->optional_len = dwOptionalLength;
5386 task->content_len = dwOptionalLength;
5387 task->end_request = TRUE;
5389 INTERNET_AsyncCall(&task->hdr);
5390 res = ERROR_IO_PENDING;
5392 else
5394 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5395 dwHeaderLength, lpOptional, dwOptionalLength,
5396 dwOptionalLength, TRUE);
5398 lend:
5399 if( request )
5400 WININET_Release( &request->hdr );
5402 SetLastError(res);
5403 return res == ERROR_SUCCESS;
5406 /***********************************************************************
5407 * HttpSendRequestA (WININET.@)
5409 * Sends the specified request to the HTTP server
5411 * RETURNS
5412 * TRUE on success
5413 * FALSE on failure
5416 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5417 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5419 BOOL result;
5420 LPWSTR szHeaders=NULL;
5421 DWORD nLen=dwHeaderLength;
5422 if(lpszHeaders!=NULL)
5424 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5425 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5426 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5428 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5429 heap_free(szHeaders);
5430 return result;
5433 /***********************************************************************
5434 * HTTPSESSION_Destroy (internal)
5436 * Deallocate session handle
5439 static void HTTPSESSION_Destroy(object_header_t *hdr)
5441 http_session_t *session = (http_session_t*) hdr;
5443 TRACE("%p\n", session);
5445 WININET_Release(&session->appInfo->hdr);
5447 heap_free(session->hostName);
5448 heap_free(session->password);
5449 heap_free(session->userName);
5452 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5454 http_session_t *ses = (http_session_t *)hdr;
5456 switch(option) {
5457 case INTERNET_OPTION_HANDLE_TYPE:
5458 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5460 if (*size < sizeof(ULONG))
5461 return ERROR_INSUFFICIENT_BUFFER;
5463 *size = sizeof(DWORD);
5464 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5465 return ERROR_SUCCESS;
5466 case INTERNET_OPTION_CONNECT_TIMEOUT:
5467 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5469 if (*size < sizeof(DWORD))
5470 return ERROR_INSUFFICIENT_BUFFER;
5472 *size = sizeof(DWORD);
5473 *(DWORD *)buffer = ses->connect_timeout;
5474 return ERROR_SUCCESS;
5476 case INTERNET_OPTION_SEND_TIMEOUT:
5477 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5479 if (*size < sizeof(DWORD))
5480 return ERROR_INSUFFICIENT_BUFFER;
5482 *size = sizeof(DWORD);
5483 *(DWORD *)buffer = ses->send_timeout;
5484 return ERROR_SUCCESS;
5486 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5487 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5489 if (*size < sizeof(DWORD))
5490 return ERROR_INSUFFICIENT_BUFFER;
5492 *size = sizeof(DWORD);
5493 *(DWORD *)buffer = ses->receive_timeout;
5494 return ERROR_SUCCESS;
5497 return INET_QueryOption(hdr, option, buffer, size, unicode);
5500 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5502 http_session_t *ses = (http_session_t*)hdr;
5504 switch(option) {
5505 case INTERNET_OPTION_USERNAME:
5507 heap_free(ses->userName);
5508 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5509 return ERROR_SUCCESS;
5511 case INTERNET_OPTION_PASSWORD:
5513 heap_free(ses->password);
5514 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5515 return ERROR_SUCCESS;
5517 case INTERNET_OPTION_CONNECT_TIMEOUT:
5519 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5520 ses->connect_timeout = *(DWORD *)buffer;
5521 return ERROR_SUCCESS;
5523 case INTERNET_OPTION_SEND_TIMEOUT:
5525 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5526 ses->send_timeout = *(DWORD *)buffer;
5527 return ERROR_SUCCESS;
5529 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5531 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5532 ses->receive_timeout = *(DWORD *)buffer;
5533 return ERROR_SUCCESS;
5535 default: break;
5538 return INET_SetOption(hdr, option, buffer, size);
5541 static const object_vtbl_t HTTPSESSIONVtbl = {
5542 HTTPSESSION_Destroy,
5543 NULL,
5544 HTTPSESSION_QueryOption,
5545 HTTPSESSION_SetOption,
5546 NULL,
5547 NULL,
5548 NULL,
5549 NULL,
5550 NULL
5554 /***********************************************************************
5555 * HTTP_Connect (internal)
5557 * Create http session handle
5559 * RETURNS
5560 * HINTERNET a session handle on success
5561 * NULL on failure
5564 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5565 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5566 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5567 DWORD dwInternalFlags, HINTERNET *ret)
5569 http_session_t *session = NULL;
5571 TRACE("-->\n");
5573 if (!lpszServerName || !lpszServerName[0])
5574 return ERROR_INVALID_PARAMETER;
5576 assert( hIC->hdr.htype == WH_HINIT );
5578 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5579 if (!session)
5580 return ERROR_OUTOFMEMORY;
5583 * According to my tests. The name is not resolved until a request is sent
5586 session->hdr.htype = WH_HHTTPSESSION;
5587 session->hdr.dwFlags = dwFlags;
5588 session->hdr.dwContext = dwContext;
5589 session->hdr.dwInternalFlags |= dwInternalFlags;
5591 WININET_AddRef( &hIC->hdr );
5592 session->appInfo = hIC;
5593 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5595 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5596 if(hIC->proxyBypass)
5597 FIXME("Proxy bypass is ignored.\n");
5599 session->hostName = heap_strdupW(lpszServerName);
5600 if (lpszUserName && lpszUserName[0])
5601 session->userName = heap_strdupW(lpszUserName);
5602 if (lpszPassword && lpszPassword[0])
5603 session->password = heap_strdupW(lpszPassword);
5604 session->hostPort = serverPort;
5605 session->connect_timeout = hIC->connect_timeout;
5606 session->send_timeout = INFINITE;
5607 session->receive_timeout = INFINITE;
5609 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5610 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5612 INTERNET_SendCallback(&hIC->hdr, dwContext,
5613 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5614 sizeof(HINTERNET));
5618 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5619 * windows
5622 TRACE("%p --> %p\n", hIC, session);
5624 *ret = session->hdr.hInternet;
5625 return ERROR_SUCCESS;
5628 /***********************************************************************
5629 * HTTP_clear_response_headers (internal)
5631 * clear out any old response headers
5633 static void HTTP_clear_response_headers( http_request_t *request )
5635 DWORD i;
5637 for( i=0; i<request->nCustHeaders; i++)
5639 if( !request->custHeaders[i].lpszField )
5640 continue;
5641 if( !request->custHeaders[i].lpszValue )
5642 continue;
5643 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5644 continue;
5645 HTTP_DeleteCustomHeader( request, i );
5646 i--;
5650 /***********************************************************************
5651 * HTTP_GetResponseHeaders (internal)
5653 * Read server response
5655 * RETURNS
5657 * TRUE on success
5658 * FALSE on error
5660 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5662 INT cbreaks = 0;
5663 WCHAR buffer[MAX_REPLY_LEN];
5664 DWORD buflen = MAX_REPLY_LEN;
5665 BOOL bSuccess = FALSE;
5666 INT rc = 0;
5667 char bufferA[MAX_REPLY_LEN];
5668 LPWSTR status_code = NULL, status_text = NULL;
5669 DWORD cchMaxRawHeaders = 1024;
5670 LPWSTR lpszRawHeaders = NULL;
5671 LPWSTR temp;
5672 DWORD cchRawHeaders = 0;
5673 BOOL codeHundred = FALSE;
5675 TRACE("-->\n");
5677 if(!request->netconn)
5678 goto lend;
5680 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5681 do {
5683 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5685 buflen = MAX_REPLY_LEN;
5686 if (!read_line(request, bufferA, &buflen))
5687 goto lend;
5689 /* clear old response headers (eg. from a redirect response) */
5690 if (clear) {
5691 HTTP_clear_response_headers( request );
5692 clear = FALSE;
5695 rc += buflen;
5696 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5697 /* check is this a status code line? */
5698 if (!strncmpW(buffer, g_szHttp1_0, 4))
5700 /* split the version from the status code */
5701 status_code = strchrW( buffer, ' ' );
5702 if( !status_code )
5703 goto lend;
5704 *status_code++=0;
5706 /* split the status code from the status text */
5707 status_text = strchrW( status_code, ' ' );
5708 if( !status_text )
5709 goto lend;
5710 *status_text++=0;
5712 request->status_code = atoiW(status_code);
5714 TRACE("version [%s] status code [%s] status text [%s]\n",
5715 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5717 codeHundred = request->status_code == HTTP_STATUS_CONTINUE;
5719 else if (!codeHundred)
5721 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5723 heap_free(request->version);
5724 heap_free(request->statusText);
5726 request->status_code = HTTP_STATUS_OK;
5727 request->version = heap_strdupW(g_szHttp1_0);
5728 request->statusText = heap_strdupW(szOK);
5730 heap_free(request->rawHeaders);
5731 request->rawHeaders = heap_strdupW(szDefaultHeader);
5733 bSuccess = TRUE;
5734 goto lend;
5736 } while (codeHundred);
5738 /* Add status code */
5739 HTTP_ProcessHeader(request, szStatus, status_code,
5740 HTTP_ADDHDR_FLAG_REPLACE);
5742 heap_free(request->version);
5743 heap_free(request->statusText);
5745 request->version = heap_strdupW(buffer);
5746 request->statusText = heap_strdupW(status_text);
5748 /* Restore the spaces */
5749 *(status_code-1) = ' ';
5750 *(status_text-1) = ' ';
5752 /* regenerate raw headers */
5753 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5754 if (!lpszRawHeaders) goto lend;
5756 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5757 cchMaxRawHeaders *= 2;
5758 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5759 if (temp == NULL) goto lend;
5760 lpszRawHeaders = temp;
5761 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5762 cchRawHeaders += (buflen-1);
5763 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5764 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5765 lpszRawHeaders[cchRawHeaders] = '\0';
5767 /* Parse each response line */
5770 buflen = MAX_REPLY_LEN;
5771 if (read_line(request, bufferA, &buflen))
5773 LPWSTR * pFieldAndValue;
5775 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5777 if (!bufferA[0]) break;
5778 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5780 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5781 if (pFieldAndValue)
5783 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5784 cchMaxRawHeaders *= 2;
5785 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5786 if (temp == NULL) goto lend;
5787 lpszRawHeaders = temp;
5788 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5789 cchRawHeaders += (buflen-1);
5790 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5791 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5792 lpszRawHeaders[cchRawHeaders] = '\0';
5794 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5795 HTTP_ADDREQ_FLAG_ADD );
5797 HTTP_FreeTokens(pFieldAndValue);
5800 else
5802 cbreaks++;
5803 if (cbreaks >= 2)
5804 break;
5806 }while(1);
5808 /* make sure the response header is terminated with an empty line. Some apps really
5809 truly care about that empty line being there for some reason. Just add it to the
5810 header. */
5811 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5813 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5814 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5815 if (temp == NULL) goto lend;
5816 lpszRawHeaders = temp;
5819 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5821 heap_free(request->rawHeaders);
5822 request->rawHeaders = lpszRawHeaders;
5823 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5824 bSuccess = TRUE;
5826 lend:
5828 TRACE("<--\n");
5829 if (bSuccess)
5830 return rc;
5831 else
5833 heap_free(lpszRawHeaders);
5834 return 0;
5838 /***********************************************************************
5839 * HTTP_InterpretHttpHeader (internal)
5841 * Parse server response
5843 * RETURNS
5845 * Pointer to array of field, value, NULL on success.
5846 * NULL on error.
5848 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5850 LPWSTR * pTokenPair;
5851 LPWSTR pszColon;
5852 INT len;
5854 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5856 pszColon = strchrW(buffer, ':');
5857 /* must have two tokens */
5858 if (!pszColon)
5860 HTTP_FreeTokens(pTokenPair);
5861 if (buffer[0])
5862 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5863 return NULL;
5866 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5867 if (!pTokenPair[0])
5869 HTTP_FreeTokens(pTokenPair);
5870 return NULL;
5872 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5873 pTokenPair[0][pszColon - buffer] = '\0';
5875 /* skip colon */
5876 pszColon++;
5877 len = strlenW(pszColon);
5878 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5879 if (!pTokenPair[1])
5881 HTTP_FreeTokens(pTokenPair);
5882 return NULL;
5884 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5886 strip_spaces(pTokenPair[0]);
5887 strip_spaces(pTokenPair[1]);
5889 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5890 return pTokenPair;
5893 /***********************************************************************
5894 * HTTP_ProcessHeader (internal)
5896 * Stuff header into header tables according to <dwModifier>
5900 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5902 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5904 LPHTTPHEADERW lphttpHdr = NULL;
5905 INT index = -1;
5906 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5907 DWORD res = ERROR_HTTP_INVALID_HEADER;
5909 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5911 /* REPLACE wins out over ADD */
5912 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5913 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5915 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5916 index = -1;
5917 else
5918 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5920 if (index >= 0)
5922 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5923 return ERROR_HTTP_INVALID_HEADER;
5924 lphttpHdr = &request->custHeaders[index];
5926 else if (value)
5928 HTTPHEADERW hdr;
5930 hdr.lpszField = (LPWSTR)field;
5931 hdr.lpszValue = (LPWSTR)value;
5932 hdr.wFlags = hdr.wCount = 0;
5934 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5935 hdr.wFlags |= HDR_ISREQUEST;
5937 return HTTP_InsertCustomHeader(request, &hdr);
5939 /* no value to delete */
5940 else return ERROR_SUCCESS;
5942 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5943 lphttpHdr->wFlags |= HDR_ISREQUEST;
5944 else
5945 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5947 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5949 HTTP_DeleteCustomHeader( request, index );
5951 if (value)
5953 HTTPHEADERW hdr;
5955 hdr.lpszField = (LPWSTR)field;
5956 hdr.lpszValue = (LPWSTR)value;
5957 hdr.wFlags = hdr.wCount = 0;
5959 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5960 hdr.wFlags |= HDR_ISREQUEST;
5962 return HTTP_InsertCustomHeader(request, &hdr);
5965 return ERROR_SUCCESS;
5967 else if (dwModifier & COALESCEFLAGS)
5969 LPWSTR lpsztmp;
5970 WCHAR ch = 0;
5971 INT len = 0;
5972 INT origlen = strlenW(lphttpHdr->lpszValue);
5973 INT valuelen = strlenW(value);
5975 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5977 ch = ',';
5978 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5980 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5982 ch = ';';
5983 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5986 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5988 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5989 if (lpsztmp)
5991 lphttpHdr->lpszValue = lpsztmp;
5992 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5993 if (ch > 0)
5995 lphttpHdr->lpszValue[origlen] = ch;
5996 origlen++;
5997 lphttpHdr->lpszValue[origlen] = ' ';
5998 origlen++;
6001 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6002 lphttpHdr->lpszValue[len] = '\0';
6003 res = ERROR_SUCCESS;
6005 else
6007 WARN("heap_realloc (%d bytes) failed\n",len+1);
6008 res = ERROR_OUTOFMEMORY;
6011 TRACE("<-- %d\n", res);
6012 return res;
6015 /***********************************************************************
6016 * HTTP_GetCustomHeaderIndex (internal)
6018 * Return index of custom header from header array
6021 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6022 int requested_index, BOOL request_only)
6024 DWORD index;
6026 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6028 for (index = 0; index < request->nCustHeaders; index++)
6030 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6031 continue;
6033 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6034 continue;
6036 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6037 continue;
6039 if (requested_index == 0)
6040 break;
6041 requested_index --;
6044 if (index >= request->nCustHeaders)
6045 index = -1;
6047 TRACE("Return: %d\n", index);
6048 return index;
6052 /***********************************************************************
6053 * HTTP_InsertCustomHeader (internal)
6055 * Insert header into array
6058 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6060 INT count;
6061 LPHTTPHEADERW lph = NULL;
6063 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6064 count = request->nCustHeaders + 1;
6065 if (count > 1)
6066 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6067 else
6068 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6070 if (!lph)
6071 return ERROR_OUTOFMEMORY;
6073 request->custHeaders = lph;
6074 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6075 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6076 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6077 request->custHeaders[count-1].wCount= lpHdr->wCount;
6078 request->nCustHeaders++;
6080 return ERROR_SUCCESS;
6084 /***********************************************************************
6085 * HTTP_DeleteCustomHeader (internal)
6087 * Delete header from array
6088 * If this function is called, the indexs may change.
6090 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6092 if( request->nCustHeaders <= 0 )
6093 return FALSE;
6094 if( index >= request->nCustHeaders )
6095 return FALSE;
6096 request->nCustHeaders--;
6098 heap_free(request->custHeaders[index].lpszField);
6099 heap_free(request->custHeaders[index].lpszValue);
6101 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6102 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6103 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6105 return TRUE;
6109 /***********************************************************************
6110 * HTTP_VerifyValidHeader (internal)
6112 * Verify the given header is not invalid for the given http request
6115 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6117 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6118 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6119 return ERROR_HTTP_INVALID_HEADER;
6121 return ERROR_SUCCESS;
6124 /***********************************************************************
6125 * IsHostInProxyBypassList (@)
6127 * Undocumented
6130 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6132 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6133 return FALSE;