wininet: Store both proxy and destination host in http_request_t.
[wine/testsucceed.git] / dlls / wininet / http.c
blob1c24153c1a107ba9c10dc03b683aef494f4f7db9
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"
69 #include "cryptuiapi.h"
71 #include "internet.h"
72 #include "wine/debug.h"
73 #include "wine/exception.h"
74 #include "wine/unicode.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
78 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
79 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
80 static const WCHAR szOK[] = {'O','K',0};
81 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
82 static const WCHAR hostW[] = { 'H','o','s','t',0 };
83 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
85 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
86 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
87 static const WCHAR szGET[] = { 'G','E','T', 0 };
88 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
89 static const WCHAR szCrLf[] = {'\r','\n', 0};
91 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
92 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
93 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
94 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
95 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
96 static const WCHAR szAge[] = { 'A','g','e',0 };
97 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
98 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
99 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
100 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
101 static const WCHAR szContent_Disposition[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
102 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
104 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
105 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
106 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
107 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
108 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
109 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
110 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
111 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
112 static const WCHAR szDate[] = { 'D','a','t','e',0 };
113 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
114 static const WCHAR szETag[] = { 'E','T','a','g',0 };
115 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
116 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
117 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
118 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
120 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
121 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
122 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
123 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
124 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
125 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
126 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
127 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
128 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
129 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
130 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
131 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
132 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
133 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
134 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
135 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
136 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
137 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
138 static const WCHAR szURI[] = { 'U','R','I',0 };
139 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
140 static const WCHAR szVary[] = { 'V','a','r','y',0 };
141 static const WCHAR szVia[] = { 'V','i','a',0 };
142 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
143 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
145 #define HTTP_REFERER szReferer
146 #define HTTP_ACCEPT szAccept
147 #define HTTP_USERAGENT szUser_Agent
149 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
150 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
151 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
154 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
155 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157 #define COLLECT_TIME 60000
159 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
161 struct HttpAuthInfo
163 LPWSTR scheme;
164 CredHandle cred;
165 CtxtHandle ctx;
166 TimeStamp exp;
167 ULONG attr;
168 ULONG max_token;
169 void *auth_data;
170 unsigned int auth_data_len;
171 BOOL finished; /* finished authenticating */
175 typedef struct _basicAuthorizationData
177 struct list entry;
179 LPWSTR host;
180 LPWSTR realm;
181 LPSTR authorization;
182 UINT authorizationLen;
183 } basicAuthorizationData;
185 typedef struct _authorizationData
187 struct list entry;
189 LPWSTR host;
190 LPWSTR scheme;
191 LPWSTR domain;
192 UINT domain_len;
193 LPWSTR user;
194 UINT user_len;
195 LPWSTR password;
196 UINT password_len;
197 } authorizationData;
199 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
200 static struct list authorizationCache = LIST_INIT(authorizationCache);
202 static CRITICAL_SECTION authcache_cs;
203 static CRITICAL_SECTION_DEBUG critsect_debug =
205 0, 0, &authcache_cs,
206 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
207 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
209 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
211 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
212 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
213 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
214 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
215 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
216 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
217 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
218 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
219 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
220 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
221 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
222 static BOOL drain_content(http_request_t*,BOOL);
224 static CRITICAL_SECTION connection_pool_cs;
225 static CRITICAL_SECTION_DEBUG connection_pool_debug =
227 0, 0, &connection_pool_cs,
228 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
229 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
231 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
233 static struct list connection_pool = LIST_INIT(connection_pool);
234 static BOOL collector_running;
236 void server_addref(server_t *server)
238 InterlockedIncrement(&server->ref);
241 void server_release(server_t *server)
243 if(InterlockedDecrement(&server->ref))
244 return;
246 list_remove(&server->entry);
248 if(server->cert_chain)
249 CertFreeCertificateChain(server->cert_chain);
250 heap_free(server->name);
251 heap_free(server);
254 server_t *get_server(const WCHAR *name, INTERNET_PORT port, BOOL do_create)
256 server_t *iter, *server = NULL;
258 EnterCriticalSection(&connection_pool_cs);
260 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
261 if(iter->port == port && !strcmpW(iter->name, name)) {
262 server = iter;
263 server_addref(server);
264 break;
268 if(!server && do_create) {
269 server = heap_alloc_zero(sizeof(*server));
270 if(server) {
271 server->ref = 2; /* list reference and return */
272 server->port = port;
273 list_init(&server->conn_pool);
274 server->name = heap_strdupW(name);
275 if(server->name) {
276 list_add_head(&connection_pool, &server->entry);
277 }else {
278 heap_free(server);
279 server = NULL;
284 LeaveCriticalSection(&connection_pool_cs);
286 return server;
289 BOOL collect_connections(collect_type_t collect_type)
291 netconn_t *netconn, *netconn_safe;
292 server_t *server, *server_safe;
293 BOOL remaining = FALSE;
294 DWORD64 now;
296 now = GetTickCount64();
298 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
299 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
300 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
301 TRACE("freeing %p\n", netconn);
302 list_remove(&netconn->pool_entry);
303 free_netconn(netconn);
304 }else {
305 remaining = TRUE;
309 if(collect_type == COLLECT_CLEANUP) {
310 list_remove(&server->entry);
311 list_init(&server->entry);
312 server_release(server);
316 return remaining;
319 static DWORD WINAPI collect_connections_proc(void *arg)
321 BOOL remaining_conns;
323 do {
324 /* FIXME: Use more sophisticated method */
325 Sleep(5000);
327 EnterCriticalSection(&connection_pool_cs);
329 remaining_conns = collect_connections(COLLECT_TIMEOUT);
330 if(!remaining_conns)
331 collector_running = FALSE;
333 LeaveCriticalSection(&connection_pool_cs);
334 }while(remaining_conns);
336 FreeLibraryAndExitThread(WININET_hModule, 0);
339 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
341 int HeaderIndex = 0;
342 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
343 if (HeaderIndex == -1)
344 return NULL;
345 else
346 return &req->custHeaders[HeaderIndex];
349 typedef enum {
350 READMODE_SYNC,
351 READMODE_ASYNC,
352 READMODE_NOBLOCK
353 } read_mode_t;
355 struct data_stream_vtbl_t {
356 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
357 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
358 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
359 BOOL (*drain_content)(data_stream_t*,http_request_t*);
360 void (*destroy)(data_stream_t*);
363 typedef struct {
364 data_stream_t data_stream;
366 BYTE buf[READ_BUFFER_SIZE];
367 DWORD buf_size;
368 DWORD buf_pos;
369 DWORD chunk_size;
370 } chunked_stream_t;
372 static inline void destroy_data_stream(data_stream_t *stream)
374 stream->vtbl->destroy(stream);
377 static void reset_data_stream(http_request_t *req)
379 destroy_data_stream(req->data_stream);
380 req->data_stream = &req->netconn_stream.data_stream;
381 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
382 req->read_chunked = req->read_gzip = FALSE;
385 #ifdef HAVE_ZLIB
387 typedef struct {
388 data_stream_t stream;
389 data_stream_t *parent_stream;
390 z_stream zstream;
391 BYTE buf[READ_BUFFER_SIZE];
392 DWORD buf_size;
393 DWORD buf_pos;
394 BOOL end_of_data;
395 } gzip_stream_t;
397 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
399 /* Allow reading only from read buffer */
400 return 0;
403 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
405 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
406 return gzip_stream->end_of_data;
409 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
410 DWORD *read, read_mode_t read_mode)
412 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
413 z_stream *zstream = &gzip_stream->zstream;
414 DWORD current_read, ret_read = 0;
415 BOOL end;
416 int zres;
417 DWORD res = ERROR_SUCCESS;
419 while(size && !gzip_stream->end_of_data) {
420 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
422 if(gzip_stream->buf_size <= 64 && !end) {
423 if(gzip_stream->buf_pos) {
424 if(gzip_stream->buf_size)
425 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
426 gzip_stream->buf_pos = 0;
428 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
429 sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
430 gzip_stream->buf_size += current_read;
431 if(res != ERROR_SUCCESS)
432 break;
433 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
434 if(!current_read && !end) {
435 if(read_mode != READMODE_NOBLOCK) {
436 WARN("unexpected end of data\n");
437 gzip_stream->end_of_data = TRUE;
439 break;
441 if(gzip_stream->buf_size <= 64 && !end)
442 continue;
445 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
446 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
447 zstream->next_out = buf+ret_read;
448 zstream->avail_out = size;
449 zres = inflate(&gzip_stream->zstream, 0);
450 current_read = size - zstream->avail_out;
451 size -= current_read;
452 ret_read += current_read;
453 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
454 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
455 if(zres == Z_STREAM_END) {
456 TRACE("end of data\n");
457 gzip_stream->end_of_data = TRUE;
458 inflateEnd(zstream);
459 }else if(zres != Z_OK) {
460 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
461 if(!ret_read)
462 res = ERROR_INTERNET_DECODING_FAILED;
463 break;
466 if(ret_read && read_mode == READMODE_ASYNC)
467 read_mode = READMODE_NOBLOCK;
470 TRACE("read %u bytes\n", ret_read);
471 *read = ret_read;
472 return res;
475 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
477 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
478 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
481 static void gzip_destroy(data_stream_t *stream)
483 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
485 destroy_data_stream(gzip_stream->parent_stream);
487 if(!gzip_stream->end_of_data)
488 inflateEnd(&gzip_stream->zstream);
489 heap_free(gzip_stream);
492 static const data_stream_vtbl_t gzip_stream_vtbl = {
493 gzip_get_avail_data,
494 gzip_end_of_data,
495 gzip_read,
496 gzip_drain_content,
497 gzip_destroy
500 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
502 return heap_alloc(items*size);
505 static void wininet_zfree(voidpf opaque, voidpf address)
507 heap_free(address);
510 static DWORD init_gzip_stream(http_request_t *req)
512 gzip_stream_t *gzip_stream;
513 int index, zres;
515 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
516 if(!gzip_stream)
517 return ERROR_OUTOFMEMORY;
519 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
520 gzip_stream->zstream.zalloc = wininet_zalloc;
521 gzip_stream->zstream.zfree = wininet_zfree;
523 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
524 if(zres != Z_OK) {
525 ERR("inflateInit failed: %d\n", zres);
526 heap_free(gzip_stream);
527 return ERROR_OUTOFMEMORY;
530 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
531 if(index != -1)
532 HTTP_DeleteCustomHeader(req, index);
534 if(req->read_size) {
535 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
536 gzip_stream->buf_size = req->read_size;
537 req->read_pos = req->read_size = 0;
540 req->read_gzip = TRUE;
541 gzip_stream->parent_stream = req->data_stream;
542 req->data_stream = &gzip_stream->stream;
543 return ERROR_SUCCESS;
546 #else
548 static DWORD init_gzip_stream(http_request_t *req)
550 ERR("gzip stream not supported, missing zlib.\n");
551 return ERROR_SUCCESS;
554 #endif
556 /***********************************************************************
557 * HTTP_Tokenize (internal)
559 * Tokenize a string, allocating memory for the tokens.
561 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
563 LPWSTR * token_array;
564 int tokens = 0;
565 int i;
566 LPCWSTR next_token;
568 if (string)
570 /* empty string has no tokens */
571 if (*string)
572 tokens++;
573 /* count tokens */
574 for (i = 0; string[i]; i++)
576 if (!strncmpW(string+i, token_string, strlenW(token_string)))
578 DWORD j;
579 tokens++;
580 /* we want to skip over separators, but not the null terminator */
581 for (j = 0; j < strlenW(token_string) - 1; j++)
582 if (!string[i+j])
583 break;
584 i += j;
589 /* add 1 for terminating NULL */
590 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
591 token_array[tokens] = NULL;
592 if (!tokens)
593 return token_array;
594 for (i = 0; i < tokens; i++)
596 int len;
597 next_token = strstrW(string, token_string);
598 if (!next_token) next_token = string+strlenW(string);
599 len = next_token - string;
600 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
601 memcpy(token_array[i], string, len*sizeof(WCHAR));
602 token_array[i][len] = '\0';
603 string = next_token+strlenW(token_string);
605 return token_array;
608 /***********************************************************************
609 * HTTP_FreeTokens (internal)
611 * Frees memory returned from HTTP_Tokenize.
613 static void HTTP_FreeTokens(LPWSTR * token_array)
615 int i;
616 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
617 heap_free(token_array);
620 static void HTTP_FixURL(http_request_t *request)
622 static const WCHAR szSlash[] = { '/',0 };
623 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
625 /* If we don't have a path we set it to root */
626 if (NULL == request->path)
627 request->path = heap_strdupW(szSlash);
628 else /* remove \r and \n*/
630 int nLen = strlenW(request->path);
631 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
633 nLen--;
634 request->path[nLen]='\0';
636 /* Replace '\' with '/' */
637 while (nLen>0) {
638 nLen--;
639 if (request->path[nLen] == '\\') request->path[nLen]='/';
643 if(CSTR_EQUAL != CompareStringW( LOCALE_INVARIANT, NORM_IGNORECASE,
644 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
645 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
647 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
648 *fixurl = '/';
649 strcpyW(fixurl + 1, request->path);
650 heap_free( request->path );
651 request->path = fixurl;
655 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
657 LPWSTR requestString;
658 DWORD len, n;
659 LPCWSTR *req;
660 UINT i;
661 LPWSTR p;
663 static const WCHAR szSpace[] = { ' ',0 };
664 static const WCHAR szColon[] = { ':',' ',0 };
665 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
667 /* allocate space for an array of all the string pointers to be added */
668 len = (request->nCustHeaders)*4 + 10;
669 req = heap_alloc(len*sizeof(LPCWSTR));
671 /* add the verb, path and HTTP version string */
672 n = 0;
673 req[n++] = verb;
674 req[n++] = szSpace;
675 req[n++] = path;
676 req[n++] = szSpace;
677 req[n++] = version;
679 /* Append custom request headers */
680 for (i = 0; i < request->nCustHeaders; i++)
682 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
684 req[n++] = szCrLf;
685 req[n++] = request->custHeaders[i].lpszField;
686 req[n++] = szColon;
687 req[n++] = request->custHeaders[i].lpszValue;
689 TRACE("Adding custom header %s (%s)\n",
690 debugstr_w(request->custHeaders[i].lpszField),
691 debugstr_w(request->custHeaders[i].lpszValue));
695 if( n >= len )
696 ERR("oops. buffer overrun\n");
698 req[n] = NULL;
699 requestString = HTTP_build_req( req, 4 );
700 heap_free( req );
703 * Set (header) termination string for request
704 * Make sure there's exactly two new lines at the end of the request
706 p = &requestString[strlenW(requestString)-1];
707 while ( (*p == '\n') || (*p == '\r') )
708 p--;
709 strcpyW( p+1, sztwocrlf );
711 return requestString;
714 static void HTTP_ProcessCookies( http_request_t *request )
716 int HeaderIndex;
717 int numCookies = 0;
718 LPHTTPHEADERW setCookieHeader;
720 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
721 return;
723 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
725 HTTPHEADERW *host;
726 const WCHAR *data;
727 WCHAR *name;
729 setCookieHeader = &request->custHeaders[HeaderIndex];
731 if (!setCookieHeader->lpszValue)
732 continue;
734 host = HTTP_GetHeader(request, hostW);
735 if(!host)
736 continue;
738 data = strchrW(setCookieHeader->lpszValue, '=');
739 if(!data)
740 continue;
742 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
743 if(!name)
744 continue;
746 data++;
747 set_cookie(host->lpszValue, request->path, name, data);
748 heap_free(name);
752 static void strip_spaces(LPWSTR start)
754 LPWSTR str = start;
755 LPWSTR end;
757 while (*str == ' ' && *str != '\0')
758 str++;
760 if (str != start)
761 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
763 end = start + strlenW(start) - 1;
764 while (end >= start && *end == ' ')
766 *end = '\0';
767 end--;
771 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
773 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
774 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
775 BOOL is_basic;
776 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
777 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
778 if (is_basic && pszRealm)
780 LPCWSTR token;
781 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
782 LPCWSTR realm;
783 ptr++;
784 *pszRealm=NULL;
785 token = strchrW(ptr,'=');
786 if (!token)
787 return TRUE;
788 realm = ptr;
789 while (*realm == ' ' && *realm != '\0')
790 realm++;
791 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
792 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
794 token++;
795 while (*token == ' ' && *token != '\0')
796 token++;
797 if (*token == '\0')
798 return TRUE;
799 *pszRealm = heap_strdupW(token);
800 strip_spaces(*pszRealm);
804 return is_basic;
807 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
809 if (!authinfo) return;
811 if (SecIsValidHandle(&authinfo->ctx))
812 DeleteSecurityContext(&authinfo->ctx);
813 if (SecIsValidHandle(&authinfo->cred))
814 FreeCredentialsHandle(&authinfo->cred);
816 heap_free(authinfo->auth_data);
817 heap_free(authinfo->scheme);
818 heap_free(authinfo);
821 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
823 basicAuthorizationData *ad;
824 UINT rc = 0;
826 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
828 EnterCriticalSection(&authcache_cs);
829 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
831 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
833 TRACE("Authorization found in cache\n");
834 *auth_data = heap_alloc(ad->authorizationLen);
835 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
836 rc = ad->authorizationLen;
837 break;
840 LeaveCriticalSection(&authcache_cs);
841 return rc;
844 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
846 struct list *cursor;
847 basicAuthorizationData* ad = NULL;
849 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
851 EnterCriticalSection(&authcache_cs);
852 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
854 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
855 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
857 ad = check;
858 break;
862 if (ad)
864 TRACE("Found match in cache, replacing\n");
865 heap_free(ad->authorization);
866 ad->authorization = heap_alloc(auth_data_len);
867 memcpy(ad->authorization, auth_data, auth_data_len);
868 ad->authorizationLen = auth_data_len;
870 else
872 ad = heap_alloc(sizeof(basicAuthorizationData));
873 ad->host = heap_strdupW(host);
874 ad->realm = heap_strdupW(realm);
875 ad->authorization = heap_alloc(auth_data_len);
876 memcpy(ad->authorization, auth_data, auth_data_len);
877 ad->authorizationLen = auth_data_len;
878 list_add_head(&basicAuthorizationCache,&ad->entry);
879 TRACE("authorization cached\n");
881 LeaveCriticalSection(&authcache_cs);
884 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
885 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
887 authorizationData *ad;
889 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
891 EnterCriticalSection(&authcache_cs);
892 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
893 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
894 TRACE("Authorization found in cache\n");
896 nt_auth_identity->User = heap_strdupW(ad->user);
897 nt_auth_identity->Password = heap_strdupW(ad->password);
898 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
899 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
900 (!nt_auth_identity->Domain && ad->domain_len)) {
901 heap_free(nt_auth_identity->User);
902 heap_free(nt_auth_identity->Password);
903 heap_free(nt_auth_identity->Domain);
904 break;
907 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
908 nt_auth_identity->UserLength = ad->user_len;
909 nt_auth_identity->PasswordLength = ad->password_len;
910 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
911 nt_auth_identity->DomainLength = ad->domain_len;
912 LeaveCriticalSection(&authcache_cs);
913 return TRUE;
916 LeaveCriticalSection(&authcache_cs);
918 return FALSE;
921 static void cache_authorization(LPWSTR host, LPWSTR scheme,
922 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
924 authorizationData *ad;
925 BOOL found = FALSE;
927 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
929 EnterCriticalSection(&authcache_cs);
930 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
931 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
932 found = TRUE;
933 break;
936 if(found) {
937 heap_free(ad->user);
938 heap_free(ad->password);
939 heap_free(ad->domain);
940 } else {
941 ad = heap_alloc(sizeof(authorizationData));
942 if(!ad) {
943 LeaveCriticalSection(&authcache_cs);
944 return;
947 ad->host = heap_strdupW(host);
948 ad->scheme = heap_strdupW(scheme);
949 list_add_head(&authorizationCache, &ad->entry);
952 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
953 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
954 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
955 ad->user_len = nt_auth_identity->UserLength;
956 ad->password_len = nt_auth_identity->PasswordLength;
957 ad->domain_len = nt_auth_identity->DomainLength;
959 if(!ad->host || !ad->scheme || !ad->user || !ad->password
960 || (nt_auth_identity->Domain && !ad->domain)) {
961 heap_free(ad->host);
962 heap_free(ad->scheme);
963 heap_free(ad->user);
964 heap_free(ad->password);
965 heap_free(ad->domain);
966 list_remove(&ad->entry);
967 heap_free(ad);
970 LeaveCriticalSection(&authcache_cs);
973 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
974 struct HttpAuthInfo **ppAuthInfo,
975 LPWSTR domain_and_username, LPWSTR password,
976 LPWSTR host )
978 SECURITY_STATUS sec_status;
979 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
980 BOOL first = FALSE;
981 LPWSTR szRealm = NULL;
983 TRACE("%s\n", debugstr_w(pszAuthValue));
985 if (!pAuthInfo)
987 TimeStamp exp;
989 first = TRUE;
990 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
991 if (!pAuthInfo)
992 return FALSE;
994 SecInvalidateHandle(&pAuthInfo->cred);
995 SecInvalidateHandle(&pAuthInfo->ctx);
996 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
997 pAuthInfo->attr = 0;
998 pAuthInfo->auth_data = NULL;
999 pAuthInfo->auth_data_len = 0;
1000 pAuthInfo->finished = FALSE;
1002 if (is_basic_auth_value(pszAuthValue,NULL))
1004 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1005 pAuthInfo->scheme = heap_strdupW(szBasic);
1006 if (!pAuthInfo->scheme)
1008 heap_free(pAuthInfo);
1009 return FALSE;
1012 else
1014 PVOID pAuthData;
1015 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1017 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1018 if (!pAuthInfo->scheme)
1020 heap_free(pAuthInfo);
1021 return FALSE;
1024 if (domain_and_username)
1026 WCHAR *user = strchrW(domain_and_username, '\\');
1027 WCHAR *domain = domain_and_username;
1029 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1031 pAuthData = &nt_auth_identity;
1033 if (user) user++;
1034 else
1036 user = domain_and_username;
1037 domain = NULL;
1040 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1041 nt_auth_identity.User = user;
1042 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1043 nt_auth_identity.Domain = domain;
1044 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1045 nt_auth_identity.Password = password;
1046 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1048 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1050 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1051 pAuthData = &nt_auth_identity;
1052 else
1053 /* use default credentials */
1054 pAuthData = NULL;
1056 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1057 SECPKG_CRED_OUTBOUND, NULL,
1058 pAuthData, NULL,
1059 NULL, &pAuthInfo->cred,
1060 &exp);
1062 if(pAuthData && !domain_and_username) {
1063 heap_free(nt_auth_identity.User);
1064 heap_free(nt_auth_identity.Domain);
1065 heap_free(nt_auth_identity.Password);
1068 if (sec_status == SEC_E_OK)
1070 PSecPkgInfoW sec_pkg_info;
1071 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1072 if (sec_status == SEC_E_OK)
1074 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1075 FreeContextBuffer(sec_pkg_info);
1078 if (sec_status != SEC_E_OK)
1080 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1081 debugstr_w(pAuthInfo->scheme), sec_status);
1082 heap_free(pAuthInfo->scheme);
1083 heap_free(pAuthInfo);
1084 return FALSE;
1087 *ppAuthInfo = pAuthInfo;
1089 else if (pAuthInfo->finished)
1090 return FALSE;
1092 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1093 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1095 ERR("authentication scheme changed from %s to %s\n",
1096 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1097 return FALSE;
1100 if (is_basic_auth_value(pszAuthValue,&szRealm))
1102 int userlen;
1103 int passlen;
1104 char *auth_data = NULL;
1105 UINT auth_data_len = 0;
1107 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1109 if (!domain_and_username)
1111 if (host && szRealm)
1112 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1113 if (auth_data_len == 0)
1115 heap_free(szRealm);
1116 return FALSE;
1119 else
1121 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1122 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1124 /* length includes a nul terminator, which will be re-used for the ':' */
1125 auth_data = heap_alloc(userlen + 1 + passlen);
1126 if (!auth_data)
1128 heap_free(szRealm);
1129 return FALSE;
1132 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1133 auth_data[userlen] = ':';
1134 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1135 auth_data_len = userlen + 1 + passlen;
1136 if (host && szRealm)
1137 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1140 pAuthInfo->auth_data = auth_data;
1141 pAuthInfo->auth_data_len = auth_data_len;
1142 pAuthInfo->finished = TRUE;
1143 heap_free(szRealm);
1144 return TRUE;
1146 else
1148 LPCWSTR pszAuthData;
1149 SecBufferDesc out_desc, in_desc;
1150 SecBuffer out, in;
1151 unsigned char *buffer;
1152 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1153 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1155 in.BufferType = SECBUFFER_TOKEN;
1156 in.cbBuffer = 0;
1157 in.pvBuffer = NULL;
1159 in_desc.ulVersion = 0;
1160 in_desc.cBuffers = 1;
1161 in_desc.pBuffers = &in;
1163 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1164 if (*pszAuthData == ' ')
1166 pszAuthData++;
1167 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1168 in.pvBuffer = heap_alloc(in.cbBuffer);
1169 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1172 buffer = heap_alloc(pAuthInfo->max_token);
1174 out.BufferType = SECBUFFER_TOKEN;
1175 out.cbBuffer = pAuthInfo->max_token;
1176 out.pvBuffer = buffer;
1178 out_desc.ulVersion = 0;
1179 out_desc.cBuffers = 1;
1180 out_desc.pBuffers = &out;
1182 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1183 first ? NULL : &pAuthInfo->ctx,
1184 first ? request->server->name : NULL,
1185 context_req, 0, SECURITY_NETWORK_DREP,
1186 in.pvBuffer ? &in_desc : NULL,
1187 0, &pAuthInfo->ctx, &out_desc,
1188 &pAuthInfo->attr, &pAuthInfo->exp);
1189 if (sec_status == SEC_E_OK)
1191 pAuthInfo->finished = TRUE;
1192 pAuthInfo->auth_data = out.pvBuffer;
1193 pAuthInfo->auth_data_len = out.cbBuffer;
1194 TRACE("sending last auth packet\n");
1196 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1198 pAuthInfo->auth_data = out.pvBuffer;
1199 pAuthInfo->auth_data_len = out.cbBuffer;
1200 TRACE("sending next auth packet\n");
1202 else
1204 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1205 heap_free(out.pvBuffer);
1206 destroy_authinfo(pAuthInfo);
1207 *ppAuthInfo = NULL;
1208 return FALSE;
1212 return TRUE;
1215 /***********************************************************************
1216 * HTTP_HttpAddRequestHeadersW (internal)
1218 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1219 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1221 LPWSTR lpszStart;
1222 LPWSTR lpszEnd;
1223 LPWSTR buffer;
1224 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1226 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1228 if( dwHeaderLength == ~0U )
1229 len = strlenW(lpszHeader);
1230 else
1231 len = dwHeaderLength;
1232 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1233 lstrcpynW( buffer, lpszHeader, len + 1);
1235 lpszStart = buffer;
1239 LPWSTR * pFieldAndValue;
1241 lpszEnd = lpszStart;
1243 while (*lpszEnd != '\0')
1245 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1246 break;
1247 lpszEnd++;
1250 if (*lpszStart == '\0')
1251 break;
1253 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1255 *lpszEnd = '\0';
1256 lpszEnd++; /* Jump over newline */
1258 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1259 if (*lpszStart == '\0')
1261 /* Skip 0-length headers */
1262 lpszStart = lpszEnd;
1263 res = ERROR_SUCCESS;
1264 continue;
1266 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1267 if (pFieldAndValue)
1269 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1270 if (res == ERROR_SUCCESS)
1271 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1272 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1273 HTTP_FreeTokens(pFieldAndValue);
1276 lpszStart = lpszEnd;
1277 } while (res == ERROR_SUCCESS);
1279 heap_free(buffer);
1280 return res;
1283 /***********************************************************************
1284 * HttpAddRequestHeadersW (WININET.@)
1286 * Adds one or more HTTP header to the request handler
1288 * NOTE
1289 * On Windows if dwHeaderLength includes the trailing '\0', then
1290 * HttpAddRequestHeadersW() adds it too. However this results in an
1291 * invalid HTTP header which is rejected by some servers so we probably
1292 * don't need to match Windows on that point.
1294 * RETURNS
1295 * TRUE on success
1296 * FALSE on failure
1299 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1300 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1302 http_request_t *request;
1303 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1305 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1307 if (!lpszHeader)
1308 return TRUE;
1310 request = (http_request_t*) get_handle_object( hHttpRequest );
1311 if (request && request->hdr.htype == WH_HHTTPREQ)
1312 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1313 if( request )
1314 WININET_Release( &request->hdr );
1316 if(res != ERROR_SUCCESS)
1317 SetLastError(res);
1318 return res == ERROR_SUCCESS;
1321 /***********************************************************************
1322 * HttpAddRequestHeadersA (WININET.@)
1324 * Adds one or more HTTP header to the request handler
1326 * RETURNS
1327 * TRUE on success
1328 * FALSE on failure
1331 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1332 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1334 DWORD len;
1335 LPWSTR hdr;
1336 BOOL r;
1338 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1340 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1341 hdr = heap_alloc(len*sizeof(WCHAR));
1342 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1343 if( dwHeaderLength != ~0U )
1344 dwHeaderLength = len;
1346 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1348 heap_free( hdr );
1349 return r;
1352 static void free_accept_types( WCHAR **accept_types )
1354 WCHAR *ptr, **types = accept_types;
1356 if (!types) return;
1357 while ((ptr = *types))
1359 heap_free( ptr );
1360 types++;
1362 heap_free( accept_types );
1365 static WCHAR **convert_accept_types( const char **accept_types )
1367 unsigned int count;
1368 const char **types = accept_types;
1369 WCHAR **typesW;
1370 BOOL invalid_pointer = FALSE;
1372 if (!types) return NULL;
1373 count = 0;
1374 while (*types)
1376 __TRY
1378 /* find out how many there are */
1379 if (*types && **types)
1381 TRACE("accept type: %s\n", debugstr_a(*types));
1382 count++;
1385 __EXCEPT_PAGE_FAULT
1387 WARN("invalid accept type pointer\n");
1388 invalid_pointer = TRUE;
1390 __ENDTRY;
1391 types++;
1393 if (invalid_pointer) return NULL;
1394 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1395 count = 0;
1396 types = accept_types;
1397 while (*types)
1399 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1400 types++;
1402 typesW[count] = NULL;
1403 return typesW;
1406 /***********************************************************************
1407 * HttpOpenRequestA (WININET.@)
1409 * Open a HTTP request handle
1411 * RETURNS
1412 * HINTERNET a HTTP request handle on success
1413 * NULL on failure
1416 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1417 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1418 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1419 DWORD dwFlags, DWORD_PTR dwContext)
1421 LPWSTR szVerb = NULL, szObjectName = NULL;
1422 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1423 HINTERNET rc = FALSE;
1425 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1426 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1427 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1428 dwFlags, dwContext);
1430 if (lpszVerb)
1432 szVerb = heap_strdupAtoW(lpszVerb);
1433 if ( !szVerb )
1434 goto end;
1437 if (lpszObjectName)
1439 szObjectName = heap_strdupAtoW(lpszObjectName);
1440 if ( !szObjectName )
1441 goto end;
1444 if (lpszVersion)
1446 szVersion = heap_strdupAtoW(lpszVersion);
1447 if ( !szVersion )
1448 goto end;
1451 if (lpszReferrer)
1453 szReferrer = heap_strdupAtoW(lpszReferrer);
1454 if ( !szReferrer )
1455 goto end;
1458 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1459 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1460 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1462 end:
1463 free_accept_types(szAcceptTypes);
1464 heap_free(szReferrer);
1465 heap_free(szVersion);
1466 heap_free(szObjectName);
1467 heap_free(szVerb);
1468 return rc;
1471 /***********************************************************************
1472 * HTTP_EncodeBase64
1474 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1476 UINT n = 0, x;
1477 static const CHAR HTTP_Base64Enc[] =
1478 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1480 while( len > 0 )
1482 /* first 6 bits, all from bin[0] */
1483 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1484 x = (bin[0] & 3) << 4;
1486 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1487 if( len == 1 )
1489 base64[n++] = HTTP_Base64Enc[x];
1490 base64[n++] = '=';
1491 base64[n++] = '=';
1492 break;
1494 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1495 x = ( bin[1] & 0x0f ) << 2;
1497 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1498 if( len == 2 )
1500 base64[n++] = HTTP_Base64Enc[x];
1501 base64[n++] = '=';
1502 break;
1504 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1506 /* last 6 bits, all from bin [2] */
1507 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1508 bin += 3;
1509 len -= 3;
1511 base64[n] = 0;
1512 return n;
1515 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1516 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1517 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1518 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1519 static const signed char HTTP_Base64Dec[256] =
1521 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1522 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1523 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1524 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1525 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1526 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1527 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1528 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1529 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1530 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1531 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1532 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1533 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1534 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1535 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1536 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1537 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1538 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1539 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1540 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1541 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1542 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1543 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1544 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1545 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1546 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1548 #undef CH
1550 /***********************************************************************
1551 * HTTP_DecodeBase64
1553 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1555 unsigned int n = 0;
1557 while(*base64)
1559 signed char in[4];
1561 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1562 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1563 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1564 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1566 WARN("invalid base64: %s\n", debugstr_w(base64));
1567 return 0;
1569 if (bin)
1570 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1571 n++;
1573 if ((base64[2] == '=') && (base64[3] == '='))
1574 break;
1575 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1576 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1578 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1579 return 0;
1581 if (bin)
1582 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1583 n++;
1585 if (base64[3] == '=')
1586 break;
1587 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1588 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1590 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1591 return 0;
1593 if (bin)
1594 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1595 n++;
1597 base64 += 4;
1600 return n;
1603 /***********************************************************************
1604 * HTTP_InsertAuthorization
1606 * Insert or delete the authorization field in the request header.
1608 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1610 if (pAuthInfo)
1612 static const WCHAR wszSpace[] = {' ',0};
1613 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1614 unsigned int len;
1615 WCHAR *authorization = NULL;
1617 if (pAuthInfo->auth_data_len)
1619 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1620 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1621 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1622 if (!authorization)
1623 return FALSE;
1625 strcpyW(authorization, pAuthInfo->scheme);
1626 strcatW(authorization, wszSpace);
1627 HTTP_EncodeBase64(pAuthInfo->auth_data,
1628 pAuthInfo->auth_data_len,
1629 authorization+strlenW(authorization));
1631 /* clear the data as it isn't valid now that it has been sent to the
1632 * server, unless it's Basic authentication which doesn't do
1633 * connection tracking */
1634 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1636 heap_free(pAuthInfo->auth_data);
1637 pAuthInfo->auth_data = NULL;
1638 pAuthInfo->auth_data_len = 0;
1642 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1644 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1645 heap_free(authorization);
1647 return TRUE;
1650 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1652 static const WCHAR slash[] = { '/',0 };
1653 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1654 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1655 http_session_t *session = req->session;
1656 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1657 DWORD size;
1659 size = sizeof(new_location);
1660 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1662 URL_COMPONENTSW UrlComponents;
1664 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1665 strcpyW( url, new_location );
1667 ZeroMemory(&UrlComponents,sizeof(URL_COMPONENTSW));
1668 if(InternetCrackUrlW(url, 0, 0, &UrlComponents)) goto done;
1669 heap_free(url);
1672 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1673 size += strlenW( session->hostName ) + strlenW( req->path );
1675 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1677 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1678 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1679 else
1680 sprintfW( url, format, session->hostName, session->hostPort );
1681 if (req->path[0] != '/') strcatW( url, slash );
1682 strcatW( url, req->path );
1684 done:
1685 TRACE("url=%s\n", debugstr_w(url));
1686 return url;
1689 /***********************************************************************
1690 * HTTP_DealWithProxy
1692 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1694 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1695 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1696 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1697 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1698 static WCHAR szNul[] = { 0 };
1699 URL_COMPONENTSW UrlComponents;
1700 server_t *new_server;
1701 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1702 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1703 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1705 memset( &UrlComponents, 0, sizeof UrlComponents );
1706 UrlComponents.dwStructSize = sizeof UrlComponents;
1707 UrlComponents.lpszHostName = buf;
1708 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1710 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1711 return FALSE;
1712 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1713 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1714 sprintfW(proxy, szFormat, protoProxy);
1715 else
1716 strcpyW(proxy, protoProxy);
1717 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1718 return FALSE;
1719 if( UrlComponents.dwHostNameLength == 0 )
1720 return FALSE;
1722 if( !request->path )
1723 request->path = szNul;
1725 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1726 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1728 new_server = get_server(UrlComponents.lpszHostName, UrlComponents.nPort, TRUE);
1729 if(!new_server)
1730 return FALSE;
1732 request->proxy = new_server;
1734 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1735 return TRUE;
1738 static DWORD HTTP_ResolveName(http_request_t *request)
1740 server_t *server = request->proxy ? request->proxy : request->server;
1741 socklen_t addr_len;
1742 void *addr;
1744 if(server->addr_len)
1745 return ERROR_SUCCESS;
1747 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1748 INTERNET_STATUS_RESOLVING_NAME,
1749 server->name,
1750 (strlenW(server->name)+1) * sizeof(WCHAR));
1752 addr_len = sizeof(server->addr);
1753 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1754 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1756 switch(server->addr.ss_family) {
1757 case AF_INET:
1758 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1759 break;
1760 case AF_INET6:
1761 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1762 break;
1763 default:
1764 WARN("unsupported family %d\n", server->addr.ss_family);
1765 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1768 server->addr_len = addr_len;
1769 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1770 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1771 INTERNET_STATUS_NAME_RESOLVED,
1772 server->addr_str, strlen(server->addr_str)+1);
1774 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1775 return ERROR_SUCCESS;
1778 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1780 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1781 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1782 static const WCHAR slash[] = { '/',0 };
1783 LPHTTPHEADERW host_header;
1784 LPCWSTR scheme;
1786 host_header = HTTP_GetHeader(req, hostW);
1787 if(!host_header)
1788 return FALSE;
1790 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1791 scheme = https;
1792 else
1793 scheme = http;
1794 strcpyW(buf, scheme);
1795 strcatW(buf, host_header->lpszValue);
1796 if (req->path[0] != '/')
1797 strcatW(buf, slash);
1798 strcatW(buf, req->path);
1799 return TRUE;
1803 /***********************************************************************
1804 * HTTPREQ_Destroy (internal)
1806 * Deallocate request handle
1809 static void HTTPREQ_Destroy(object_header_t *hdr)
1811 http_request_t *request = (http_request_t*) hdr;
1812 DWORD i;
1814 TRACE("\n");
1816 if(request->hCacheFile) {
1817 CloseHandle(request->hCacheFile);
1818 DeleteFileW(request->cacheFile);
1820 heap_free(request->cacheFile);
1822 request->read_section.DebugInfo->Spare[0] = 0;
1823 DeleteCriticalSection( &request->read_section );
1824 WININET_Release(&request->session->hdr);
1826 destroy_authinfo(request->authInfo);
1827 destroy_authinfo(request->proxyAuthInfo);
1829 if(request->server)
1830 server_release(request->server);
1831 if(request->proxy)
1832 server_release(request->proxy);
1834 heap_free(request->path);
1835 heap_free(request->verb);
1836 heap_free(request->rawHeaders);
1837 heap_free(request->version);
1838 heap_free(request->statusText);
1840 for (i = 0; i < request->nCustHeaders; i++)
1842 heap_free(request->custHeaders[i].lpszField);
1843 heap_free(request->custHeaders[i].lpszValue);
1845 destroy_data_stream(request->data_stream);
1846 heap_free(request->custHeaders);
1849 static void http_release_netconn(http_request_t *req, BOOL reuse)
1851 TRACE("%p %p\n",req, req->netconn);
1853 if(!req->netconn)
1854 return;
1856 if(reuse && req->netconn->keep_alive) {
1857 BOOL run_collector;
1859 EnterCriticalSection(&connection_pool_cs);
1861 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1862 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1863 req->netconn = NULL;
1865 run_collector = !collector_running;
1866 collector_running = TRUE;
1868 LeaveCriticalSection(&connection_pool_cs);
1870 if(run_collector) {
1871 HANDLE thread = NULL;
1872 HMODULE module;
1874 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1875 if(module)
1876 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1877 if(!thread) {
1878 EnterCriticalSection(&connection_pool_cs);
1879 collector_running = FALSE;
1880 LeaveCriticalSection(&connection_pool_cs);
1882 if(module)
1883 FreeLibrary(module);
1885 else
1886 CloseHandle(thread);
1888 return;
1891 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1892 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1894 free_netconn(req->netconn);
1895 req->netconn = NULL;
1897 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1898 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1901 static BOOL HTTP_KeepAlive(http_request_t *request)
1903 WCHAR szVersion[10];
1904 WCHAR szConnectionResponse[20];
1905 DWORD dwBufferSize = sizeof(szVersion);
1906 BOOL keepalive = FALSE;
1908 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1909 * the connection is keep-alive by default */
1910 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1911 && !strcmpiW(szVersion, g_szHttp1_1))
1913 keepalive = TRUE;
1916 dwBufferSize = sizeof(szConnectionResponse);
1917 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1918 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1920 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1923 return keepalive;
1926 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1928 http_request_t *req = (http_request_t*)hdr;
1930 http_release_netconn(req, drain_content(req, FALSE));
1933 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1935 http_request_t *req = (http_request_t*)hdr;
1937 switch(option) {
1938 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1940 http_session_t *session = req->session;
1941 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1943 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1945 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1946 return ERROR_INSUFFICIENT_BUFFER;
1947 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1948 /* FIXME: can't get a SOCKET from our connection since we don't use
1949 * winsock
1951 info->Socket = 0;
1952 /* FIXME: get source port from req->netConnection */
1953 info->SourcePort = 0;
1954 info->DestPort = session->hostPort;
1955 info->Flags = 0;
1956 if (HTTP_KeepAlive(req))
1957 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1958 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1959 info->Flags |= IDSI_FLAG_PROXY;
1960 if (req->netconn->useSSL)
1961 info->Flags |= IDSI_FLAG_SECURE;
1963 return ERROR_SUCCESS;
1966 case 98:
1967 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1968 /* fall through */
1969 case INTERNET_OPTION_SECURITY_FLAGS:
1971 DWORD flags;
1973 if (*size < sizeof(ULONG))
1974 return ERROR_INSUFFICIENT_BUFFER;
1976 *size = sizeof(DWORD);
1977 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
1978 *(DWORD *)buffer = flags;
1980 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
1981 return ERROR_SUCCESS;
1984 case INTERNET_OPTION_HANDLE_TYPE:
1985 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1987 if (*size < sizeof(ULONG))
1988 return ERROR_INSUFFICIENT_BUFFER;
1990 *size = sizeof(DWORD);
1991 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1992 return ERROR_SUCCESS;
1994 case INTERNET_OPTION_URL: {
1995 WCHAR url[INTERNET_MAX_URL_LENGTH];
1996 HTTPHEADERW *host;
1997 DWORD len;
1998 WCHAR *pch;
2000 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2002 TRACE("INTERNET_OPTION_URL\n");
2004 host = HTTP_GetHeader(req, hostW);
2005 strcpyW(url, httpW);
2006 strcatW(url, host->lpszValue);
2007 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2008 *pch = 0;
2009 strcatW(url, req->path);
2011 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2013 if(unicode) {
2014 len = (strlenW(url)+1) * sizeof(WCHAR);
2015 if(*size < len)
2016 return ERROR_INSUFFICIENT_BUFFER;
2018 *size = len;
2019 strcpyW(buffer, url);
2020 return ERROR_SUCCESS;
2021 }else {
2022 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2023 if(len > *size)
2024 return ERROR_INSUFFICIENT_BUFFER;
2026 *size = len;
2027 return ERROR_SUCCESS;
2031 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2032 INTERNET_CACHE_ENTRY_INFOW *info;
2033 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2034 WCHAR url[INTERNET_MAX_URL_LENGTH];
2035 DWORD nbytes, error;
2036 BOOL ret;
2038 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2040 if (*size < sizeof(*ts))
2042 *size = sizeof(*ts);
2043 return ERROR_INSUFFICIENT_BUFFER;
2045 nbytes = 0;
2046 HTTP_GetRequestURL(req, url);
2047 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2048 error = GetLastError();
2049 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2051 if (!(info = heap_alloc(nbytes)))
2052 return ERROR_OUTOFMEMORY;
2054 GetUrlCacheEntryInfoW(url, info, &nbytes);
2056 ts->ftExpires = info->ExpireTime;
2057 ts->ftLastModified = info->LastModifiedTime;
2059 heap_free(info);
2060 *size = sizeof(*ts);
2061 return ERROR_SUCCESS;
2063 return error;
2066 case INTERNET_OPTION_DATAFILE_NAME: {
2067 DWORD req_size;
2069 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2071 if(!req->cacheFile) {
2072 *size = 0;
2073 return ERROR_INTERNET_ITEM_NOT_FOUND;
2076 if(unicode) {
2077 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2078 if(*size < req_size)
2079 return ERROR_INSUFFICIENT_BUFFER;
2081 *size = req_size;
2082 memcpy(buffer, req->cacheFile, *size);
2083 return ERROR_SUCCESS;
2084 }else {
2085 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2086 if (req_size > *size)
2087 return ERROR_INSUFFICIENT_BUFFER;
2089 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2090 -1, buffer, *size, NULL, NULL);
2091 return ERROR_SUCCESS;
2095 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2096 PCCERT_CONTEXT context;
2098 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2099 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2100 return ERROR_INSUFFICIENT_BUFFER;
2103 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2104 if(context) {
2105 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2106 DWORD len;
2108 memset(info, 0, sizeof(*info));
2109 info->ftExpiry = context->pCertInfo->NotAfter;
2110 info->ftStart = context->pCertInfo->NotBefore;
2111 len = CertNameToStrA(context->dwCertEncodingType,
2112 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2113 info->lpszSubjectInfo = LocalAlloc(0, len);
2114 if(info->lpszSubjectInfo)
2115 CertNameToStrA(context->dwCertEncodingType,
2116 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2117 info->lpszSubjectInfo, len);
2118 len = CertNameToStrA(context->dwCertEncodingType,
2119 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2120 info->lpszIssuerInfo = LocalAlloc(0, len);
2121 if(info->lpszIssuerInfo)
2122 CertNameToStrA(context->dwCertEncodingType,
2123 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2124 info->lpszIssuerInfo, len);
2125 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2126 CertFreeCertificateContext(context);
2127 return ERROR_SUCCESS;
2129 return ERROR_NOT_SUPPORTED;
2131 case INTERNET_OPTION_CONNECT_TIMEOUT:
2132 if (*size < sizeof(DWORD))
2133 return ERROR_INSUFFICIENT_BUFFER;
2135 *size = sizeof(DWORD);
2136 *(DWORD *)buffer = req->connect_timeout;
2137 return ERROR_SUCCESS;
2138 case INTERNET_OPTION_REQUEST_FLAGS: {
2139 DWORD flags = 0;
2141 if (*size < sizeof(DWORD))
2142 return ERROR_INSUFFICIENT_BUFFER;
2144 /* FIXME: Add support for:
2145 * INTERNET_REQFLAG_FROM_CACHE
2146 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2149 if(req->session->appInfo->proxy)
2150 flags |= INTERNET_REQFLAG_VIA_PROXY;
2151 if(!req->rawHeaders)
2152 flags |= INTERNET_REQFLAG_NO_HEADERS;
2154 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2156 *size = sizeof(DWORD);
2157 *(DWORD*)buffer = flags;
2158 return ERROR_SUCCESS;
2162 return INET_QueryOption(hdr, option, buffer, size, unicode);
2165 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2167 http_request_t *req = (http_request_t*)hdr;
2169 switch(option) {
2170 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2171 TRACE("Undocumented option 99\n");
2173 if (!buffer || size != sizeof(DWORD))
2174 return ERROR_INVALID_PARAMETER;
2175 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2176 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2178 /* fall through */
2179 case INTERNET_OPTION_SECURITY_FLAGS:
2181 DWORD flags;
2183 if (!buffer || size != sizeof(DWORD))
2184 return ERROR_INVALID_PARAMETER;
2185 flags = *(DWORD *)buffer;
2186 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2187 flags &= SECURITY_SET_MASK;
2188 req->security_flags |= flags;
2189 if(req->netconn)
2190 req->netconn->security_flags |= flags;
2191 return ERROR_SUCCESS;
2193 case INTERNET_OPTION_CONNECT_TIMEOUT:
2194 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2195 req->connect_timeout = *(DWORD *)buffer;
2196 return ERROR_SUCCESS;
2198 case INTERNET_OPTION_SEND_TIMEOUT:
2199 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2200 req->send_timeout = *(DWORD *)buffer;
2201 return ERROR_SUCCESS;
2203 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2204 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2205 req->receive_timeout = *(DWORD *)buffer;
2206 return ERROR_SUCCESS;
2208 case INTERNET_OPTION_USERNAME:
2209 heap_free(req->session->userName);
2210 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2211 return ERROR_SUCCESS;
2213 case INTERNET_OPTION_PASSWORD:
2214 heap_free(req->session->password);
2215 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2216 return ERROR_SUCCESS;
2217 case INTERNET_OPTION_HTTP_DECODING:
2218 if(size != sizeof(BOOL))
2219 return ERROR_INVALID_PARAMETER;
2220 req->decoding = *(BOOL*)buffer;
2221 return ERROR_SUCCESS;
2224 return INET_SetOption(hdr, option, buffer, size);
2227 static void commit_cache_entry(http_request_t *req)
2229 WCHAR url[INTERNET_MAX_URL_LENGTH];
2231 TRACE("%p\n", req);
2233 CloseHandle(req->hCacheFile);
2234 req->hCacheFile = NULL;
2236 if(HTTP_GetRequestURL(req, url)) {
2237 DWORD headersLen;
2239 headersLen = req->rawHeaders ? strlenW(req->rawHeaders) : 0;
2240 CommitUrlCacheEntryW(url, req->cacheFile, req->expires,
2241 req->last_modified, NORMAL_CACHE_ENTRY,
2242 req->rawHeaders, headersLen, NULL, 0);
2246 static void create_cache_entry(http_request_t *req)
2248 WCHAR url[INTERNET_MAX_URL_LENGTH];
2249 WCHAR file_name[MAX_PATH+1];
2250 BOOL b;
2252 /* FIXME: We should free previous cache file earlier */
2253 heap_free(req->cacheFile);
2254 CloseHandle(req->hCacheFile);
2255 req->hCacheFile = NULL;
2257 b = HTTP_GetRequestURL(req, url);
2258 if(!b) {
2259 WARN("Could not get URL\n");
2260 return;
2263 b = CreateUrlCacheEntryW(url, req->contentLength, NULL, file_name, 0);
2264 if(!b) {
2265 WARN("Could not create cache entry: %08x\n", GetLastError());
2266 return;
2269 req->cacheFile = heap_strdupW(file_name);
2270 req->hCacheFile = CreateFileW(req->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2271 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2272 if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2273 WARN("Could not create file: %u\n", GetLastError());
2274 req->hCacheFile = NULL;
2275 return;
2278 if(req->read_size) {
2279 DWORD written;
2281 b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2282 if(!b)
2283 FIXME("WriteFile failed: %u\n", GetLastError());
2285 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2286 commit_cache_entry(req);
2290 /* read some more data into the read buffer (the read section must be held) */
2291 static DWORD read_more_data( http_request_t *req, int maxlen )
2293 DWORD res;
2294 int len;
2296 if (req->read_pos)
2298 /* move existing data to the start of the buffer */
2299 if(req->read_size)
2300 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2301 req->read_pos = 0;
2304 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2306 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2307 maxlen - req->read_size, 0, &len );
2308 if(res == ERROR_SUCCESS)
2309 req->read_size += len;
2311 return res;
2314 /* remove some amount of data from the read buffer (the read section must be held) */
2315 static void remove_data( http_request_t *req, int count )
2317 if (!(req->read_size -= count)) req->read_pos = 0;
2318 else req->read_pos += count;
2321 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2323 int count, bytes_read, pos = 0;
2324 DWORD res;
2326 EnterCriticalSection( &req->read_section );
2327 for (;;)
2329 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2331 if (eol)
2333 count = eol - (req->read_buf + req->read_pos);
2334 bytes_read = count + 1;
2336 else count = bytes_read = req->read_size;
2338 count = min( count, *len - pos );
2339 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2340 pos += count;
2341 remove_data( req, bytes_read );
2342 if (eol) break;
2344 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2346 *len = 0;
2347 TRACE( "returning empty string %u\n", res);
2348 LeaveCriticalSection( &req->read_section );
2349 INTERNET_SetLastError(res);
2350 return FALSE;
2353 LeaveCriticalSection( &req->read_section );
2355 if (pos < *len)
2357 if (pos && buffer[pos - 1] == '\r') pos--;
2358 *len = pos + 1;
2360 buffer[*len - 1] = 0;
2361 TRACE( "returning %s\n", debugstr_a(buffer));
2362 return TRUE;
2365 /* check if we have reached the end of the data to read (the read section must be held) */
2366 static BOOL end_of_read_data( http_request_t *req )
2368 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2371 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
2373 DWORD res;
2375 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
2376 assert(*read <= size);
2378 if(req->hCacheFile) {
2379 if(*read) {
2380 BOOL bres;
2381 DWORD written;
2383 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2384 if(!bres)
2385 FIXME("WriteFile failed: %u\n", GetLastError());
2388 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2389 commit_cache_entry(req);
2392 return res;
2395 /* fetch some more data into the read buffer (the read section must be held) */
2396 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2398 DWORD res, read=0;
2400 if(req->read_size == sizeof(req->read_buf))
2401 return ERROR_SUCCESS;
2403 if(req->read_pos) {
2404 if(req->read_size)
2405 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2406 req->read_pos = 0;
2409 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2410 &read, read_mode);
2411 req->read_size += read;
2413 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2414 if(read_bytes)
2415 *read_bytes = read;
2416 return res;
2419 /* return the size of data available to be read immediately (the read section must be held) */
2420 static DWORD get_avail_data( http_request_t *req )
2422 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2425 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2427 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2428 DWORD avail = 0;
2430 if(req->netconn)
2431 NETCON_query_data_available(req->netconn, &avail);
2432 return netconn_stream->content_length == ~0u
2433 ? avail
2434 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2437 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2439 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2440 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2443 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2444 DWORD *read, read_mode_t read_mode)
2446 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2447 int len = 0;
2449 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2451 if(read_mode == READMODE_NOBLOCK) {
2452 DWORD avail = netconn_get_avail_data(stream, req);
2453 if (size > avail)
2454 size = avail;
2457 if(size && req->netconn) {
2458 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2459 len = 0;
2460 if(!len)
2461 netconn_stream->content_length = netconn_stream->content_read;
2464 netconn_stream->content_read += *read = len;
2465 TRACE("read %u bytes\n", len);
2466 return ERROR_SUCCESS;
2469 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2471 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2472 BYTE buf[1024];
2473 DWORD avail;
2474 int len;
2476 if(netconn_end_of_data(stream, req))
2477 return TRUE;
2479 do {
2480 avail = netconn_get_avail_data(stream, req);
2481 if(!avail)
2482 return FALSE;
2484 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2485 return FALSE;
2487 netconn_stream->content_read += len;
2488 }while(netconn_stream->content_read < netconn_stream->content_length);
2490 return TRUE;
2493 static void netconn_destroy(data_stream_t *stream)
2497 static const data_stream_vtbl_t netconn_stream_vtbl = {
2498 netconn_get_avail_data,
2499 netconn_end_of_data,
2500 netconn_read,
2501 netconn_drain_content,
2502 netconn_destroy
2505 /* read some more data into the read buffer (the read section must be held) */
2506 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2508 DWORD res;
2509 int len;
2511 if (stream->buf_pos)
2513 /* move existing data to the start of the buffer */
2514 if(stream->buf_size)
2515 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2516 stream->buf_pos = 0;
2519 if (maxlen == -1) maxlen = sizeof(stream->buf);
2521 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2522 maxlen - stream->buf_size, 0, &len );
2523 if(res == ERROR_SUCCESS)
2524 stream->buf_size += len;
2526 return res;
2529 /* remove some amount of data from the read buffer (the read section must be held) */
2530 static void remove_chunked_data(chunked_stream_t *stream, int count)
2532 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2533 else stream->buf_pos += count;
2536 /* discard data contents until we reach end of line (the read section must be held) */
2537 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2539 DWORD res;
2543 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2544 if (eol)
2546 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2547 break;
2549 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2550 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2551 } while (stream->buf_size);
2552 return ERROR_SUCCESS;
2555 /* read the size of the next chunk (the read section must be held) */
2556 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2558 /* TODOO */
2559 DWORD chunk_size = 0, res;
2561 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2562 return res;
2564 for (;;)
2566 while (stream->buf_size)
2568 char ch = stream->buf[stream->buf_pos];
2569 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2570 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2571 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2572 else if (ch == ';' || ch == '\r' || ch == '\n')
2574 TRACE( "reading %u byte chunk\n", chunk_size );
2575 stream->chunk_size = chunk_size;
2576 req->contentLength += chunk_size;
2577 return discard_chunked_eol(stream, req);
2579 remove_chunked_data(stream, 1);
2581 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2582 if (!stream->buf_size)
2584 stream->chunk_size = 0;
2585 return ERROR_SUCCESS;
2590 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2592 /* Allow reading only from read buffer */
2593 return 0;
2596 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2598 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2599 return !chunked_stream->chunk_size;
2602 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2603 DWORD *read, read_mode_t read_mode)
2605 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2606 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2608 if(chunked_stream->chunk_size == ~0u) {
2609 res = start_next_chunk(chunked_stream, req);
2610 if(res != ERROR_SUCCESS)
2611 return res;
2614 while(size && chunked_stream->chunk_size) {
2615 if(chunked_stream->buf_size) {
2616 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2618 /* this could block */
2619 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2620 break;
2622 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2623 remove_chunked_data(chunked_stream, read_bytes);
2624 }else {
2625 read_bytes = min(size, chunked_stream->chunk_size);
2627 if(read_mode == READMODE_NOBLOCK) {
2628 DWORD avail;
2630 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2631 break;
2632 if(read_bytes > avail)
2633 read_bytes = avail;
2635 /* this could block */
2636 if(read_bytes == chunked_stream->chunk_size)
2637 break;
2640 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2641 if(res != ERROR_SUCCESS)
2642 break;
2645 chunked_stream->chunk_size -= read_bytes;
2646 size -= read_bytes;
2647 ret_read += read_bytes;
2648 if(!chunked_stream->chunk_size) {
2649 assert(read_mode != READMODE_NOBLOCK);
2650 res = start_next_chunk(chunked_stream, req);
2651 if(res != ERROR_SUCCESS)
2652 break;
2655 if(read_mode == READMODE_ASYNC)
2656 read_mode = READMODE_NOBLOCK;
2659 TRACE("read %u bytes\n", ret_read);
2660 *read = ret_read;
2661 return res;
2664 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2666 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2668 /* FIXME: we can do better */
2669 return !chunked_stream->chunk_size;
2672 static void chunked_destroy(data_stream_t *stream)
2674 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2675 heap_free(chunked_stream);
2678 static const data_stream_vtbl_t chunked_stream_vtbl = {
2679 chunked_get_avail_data,
2680 chunked_end_of_data,
2681 chunked_read,
2682 chunked_drain_content,
2683 chunked_destroy
2686 /* set the request content length based on the headers */
2687 static DWORD set_content_length(http_request_t *request)
2689 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2690 WCHAR encoding[20];
2691 DWORD size;
2693 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2694 request->contentLength = request->netconn_stream.content_length = 0;
2695 return ERROR_SUCCESS;
2698 size = sizeof(request->contentLength);
2699 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2700 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2701 request->contentLength = ~0u;
2702 request->netconn_stream.content_length = request->contentLength;
2703 request->netconn_stream.content_read = request->read_size;
2705 size = sizeof(encoding);
2706 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2707 !strcmpiW(encoding, szChunked))
2709 chunked_stream_t *chunked_stream;
2711 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2712 if(!chunked_stream)
2713 return ERROR_OUTOFMEMORY;
2715 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2716 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2717 chunked_stream->chunk_size = ~0u;
2719 if(request->read_size) {
2720 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2721 chunked_stream->buf_size = request->read_size;
2722 request->read_size = request->read_pos = 0;
2725 request->data_stream = &chunked_stream->data_stream;
2726 request->contentLength = ~0u;
2727 request->read_chunked = TRUE;
2730 if(request->decoding) {
2731 int encoding_idx;
2733 static const WCHAR gzipW[] = {'g','z','i','p',0};
2735 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2736 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2737 return init_gzip_stream(request);
2740 return ERROR_SUCCESS;
2743 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2745 INTERNET_ASYNC_RESULT iar;
2747 iar.dwResult = result;
2748 iar.dwError = error;
2750 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2751 sizeof(INTERNET_ASYNC_RESULT));
2754 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2756 DWORD res, read = 0, avail = 0;
2757 read_mode_t mode;
2759 TRACE("%p\n", req);
2761 EnterCriticalSection( &req->read_section );
2763 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2764 res = refill_read_buffer(req, mode, &read);
2765 if(res == ERROR_SUCCESS && !first_notif)
2766 avail = get_avail_data(req);
2768 LeaveCriticalSection( &req->read_section );
2770 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2771 WARN("res %u read %u, closing connection\n", res, read);
2772 http_release_netconn(req, FALSE);
2775 if(res == ERROR_SUCCESS)
2776 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2777 else
2778 send_request_complete(req, 0, res);
2781 /* read data from the http connection (the read section must be held) */
2782 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2784 DWORD current_read = 0, ret_read = 0;
2785 read_mode_t read_mode;
2786 DWORD res = ERROR_SUCCESS;
2788 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2790 EnterCriticalSection( &req->read_section );
2792 if(req->read_size) {
2793 ret_read = min(size, req->read_size);
2794 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2795 req->read_size -= ret_read;
2796 req->read_pos += ret_read;
2797 if(read_mode == READMODE_ASYNC)
2798 read_mode = READMODE_NOBLOCK;
2801 if(ret_read < size) {
2802 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2803 ret_read += current_read;
2806 LeaveCriticalSection( &req->read_section );
2808 *read = ret_read;
2809 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2811 if(size && !ret_read)
2812 http_release_netconn(req, res == ERROR_SUCCESS);
2814 return res;
2817 static BOOL drain_content(http_request_t *req, BOOL blocking)
2819 BOOL ret;
2821 if(!req->netconn || req->contentLength == -1)
2822 return FALSE;
2824 if(!strcmpW(req->verb, szHEAD))
2825 return TRUE;
2827 if(!blocking)
2828 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2830 EnterCriticalSection( &req->read_section );
2832 while(1) {
2833 DWORD bytes_read, res;
2834 BYTE buf[4096];
2836 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2837 if(res != ERROR_SUCCESS) {
2838 ret = FALSE;
2839 break;
2841 if(!bytes_read) {
2842 ret = TRUE;
2843 break;
2847 LeaveCriticalSection( &req->read_section );
2848 return ret;
2851 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2853 http_request_t *req = (http_request_t*)hdr;
2854 DWORD res;
2856 EnterCriticalSection( &req->read_section );
2857 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2858 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2860 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2861 if(res == ERROR_SUCCESS)
2862 res = hdr->dwError;
2863 LeaveCriticalSection( &req->read_section );
2865 return res;
2868 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2870 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2871 http_request_t *req = (http_request_t*)workRequest->hdr;
2872 DWORD res;
2874 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2876 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2877 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2879 send_request_complete(req, res == ERROR_SUCCESS, res);
2882 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2883 DWORD flags, DWORD_PTR context)
2885 http_request_t *req = (http_request_t*)hdr;
2886 DWORD res, size, read, error = ERROR_SUCCESS;
2888 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2889 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2891 if (buffers->dwStructSize != sizeof(*buffers))
2892 return ERROR_INVALID_PARAMETER;
2894 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2896 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2898 WORKREQUEST workRequest;
2900 if (TryEnterCriticalSection( &req->read_section ))
2902 if (get_avail_data(req))
2904 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2905 &buffers->dwBufferLength, FALSE);
2906 size = buffers->dwBufferLength;
2907 LeaveCriticalSection( &req->read_section );
2908 goto done;
2910 LeaveCriticalSection( &req->read_section );
2913 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2914 workRequest.hdr = WININET_AddRef(&req->hdr);
2915 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2917 INTERNET_AsyncCall(&workRequest);
2919 return ERROR_IO_PENDING;
2922 read = 0;
2923 size = buffers->dwBufferLength;
2925 EnterCriticalSection( &req->read_section );
2926 if(hdr->dwError == ERROR_SUCCESS)
2927 hdr->dwError = INTERNET_HANDLE_IN_USE;
2928 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2929 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2931 while(1) {
2932 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2933 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2934 if(res != ERROR_SUCCESS)
2935 break;
2937 read += buffers->dwBufferLength;
2938 if(read == size || end_of_read_data(req))
2939 break;
2941 LeaveCriticalSection( &req->read_section );
2943 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2944 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2945 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2946 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2948 EnterCriticalSection( &req->read_section );
2951 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2952 hdr->dwError = ERROR_SUCCESS;
2953 else
2954 error = hdr->dwError;
2956 LeaveCriticalSection( &req->read_section );
2957 size = buffers->dwBufferLength;
2958 buffers->dwBufferLength = read;
2960 done:
2961 if (res == ERROR_SUCCESS) {
2962 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2963 &size, sizeof(size));
2966 return res==ERROR_SUCCESS ? error : res;
2969 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2971 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2972 http_request_t *req = (http_request_t*)workRequest->hdr;
2973 DWORD res;
2975 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2977 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2978 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2980 send_request_complete(req, res == ERROR_SUCCESS, res);
2983 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2984 DWORD flags, DWORD_PTR context)
2987 http_request_t *req = (http_request_t*)hdr;
2988 DWORD res, size, read, error = ERROR_SUCCESS;
2990 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2991 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2993 if (buffers->dwStructSize != sizeof(*buffers))
2994 return ERROR_INVALID_PARAMETER;
2996 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2998 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
3000 WORKREQUEST workRequest;
3002 if (TryEnterCriticalSection( &req->read_section ))
3004 if (get_avail_data(req))
3006 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
3007 &buffers->dwBufferLength, FALSE);
3008 size = buffers->dwBufferLength;
3009 LeaveCriticalSection( &req->read_section );
3010 goto done;
3012 LeaveCriticalSection( &req->read_section );
3015 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
3016 workRequest.hdr = WININET_AddRef(&req->hdr);
3017 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
3019 INTERNET_AsyncCall(&workRequest);
3021 return ERROR_IO_PENDING;
3024 read = 0;
3025 size = buffers->dwBufferLength;
3027 EnterCriticalSection( &req->read_section );
3028 if(hdr->dwError == ERROR_SUCCESS)
3029 hdr->dwError = INTERNET_HANDLE_IN_USE;
3030 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3031 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3033 while(1) {
3034 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
3035 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
3036 if(res != ERROR_SUCCESS)
3037 break;
3039 read += buffers->dwBufferLength;
3040 if(read == size || end_of_read_data(req))
3041 break;
3043 LeaveCriticalSection( &req->read_section );
3045 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3046 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
3047 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
3048 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3050 EnterCriticalSection( &req->read_section );
3053 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3054 hdr->dwError = ERROR_SUCCESS;
3055 else
3056 error = hdr->dwError;
3058 LeaveCriticalSection( &req->read_section );
3059 size = buffers->dwBufferLength;
3060 buffers->dwBufferLength = read;
3062 done:
3063 if (res == ERROR_SUCCESS) {
3064 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3065 &size, sizeof(size));
3068 return res==ERROR_SUCCESS ? error : res;
3071 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3073 DWORD res;
3074 http_request_t *request = (http_request_t*)hdr;
3076 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3078 *written = 0;
3079 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3080 if (res == ERROR_SUCCESS)
3081 request->bytesWritten += *written;
3083 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3084 return res;
3087 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
3089 http_request_t *req = (http_request_t*)workRequest->hdr;
3091 HTTP_ReceiveRequestData(req, FALSE);
3094 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3096 http_request_t *req = (http_request_t*)hdr;
3098 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3100 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3102 WORKREQUEST workRequest;
3104 /* never wait, if we can't enter the section we queue an async request right away */
3105 if (TryEnterCriticalSection( &req->read_section ))
3107 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3108 if ((*available = get_avail_data( req ))) goto done;
3109 if (end_of_read_data( req )) goto done;
3110 LeaveCriticalSection( &req->read_section );
3113 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3114 workRequest.hdr = WININET_AddRef( &req->hdr );
3116 INTERNET_AsyncCall(&workRequest);
3118 return ERROR_IO_PENDING;
3121 EnterCriticalSection( &req->read_section );
3123 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3125 refill_read_buffer( req, READMODE_ASYNC, NULL );
3126 *available = get_avail_data( req );
3129 done:
3130 LeaveCriticalSection( &req->read_section );
3132 TRACE( "returning %u\n", *available );
3133 return ERROR_SUCCESS;
3136 static const object_vtbl_t HTTPREQVtbl = {
3137 HTTPREQ_Destroy,
3138 HTTPREQ_CloseConnection,
3139 HTTPREQ_QueryOption,
3140 HTTPREQ_SetOption,
3141 HTTPREQ_ReadFile,
3142 HTTPREQ_ReadFileExA,
3143 HTTPREQ_ReadFileExW,
3144 HTTPREQ_WriteFile,
3145 HTTPREQ_QueryDataAvailable,
3146 NULL
3149 /***********************************************************************
3150 * HTTP_HttpOpenRequestW (internal)
3152 * Open a HTTP request handle
3154 * RETURNS
3155 * HINTERNET a HTTP request handle on success
3156 * NULL on failure
3159 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3160 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3161 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3162 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3164 appinfo_t *hIC = session->appInfo;
3165 http_request_t *request;
3166 INTERNET_PORT port;
3167 DWORD len, res = ERROR_SUCCESS;
3169 TRACE("-->\n");
3171 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3172 if(!request)
3173 return ERROR_OUTOFMEMORY;
3175 request->hdr.htype = WH_HHTTPREQ;
3176 request->hdr.dwFlags = dwFlags;
3177 request->hdr.dwContext = dwContext;
3178 request->contentLength = ~0u;
3180 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3181 request->data_stream = &request->netconn_stream.data_stream;
3182 request->connect_timeout = session->connect_timeout;
3183 request->send_timeout = session->send_timeout;
3184 request->receive_timeout = session->receive_timeout;
3186 InitializeCriticalSection( &request->read_section );
3187 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3189 WININET_AddRef( &session->hdr );
3190 request->session = session;
3191 list_add_head( &session->hdr.children, &request->hdr.entry );
3193 port = session->hostPort;
3194 if(port == INTERNET_INVALID_PORT_NUMBER)
3195 port = dwFlags & INTERNET_FLAG_SECURE ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3197 request->server = get_server(session->hostName, port, TRUE);
3198 if(!request->server) {
3199 WININET_Release(&request->hdr);
3200 return ERROR_OUTOFMEMORY;
3203 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3204 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3205 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3206 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3208 if (lpszObjectName && *lpszObjectName) {
3209 HRESULT rc;
3211 len = 0;
3212 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3213 if (rc != E_POINTER)
3214 len = strlenW(lpszObjectName)+1;
3215 request->path = heap_alloc(len*sizeof(WCHAR));
3216 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3217 URL_ESCAPE_SPACES_ONLY);
3218 if (rc != S_OK)
3220 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3221 strcpyW(request->path,lpszObjectName);
3223 }else {
3224 static const WCHAR slashW[] = {'/',0};
3226 request->path = heap_strdupW(slashW);
3229 if (lpszReferrer && *lpszReferrer)
3230 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3232 if (lpszAcceptTypes)
3234 int i;
3235 for (i = 0; lpszAcceptTypes[i]; i++)
3237 if (!*lpszAcceptTypes[i]) continue;
3238 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3239 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3240 HTTP_ADDHDR_FLAG_REQ |
3241 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3245 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3246 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3248 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3249 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3250 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3252 WCHAR *host_name;
3254 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3256 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3257 if (!host_name) {
3258 res = ERROR_OUTOFMEMORY;
3259 goto lend;
3262 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3263 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3264 heap_free(host_name);
3266 else
3267 HTTP_ProcessHeader(request, hostW, session->hostName,
3268 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3270 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3271 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3272 INTERNET_DEFAULT_HTTPS_PORT :
3273 INTERNET_DEFAULT_HTTP_PORT);
3275 if (hIC->proxy && hIC->proxy[0])
3276 HTTP_DealWithProxy( hIC, session, request );
3278 INTERNET_SendCallback(&session->hdr, dwContext,
3279 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3280 sizeof(HINTERNET));
3282 lend:
3283 TRACE("<-- %u (%p)\n", res, request);
3285 if(res != ERROR_SUCCESS) {
3286 WININET_Release( &request->hdr );
3287 *ret = NULL;
3288 return res;
3291 *ret = request->hdr.hInternet;
3292 return ERROR_SUCCESS;
3295 /***********************************************************************
3296 * HttpOpenRequestW (WININET.@)
3298 * Open a HTTP request handle
3300 * RETURNS
3301 * HINTERNET a HTTP request handle on success
3302 * NULL on failure
3305 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3306 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3307 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3308 DWORD dwFlags, DWORD_PTR dwContext)
3310 http_session_t *session;
3311 HINTERNET handle = NULL;
3312 DWORD res;
3314 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3315 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3316 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3317 dwFlags, dwContext);
3318 if(lpszAcceptTypes!=NULL)
3320 int i;
3321 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3322 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3325 session = (http_session_t*) get_handle_object( hHttpSession );
3326 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3328 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3329 goto lend;
3333 * My tests seem to show that the windows version does not
3334 * become asynchronous until after this point. And anyhow
3335 * if this call was asynchronous then how would you get the
3336 * necessary HINTERNET pointer returned by this function.
3339 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3340 lpszVersion, lpszReferrer, lpszAcceptTypes,
3341 dwFlags, dwContext, &handle);
3342 lend:
3343 if( session )
3344 WININET_Release( &session->hdr );
3345 TRACE("returning %p\n", handle);
3346 if(res != ERROR_SUCCESS)
3347 SetLastError(res);
3348 return handle;
3351 static const LPCWSTR header_lookup[] = {
3352 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3353 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3354 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3355 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3356 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3357 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3358 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3359 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3360 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3361 szDate, /* HTTP_QUERY_DATE = 9 */
3362 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3363 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3364 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3365 szURI, /* HTTP_QUERY_URI = 13 */
3366 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3367 NULL, /* HTTP_QUERY_COST = 15 */
3368 NULL, /* HTTP_QUERY_LINK = 16 */
3369 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3370 NULL, /* HTTP_QUERY_VERSION = 18 */
3371 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3372 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3373 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3374 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3375 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3376 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3377 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3378 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3379 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3380 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3381 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3382 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3383 NULL, /* HTTP_QUERY_FROM = 31 */
3384 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3385 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3386 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3387 szReferer, /* HTTP_QUERY_REFERER = 35 */
3388 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3389 szServer, /* HTTP_QUERY_SERVER = 37 */
3390 NULL, /* HTTP_TITLE = 38 */
3391 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3392 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3393 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3394 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3395 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3396 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3397 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3398 NULL, /* HTTP_QUERY_REFRESH = 46 */
3399 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3400 szAge, /* HTTP_QUERY_AGE = 48 */
3401 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3402 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3403 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3404 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3405 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3406 szETag, /* HTTP_QUERY_ETAG = 54 */
3407 hostW, /* HTTP_QUERY_HOST = 55 */
3408 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3409 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3410 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3411 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3412 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3413 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3414 szRange, /* HTTP_QUERY_RANGE = 62 */
3415 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3416 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3417 szVary, /* HTTP_QUERY_VARY = 65 */
3418 szVia, /* HTTP_QUERY_VIA = 66 */
3419 szWarning, /* HTTP_QUERY_WARNING = 67 */
3420 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3421 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3422 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3425 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3427 /***********************************************************************
3428 * HTTP_HttpQueryInfoW (internal)
3430 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3431 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3433 LPHTTPHEADERW lphttpHdr = NULL;
3434 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3435 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3436 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3437 INT index = -1;
3439 /* Find requested header structure */
3440 switch (level)
3442 case HTTP_QUERY_CUSTOM:
3443 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3444 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3445 break;
3446 case HTTP_QUERY_RAW_HEADERS_CRLF:
3448 LPWSTR headers;
3449 DWORD len = 0;
3450 DWORD res = ERROR_INVALID_PARAMETER;
3452 if (request_only)
3453 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3454 else
3455 headers = request->rawHeaders;
3457 if (headers)
3458 len = strlenW(headers) * sizeof(WCHAR);
3460 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3462 len += sizeof(WCHAR);
3463 res = ERROR_INSUFFICIENT_BUFFER;
3465 else if (lpBuffer)
3467 if (headers)
3468 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3469 else
3471 len = strlenW(szCrLf) * sizeof(WCHAR);
3472 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3474 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3475 res = ERROR_SUCCESS;
3477 *lpdwBufferLength = len;
3479 if (request_only) heap_free(headers);
3480 return res;
3482 case HTTP_QUERY_RAW_HEADERS:
3484 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3485 DWORD i, size = 0;
3486 LPWSTR pszString = lpBuffer;
3488 for (i = 0; ppszRawHeaderLines[i]; i++)
3489 size += strlenW(ppszRawHeaderLines[i]) + 1;
3491 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3493 HTTP_FreeTokens(ppszRawHeaderLines);
3494 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3495 return ERROR_INSUFFICIENT_BUFFER;
3497 if (pszString)
3499 for (i = 0; ppszRawHeaderLines[i]; i++)
3501 DWORD len = strlenW(ppszRawHeaderLines[i]);
3502 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3503 pszString += len+1;
3505 *pszString = '\0';
3506 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3508 *lpdwBufferLength = size * sizeof(WCHAR);
3509 HTTP_FreeTokens(ppszRawHeaderLines);
3511 return ERROR_SUCCESS;
3513 case HTTP_QUERY_STATUS_TEXT:
3514 if (request->statusText)
3516 DWORD len = strlenW(request->statusText);
3517 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3519 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3520 return ERROR_INSUFFICIENT_BUFFER;
3522 if (lpBuffer)
3524 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3525 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3527 *lpdwBufferLength = len * sizeof(WCHAR);
3528 return ERROR_SUCCESS;
3530 break;
3531 case HTTP_QUERY_VERSION:
3532 if (request->version)
3534 DWORD len = strlenW(request->version);
3535 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3537 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3538 return ERROR_INSUFFICIENT_BUFFER;
3540 if (lpBuffer)
3542 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3543 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3545 *lpdwBufferLength = len * sizeof(WCHAR);
3546 return ERROR_SUCCESS;
3548 break;
3549 case HTTP_QUERY_CONTENT_ENCODING:
3550 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3551 requested_index,request_only);
3552 break;
3553 case HTTP_QUERY_STATUS_CODE: {
3554 DWORD res = ERROR_SUCCESS;
3556 if(request_only)
3557 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3559 if(requested_index)
3560 break;
3562 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3563 if(*lpdwBufferLength >= sizeof(DWORD))
3564 *(DWORD*)lpBuffer = request->status_code;
3565 else
3566 res = ERROR_INSUFFICIENT_BUFFER;
3567 *lpdwBufferLength = sizeof(DWORD);
3568 }else {
3569 WCHAR buf[12];
3570 DWORD size;
3571 static const WCHAR formatW[] = {'%','u',0};
3573 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3575 if(size <= *lpdwBufferLength) {
3576 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3577 }else {
3578 size += sizeof(WCHAR);
3579 res = ERROR_INSUFFICIENT_BUFFER;
3582 *lpdwBufferLength = size;
3584 return res;
3586 default:
3587 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3589 if (level < LAST_TABLE_HEADER && header_lookup[level])
3590 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3591 requested_index,request_only);
3594 if (index >= 0)
3595 lphttpHdr = &request->custHeaders[index];
3597 /* Ensure header satisfies requested attributes */
3598 if (!lphttpHdr ||
3599 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3600 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3602 return ERROR_HTTP_HEADER_NOT_FOUND;
3605 if (lpdwIndex) (*lpdwIndex)++;
3607 /* coalesce value to requested type */
3608 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3610 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3611 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3613 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3615 time_t tmpTime;
3616 struct tm tmpTM;
3617 SYSTEMTIME *STHook;
3619 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3621 tmpTM = *gmtime(&tmpTime);
3622 STHook = (SYSTEMTIME *)lpBuffer;
3623 STHook->wDay = tmpTM.tm_mday;
3624 STHook->wHour = tmpTM.tm_hour;
3625 STHook->wMilliseconds = 0;
3626 STHook->wMinute = tmpTM.tm_min;
3627 STHook->wDayOfWeek = tmpTM.tm_wday;
3628 STHook->wMonth = tmpTM.tm_mon + 1;
3629 STHook->wSecond = tmpTM.tm_sec;
3630 STHook->wYear = tmpTM.tm_year;
3632 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3633 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3634 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3636 else if (lphttpHdr->lpszValue)
3638 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3640 if (len > *lpdwBufferLength)
3642 *lpdwBufferLength = len;
3643 return ERROR_INSUFFICIENT_BUFFER;
3645 if (lpBuffer)
3647 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3648 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3650 *lpdwBufferLength = len - sizeof(WCHAR);
3652 return ERROR_SUCCESS;
3655 /***********************************************************************
3656 * HttpQueryInfoW (WININET.@)
3658 * Queries for information about an HTTP request
3660 * RETURNS
3661 * TRUE on success
3662 * FALSE on failure
3665 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3666 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3668 http_request_t *request;
3669 DWORD res;
3671 if (TRACE_ON(wininet)) {
3672 #define FE(x) { x, #x }
3673 static const wininet_flag_info query_flags[] = {
3674 FE(HTTP_QUERY_MIME_VERSION),
3675 FE(HTTP_QUERY_CONTENT_TYPE),
3676 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3677 FE(HTTP_QUERY_CONTENT_ID),
3678 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3679 FE(HTTP_QUERY_CONTENT_LENGTH),
3680 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3681 FE(HTTP_QUERY_ALLOW),
3682 FE(HTTP_QUERY_PUBLIC),
3683 FE(HTTP_QUERY_DATE),
3684 FE(HTTP_QUERY_EXPIRES),
3685 FE(HTTP_QUERY_LAST_MODIFIED),
3686 FE(HTTP_QUERY_MESSAGE_ID),
3687 FE(HTTP_QUERY_URI),
3688 FE(HTTP_QUERY_DERIVED_FROM),
3689 FE(HTTP_QUERY_COST),
3690 FE(HTTP_QUERY_LINK),
3691 FE(HTTP_QUERY_PRAGMA),
3692 FE(HTTP_QUERY_VERSION),
3693 FE(HTTP_QUERY_STATUS_CODE),
3694 FE(HTTP_QUERY_STATUS_TEXT),
3695 FE(HTTP_QUERY_RAW_HEADERS),
3696 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3697 FE(HTTP_QUERY_CONNECTION),
3698 FE(HTTP_QUERY_ACCEPT),
3699 FE(HTTP_QUERY_ACCEPT_CHARSET),
3700 FE(HTTP_QUERY_ACCEPT_ENCODING),
3701 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3702 FE(HTTP_QUERY_AUTHORIZATION),
3703 FE(HTTP_QUERY_CONTENT_ENCODING),
3704 FE(HTTP_QUERY_FORWARDED),
3705 FE(HTTP_QUERY_FROM),
3706 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3707 FE(HTTP_QUERY_LOCATION),
3708 FE(HTTP_QUERY_ORIG_URI),
3709 FE(HTTP_QUERY_REFERER),
3710 FE(HTTP_QUERY_RETRY_AFTER),
3711 FE(HTTP_QUERY_SERVER),
3712 FE(HTTP_QUERY_TITLE),
3713 FE(HTTP_QUERY_USER_AGENT),
3714 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3715 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3716 FE(HTTP_QUERY_ACCEPT_RANGES),
3717 FE(HTTP_QUERY_SET_COOKIE),
3718 FE(HTTP_QUERY_COOKIE),
3719 FE(HTTP_QUERY_REQUEST_METHOD),
3720 FE(HTTP_QUERY_REFRESH),
3721 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3722 FE(HTTP_QUERY_AGE),
3723 FE(HTTP_QUERY_CACHE_CONTROL),
3724 FE(HTTP_QUERY_CONTENT_BASE),
3725 FE(HTTP_QUERY_CONTENT_LOCATION),
3726 FE(HTTP_QUERY_CONTENT_MD5),
3727 FE(HTTP_QUERY_CONTENT_RANGE),
3728 FE(HTTP_QUERY_ETAG),
3729 FE(HTTP_QUERY_HOST),
3730 FE(HTTP_QUERY_IF_MATCH),
3731 FE(HTTP_QUERY_IF_NONE_MATCH),
3732 FE(HTTP_QUERY_IF_RANGE),
3733 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3734 FE(HTTP_QUERY_MAX_FORWARDS),
3735 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3736 FE(HTTP_QUERY_RANGE),
3737 FE(HTTP_QUERY_TRANSFER_ENCODING),
3738 FE(HTTP_QUERY_UPGRADE),
3739 FE(HTTP_QUERY_VARY),
3740 FE(HTTP_QUERY_VIA),
3741 FE(HTTP_QUERY_WARNING),
3742 FE(HTTP_QUERY_CUSTOM)
3744 static const wininet_flag_info modifier_flags[] = {
3745 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3746 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3747 FE(HTTP_QUERY_FLAG_NUMBER),
3748 FE(HTTP_QUERY_FLAG_COALESCE)
3750 #undef FE
3751 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3752 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3753 DWORD i;
3755 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3756 TRACE(" Attribute:");
3757 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3758 if (query_flags[i].val == info) {
3759 TRACE(" %s", query_flags[i].name);
3760 break;
3763 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3764 TRACE(" Unknown (%08x)", info);
3767 TRACE(" Modifier:");
3768 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3769 if (modifier_flags[i].val & info_mod) {
3770 TRACE(" %s", modifier_flags[i].name);
3771 info_mod &= ~ modifier_flags[i].val;
3775 if (info_mod) {
3776 TRACE(" Unknown (%08x)", info_mod);
3778 TRACE("\n");
3781 request = (http_request_t*) get_handle_object( hHttpRequest );
3782 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3784 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3785 goto lend;
3788 if (lpBuffer == NULL)
3789 *lpdwBufferLength = 0;
3790 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3791 lpBuffer, lpdwBufferLength, lpdwIndex);
3793 lend:
3794 if( request )
3795 WININET_Release( &request->hdr );
3797 TRACE("%u <--\n", res);
3798 if(res != ERROR_SUCCESS)
3799 SetLastError(res);
3800 return res == ERROR_SUCCESS;
3803 /***********************************************************************
3804 * HttpQueryInfoA (WININET.@)
3806 * Queries for information about an HTTP request
3808 * RETURNS
3809 * TRUE on success
3810 * FALSE on failure
3813 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3814 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3816 BOOL result;
3817 DWORD len;
3818 WCHAR* bufferW;
3820 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3821 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3823 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3824 lpdwBufferLength, lpdwIndex );
3827 if (lpBuffer)
3829 DWORD alloclen;
3830 len = (*lpdwBufferLength)*sizeof(WCHAR);
3831 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3833 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3834 if (alloclen < len)
3835 alloclen = len;
3837 else
3838 alloclen = len;
3839 bufferW = heap_alloc(alloclen);
3840 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3841 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3842 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3843 } else
3845 bufferW = NULL;
3846 len = 0;
3849 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3850 &len, lpdwIndex );
3851 if( result )
3853 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3854 lpBuffer, *lpdwBufferLength, NULL, NULL );
3855 *lpdwBufferLength = len - 1;
3857 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3859 else
3860 /* since the strings being returned from HttpQueryInfoW should be
3861 * only ASCII characters, it is reasonable to assume that all of
3862 * the Unicode characters can be reduced to a single byte */
3863 *lpdwBufferLength = len / sizeof(WCHAR);
3865 heap_free( bufferW );
3866 return result;
3869 /***********************************************************************
3870 * HTTP_GetRedirectURL (internal)
3872 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3874 static WCHAR szHttp[] = {'h','t','t','p',0};
3875 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3876 http_session_t *session = request->session;
3877 URL_COMPONENTSW urlComponents;
3878 DWORD url_length = 0;
3879 LPWSTR orig_url;
3880 LPWSTR combined_url;
3882 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3883 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3884 urlComponents.dwSchemeLength = 0;
3885 urlComponents.lpszHostName = session->hostName;
3886 urlComponents.dwHostNameLength = 0;
3887 urlComponents.nPort = session->hostPort;
3888 urlComponents.lpszUserName = session->userName;
3889 urlComponents.dwUserNameLength = 0;
3890 urlComponents.lpszPassword = NULL;
3891 urlComponents.dwPasswordLength = 0;
3892 urlComponents.lpszUrlPath = request->path;
3893 urlComponents.dwUrlPathLength = 0;
3894 urlComponents.lpszExtraInfo = NULL;
3895 urlComponents.dwExtraInfoLength = 0;
3897 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3898 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3899 return NULL;
3901 orig_url = heap_alloc(url_length);
3903 /* convert from bytes to characters */
3904 url_length = url_length / sizeof(WCHAR) - 1;
3905 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3907 heap_free(orig_url);
3908 return NULL;
3911 url_length = 0;
3912 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3913 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3915 heap_free(orig_url);
3916 return NULL;
3918 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3920 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3922 heap_free(orig_url);
3923 heap_free(combined_url);
3924 return NULL;
3926 heap_free(orig_url);
3927 return combined_url;
3931 /***********************************************************************
3932 * HTTP_HandleRedirect (internal)
3934 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3936 http_session_t *session = request->session;
3937 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3938 int index;
3940 if(lpszUrl[0]=='/')
3942 /* if it's an absolute path, keep the same session info */
3943 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3945 else
3947 URL_COMPONENTSW urlComponents;
3948 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3949 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3950 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3951 BOOL custom_port = FALSE;
3953 static WCHAR httpW[] = {'h','t','t','p',0};
3954 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3956 userName[0] = 0;
3957 hostName[0] = 0;
3958 protocol[0] = 0;
3960 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3961 urlComponents.lpszScheme = protocol;
3962 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3963 urlComponents.lpszHostName = hostName;
3964 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3965 urlComponents.lpszUserName = userName;
3966 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3967 urlComponents.lpszPassword = NULL;
3968 urlComponents.dwPasswordLength = 0;
3969 urlComponents.lpszUrlPath = path;
3970 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3971 urlComponents.lpszExtraInfo = NULL;
3972 urlComponents.dwExtraInfoLength = 0;
3973 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3974 return INTERNET_GetLastError();
3976 if(!strcmpiW(protocol, httpW)) {
3977 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3978 TRACE("redirect from secure page to non-secure page\n");
3979 /* FIXME: warn about from secure redirect to non-secure page */
3980 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3983 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3984 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3985 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3986 custom_port = TRUE;
3987 }else if(!strcmpiW(protocol, httpsW)) {
3988 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3989 TRACE("redirect from non-secure page to secure page\n");
3990 /* FIXME: notify about redirect to secure page */
3991 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3994 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3995 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3996 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3997 custom_port = TRUE;
4000 heap_free(session->hostName);
4002 if(custom_port) {
4003 int len;
4004 static const WCHAR fmt[] = {'%','s',':','%','u',0};
4005 len = lstrlenW(hostName);
4006 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
4007 session->hostName = heap_alloc(len*sizeof(WCHAR));
4008 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
4010 else
4011 session->hostName = heap_strdupW(hostName);
4013 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
4015 heap_free(session->userName);
4016 session->userName = NULL;
4017 if (userName[0])
4018 session->userName = heap_strdupW(userName);
4020 reset_data_stream(request);
4022 if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
4023 server_t *new_server;
4025 new_server = get_server(hostName, urlComponents.nPort, TRUE);
4026 server_release(request->server);
4027 request->server = new_server;
4030 heap_free(request->path);
4031 request->path=NULL;
4032 if (*path)
4034 DWORD needed = 0;
4035 HRESULT rc;
4037 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
4038 if (rc != E_POINTER)
4039 needed = strlenW(path)+1;
4040 request->path = heap_alloc(needed*sizeof(WCHAR));
4041 rc = UrlEscapeW(path, request->path, &needed,
4042 URL_ESCAPE_SPACES_ONLY);
4043 if (rc != S_OK)
4045 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
4046 strcpyW(request->path,path);
4050 /* Remove custom content-type/length headers on redirects. */
4051 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
4052 if (0 <= index)
4053 HTTP_DeleteCustomHeader(request, index);
4054 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
4055 if (0 <= index)
4056 HTTP_DeleteCustomHeader(request, index);
4058 return ERROR_SUCCESS;
4061 /***********************************************************************
4062 * HTTP_build_req (internal)
4064 * concatenate all the strings in the request together
4066 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
4068 LPCWSTR *t;
4069 LPWSTR str;
4071 for( t = list; *t ; t++ )
4072 len += strlenW( *t );
4073 len++;
4075 str = heap_alloc(len*sizeof(WCHAR));
4076 *str = 0;
4078 for( t = list; *t ; t++ )
4079 strcatW( str, *t );
4081 return str;
4084 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
4086 server_t *server = request->server;
4087 LPWSTR lpszPath;
4088 LPWSTR requestString;
4089 INT len;
4090 INT cnt;
4091 INT responseLen;
4092 char *ascii_req;
4093 DWORD res;
4094 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
4095 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
4097 TRACE("\n");
4099 lpszPath = heap_alloc((lstrlenW(server->name) + 13)*sizeof(WCHAR));
4100 sprintfW(lpszPath, szFormat, server->name, server->port);
4101 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
4102 heap_free( lpszPath );
4104 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4105 NULL, 0, NULL, NULL );
4106 len--; /* the nul terminator isn't needed */
4107 ascii_req = heap_alloc(len);
4108 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4109 heap_free( requestString );
4111 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4113 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4114 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4115 heap_free( ascii_req );
4116 if (res != ERROR_SUCCESS)
4117 return res;
4119 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4120 if (!responseLen)
4121 return ERROR_HTTP_INVALID_HEADER;
4123 return ERROR_SUCCESS;
4126 static void HTTP_InsertCookies(http_request_t *request)
4128 DWORD cookie_size, size, cnt = 0;
4129 HTTPHEADERW *host;
4130 WCHAR *cookies;
4132 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4134 host = HTTP_GetHeader(request, hostW);
4135 if(!host)
4136 return;
4138 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4139 return;
4141 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4142 if(!(cookies = heap_alloc(size)))
4143 return;
4145 cnt += sprintfW(cookies, cookieW);
4146 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4147 strcatW(cookies, szCrLf);
4149 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4151 heap_free(cookies);
4154 static WORD HTTP_ParseWkday(LPCWSTR day)
4156 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4157 { 'm','o','n',0 },
4158 { 't','u','e',0 },
4159 { 'w','e','d',0 },
4160 { 't','h','u',0 },
4161 { 'f','r','i',0 },
4162 { 's','a','t',0 }};
4163 int i;
4164 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4165 if (!strcmpiW(day, days[i]))
4166 return i;
4168 /* Invalid */
4169 return 7;
4172 static WORD HTTP_ParseMonth(LPCWSTR month)
4174 static const WCHAR jan[] = { 'j','a','n',0 };
4175 static const WCHAR feb[] = { 'f','e','b',0 };
4176 static const WCHAR mar[] = { 'm','a','r',0 };
4177 static const WCHAR apr[] = { 'a','p','r',0 };
4178 static const WCHAR may[] = { 'm','a','y',0 };
4179 static const WCHAR jun[] = { 'j','u','n',0 };
4180 static const WCHAR jul[] = { 'j','u','l',0 };
4181 static const WCHAR aug[] = { 'a','u','g',0 };
4182 static const WCHAR sep[] = { 's','e','p',0 };
4183 static const WCHAR oct[] = { 'o','c','t',0 };
4184 static const WCHAR nov[] = { 'n','o','v',0 };
4185 static const WCHAR dec[] = { 'd','e','c',0 };
4187 if (!strcmpiW(month, jan)) return 1;
4188 if (!strcmpiW(month, feb)) return 2;
4189 if (!strcmpiW(month, mar)) return 3;
4190 if (!strcmpiW(month, apr)) return 4;
4191 if (!strcmpiW(month, may)) return 5;
4192 if (!strcmpiW(month, jun)) return 6;
4193 if (!strcmpiW(month, jul)) return 7;
4194 if (!strcmpiW(month, aug)) return 8;
4195 if (!strcmpiW(month, sep)) return 9;
4196 if (!strcmpiW(month, oct)) return 10;
4197 if (!strcmpiW(month, nov)) return 11;
4198 if (!strcmpiW(month, dec)) return 12;
4199 /* Invalid */
4200 return 0;
4203 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4204 * optionally preceded by whitespace.
4205 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4206 * st, and sets *str to the first character after the time format.
4208 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4210 LPCWSTR ptr = *str;
4211 WCHAR *nextPtr;
4212 unsigned long num;
4214 while (isspaceW(*ptr))
4215 ptr++;
4217 num = strtoulW(ptr, &nextPtr, 10);
4218 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4220 ERR("unexpected time format %s\n", debugstr_w(ptr));
4221 return FALSE;
4223 if (num > 23)
4225 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4226 return FALSE;
4228 ptr = nextPtr + 1;
4229 st->wHour = (WORD)num;
4230 num = strtoulW(ptr, &nextPtr, 10);
4231 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4233 ERR("unexpected time format %s\n", debugstr_w(ptr));
4234 return FALSE;
4236 if (num > 59)
4238 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4239 return FALSE;
4241 ptr = nextPtr + 1;
4242 st->wMinute = (WORD)num;
4243 num = strtoulW(ptr, &nextPtr, 10);
4244 if (!nextPtr || nextPtr <= ptr)
4246 ERR("unexpected time format %s\n", debugstr_w(ptr));
4247 return FALSE;
4249 if (num > 59)
4251 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4252 return FALSE;
4254 ptr = nextPtr + 1;
4255 *str = ptr;
4256 st->wSecond = (WORD)num;
4257 return TRUE;
4260 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4262 static const WCHAR gmt[]= { 'G','M','T',0 };
4263 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4264 LPCWSTR ptr;
4265 SYSTEMTIME st = { 0 };
4266 unsigned long num;
4268 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4269 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4270 *dayPtr = *ptr;
4271 *dayPtr = 0;
4272 st.wDayOfWeek = HTTP_ParseWkday(day);
4273 if (st.wDayOfWeek >= 7)
4275 ERR("unexpected weekday %s\n", debugstr_w(day));
4276 return FALSE;
4279 while (isspaceW(*ptr))
4280 ptr++;
4282 for (monthPtr = month; !isspace(*ptr) &&
4283 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4284 monthPtr++, ptr++)
4285 *monthPtr = *ptr;
4286 *monthPtr = 0;
4287 st.wMonth = HTTP_ParseMonth(month);
4288 if (!st.wMonth || st.wMonth > 12)
4290 ERR("unexpected month %s\n", debugstr_w(month));
4291 return FALSE;
4294 while (isspaceW(*ptr))
4295 ptr++;
4297 num = strtoulW(ptr, &nextPtr, 10);
4298 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4300 ERR("unexpected day %s\n", debugstr_w(ptr));
4301 return FALSE;
4303 ptr = nextPtr;
4304 st.wDay = (WORD)num;
4306 while (isspaceW(*ptr))
4307 ptr++;
4309 if (!HTTP_ParseTime(&st, &ptr))
4310 return FALSE;
4312 while (isspaceW(*ptr))
4313 ptr++;
4315 num = strtoulW(ptr, &nextPtr, 10);
4316 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4318 ERR("unexpected year %s\n", debugstr_w(ptr));
4319 return FALSE;
4321 ptr = nextPtr;
4322 st.wYear = (WORD)num;
4324 while (isspaceW(*ptr))
4325 ptr++;
4327 /* asctime() doesn't report a timezone, but some web servers do, so accept
4328 * with or without GMT.
4330 if (*ptr && strcmpW(ptr, gmt))
4332 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4333 return FALSE;
4335 return SystemTimeToFileTime(&st, ft);
4338 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4340 static const WCHAR gmt[]= { 'G','M','T',0 };
4341 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4342 LPCWSTR ptr;
4343 unsigned long num;
4344 SYSTEMTIME st = { 0 };
4346 ptr = strchrW(value, ',');
4347 if (!ptr)
4348 return FALSE;
4349 if (ptr - value != 3)
4351 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4352 return FALSE;
4354 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4355 day[3] = 0;
4356 st.wDayOfWeek = HTTP_ParseWkday(day);
4357 if (st.wDayOfWeek > 6)
4359 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4360 return FALSE;
4362 ptr++;
4364 while (isspaceW(*ptr))
4365 ptr++;
4367 num = strtoulW(ptr, &nextPtr, 10);
4368 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4370 WARN("unexpected day %s\n", debugstr_w(value));
4371 return FALSE;
4373 ptr = nextPtr;
4374 st.wDay = (WORD)num;
4376 while (isspaceW(*ptr))
4377 ptr++;
4379 for (monthPtr = month; !isspace(*ptr) &&
4380 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4381 monthPtr++, ptr++)
4382 *monthPtr = *ptr;
4383 *monthPtr = 0;
4384 st.wMonth = HTTP_ParseMonth(month);
4385 if (!st.wMonth || st.wMonth > 12)
4387 WARN("unexpected month %s\n", debugstr_w(month));
4388 return FALSE;
4391 while (isspaceW(*ptr))
4392 ptr++;
4394 num = strtoulW(ptr, &nextPtr, 10);
4395 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4397 ERR("unexpected year %s\n", debugstr_w(value));
4398 return FALSE;
4400 ptr = nextPtr;
4401 st.wYear = (WORD)num;
4403 if (!HTTP_ParseTime(&st, &ptr))
4404 return FALSE;
4406 while (isspaceW(*ptr))
4407 ptr++;
4409 if (strcmpW(ptr, gmt))
4411 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4412 return FALSE;
4414 return SystemTimeToFileTime(&st, ft);
4417 static WORD HTTP_ParseWeekday(LPCWSTR day)
4419 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4420 { 'm','o','n','d','a','y',0 },
4421 { 't','u','e','s','d','a','y',0 },
4422 { 'w','e','d','n','e','s','d','a','y',0 },
4423 { 't','h','u','r','s','d','a','y',0 },
4424 { 'f','r','i','d','a','y',0 },
4425 { 's','a','t','u','r','d','a','y',0 }};
4426 int i;
4427 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4428 if (!strcmpiW(day, days[i]))
4429 return i;
4431 /* Invalid */
4432 return 7;
4435 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4437 static const WCHAR gmt[]= { 'G','M','T',0 };
4438 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4439 LPCWSTR ptr;
4440 unsigned long num;
4441 SYSTEMTIME st = { 0 };
4443 ptr = strchrW(value, ',');
4444 if (!ptr)
4445 return FALSE;
4446 if (ptr - value == 3)
4448 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4449 day[3] = 0;
4450 st.wDayOfWeek = HTTP_ParseWkday(day);
4451 if (st.wDayOfWeek > 6)
4453 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4454 return FALSE;
4457 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4459 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4460 day[ptr - value + 1] = 0;
4461 st.wDayOfWeek = HTTP_ParseWeekday(day);
4462 if (st.wDayOfWeek > 6)
4464 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4465 return FALSE;
4468 else
4470 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4471 return FALSE;
4473 ptr++;
4475 while (isspaceW(*ptr))
4476 ptr++;
4478 num = strtoulW(ptr, &nextPtr, 10);
4479 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4481 ERR("unexpected day %s\n", debugstr_w(value));
4482 return FALSE;
4484 ptr = nextPtr;
4485 st.wDay = (WORD)num;
4487 if (*ptr != '-')
4489 ERR("unexpected month format %s\n", debugstr_w(ptr));
4490 return FALSE;
4492 ptr++;
4494 for (monthPtr = month; *ptr != '-' &&
4495 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4496 monthPtr++, ptr++)
4497 *monthPtr = *ptr;
4498 *monthPtr = 0;
4499 st.wMonth = HTTP_ParseMonth(month);
4500 if (!st.wMonth || st.wMonth > 12)
4502 ERR("unexpected month %s\n", debugstr_w(month));
4503 return FALSE;
4506 if (*ptr != '-')
4508 ERR("unexpected year format %s\n", debugstr_w(ptr));
4509 return FALSE;
4511 ptr++;
4513 num = strtoulW(ptr, &nextPtr, 10);
4514 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4516 ERR("unexpected year %s\n", debugstr_w(value));
4517 return FALSE;
4519 ptr = nextPtr;
4520 st.wYear = (WORD)num;
4522 if (!HTTP_ParseTime(&st, &ptr))
4523 return FALSE;
4525 while (isspaceW(*ptr))
4526 ptr++;
4528 if (strcmpW(ptr, gmt))
4530 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4531 return FALSE;
4533 return SystemTimeToFileTime(&st, ft);
4536 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4538 static const WCHAR zero[] = { '0',0 };
4539 BOOL ret;
4541 if (!strcmpW(value, zero))
4543 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4544 ret = TRUE;
4546 else if (strchrW(value, ','))
4548 ret = HTTP_ParseRfc1123Date(value, ft);
4549 if (!ret)
4551 ret = HTTP_ParseRfc850Date(value, ft);
4552 if (!ret)
4553 ERR("unexpected date format %s\n", debugstr_w(value));
4556 else
4558 ret = HTTP_ParseDateAsAsctime(value, ft);
4559 if (!ret)
4560 ERR("unexpected date format %s\n", debugstr_w(value));
4562 return ret;
4565 static void HTTP_ProcessExpires(http_request_t *request)
4567 BOOL expirationFound = FALSE;
4568 int headerIndex;
4570 /* Look for a Cache-Control header with a max-age directive, as it takes
4571 * precedence over the Expires header.
4573 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4574 if (headerIndex != -1)
4576 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4577 LPWSTR ptr;
4579 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4581 LPWSTR comma = strchrW(ptr, ','), end, equal;
4583 if (comma)
4584 end = comma;
4585 else
4586 end = ptr + strlenW(ptr);
4587 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4589 if (*equal == '=')
4591 static const WCHAR max_age[] = {
4592 'm','a','x','-','a','g','e',0 };
4594 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4596 LPWSTR nextPtr;
4597 unsigned long age;
4599 age = strtoulW(equal + 1, &nextPtr, 10);
4600 if (nextPtr > equal + 1)
4602 LARGE_INTEGER ft;
4604 NtQuerySystemTime( &ft );
4605 /* Age is in seconds, FILETIME resolution is in
4606 * 100 nanosecond intervals.
4608 ft.QuadPart += age * (ULONGLONG)1000000;
4609 request->expires.dwLowDateTime = ft.u.LowPart;
4610 request->expires.dwHighDateTime = ft.u.HighPart;
4611 expirationFound = TRUE;
4615 if (comma)
4617 ptr = comma + 1;
4618 while (isspaceW(*ptr))
4619 ptr++;
4621 else
4622 ptr = NULL;
4625 if (!expirationFound)
4627 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4628 if (headerIndex != -1)
4630 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4631 FILETIME ft;
4633 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4635 expirationFound = TRUE;
4636 request->expires = ft;
4640 if (!expirationFound)
4642 LARGE_INTEGER t;
4644 /* With no known age, default to 10 minutes until expiration. */
4645 NtQuerySystemTime( &t );
4646 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4647 request->expires.dwLowDateTime = t.u.LowPart;
4648 request->expires.dwHighDateTime = t.u.HighPart;
4652 static void HTTP_ProcessLastModified(http_request_t *request)
4654 int headerIndex;
4656 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4657 if (headerIndex != -1)
4659 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4660 FILETIME ft;
4662 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4663 request->last_modified = ft;
4667 static void http_process_keep_alive(http_request_t *req)
4669 int index;
4671 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4672 if(index != -1)
4673 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4674 else
4675 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4678 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4680 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4681 netconn_t *netconn = NULL;
4682 DWORD res;
4684 assert(!request->netconn);
4685 reset_data_stream(request);
4687 res = HTTP_ResolveName(request);
4688 if(res != ERROR_SUCCESS)
4689 return res;
4691 EnterCriticalSection(&connection_pool_cs);
4693 while(!list_empty(&request->server->conn_pool)) {
4694 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4695 list_remove(&netconn->pool_entry);
4697 if(NETCON_is_alive(netconn))
4698 break;
4700 TRACE("connection %p closed during idle\n", netconn);
4701 free_netconn(netconn);
4702 netconn = NULL;
4705 LeaveCriticalSection(&connection_pool_cs);
4707 if(netconn) {
4708 TRACE("<-- reusing %p netconn\n", netconn);
4709 request->netconn = netconn;
4710 *reusing = TRUE;
4711 return ERROR_SUCCESS;
4714 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4715 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4717 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4718 INTERNET_STATUS_CONNECTING_TO_SERVER,
4719 request->server->addr_str,
4720 strlen(request->server->addr_str)+1);
4722 res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4723 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4724 request->connect_timeout, &netconn);
4725 if(res != ERROR_SUCCESS) {
4726 ERR("create_netconn failed: %u\n", res);
4727 return res;
4730 request->netconn = netconn;
4732 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4733 INTERNET_STATUS_CONNECTED_TO_SERVER,
4734 request->server->addr_str, strlen(request->server->addr_str)+1);
4736 if(is_https) {
4737 /* Note: we differ from Microsoft's WinINet here. they seem to have
4738 * a bug that causes no status callbacks to be sent when starting
4739 * a tunnel to a proxy server using the CONNECT verb. i believe our
4740 * behaviour to be more correct and to not cause any incompatibilities
4741 * because using a secure connection through a proxy server is a rare
4742 * case that would be hard for anyone to depend on */
4743 if(request->proxy)
4744 res = HTTP_SecureProxyConnect(request);
4745 if(res == ERROR_SUCCESS)
4746 res = NETCON_secure_connect(request->netconn);
4749 if(res != ERROR_SUCCESS) {
4750 http_release_netconn(request, FALSE);
4751 return res;
4754 *reusing = FALSE;
4755 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4756 return ERROR_SUCCESS;
4759 /***********************************************************************
4760 * HTTP_HttpSendRequestW (internal)
4762 * Sends the specified request to the HTTP server
4764 * RETURNS
4765 * ERROR_SUCCESS on success
4766 * win32 error code on failure
4769 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4770 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4771 DWORD dwContentLength, BOOL bEndRequest)
4773 INT cnt;
4774 BOOL redirected = FALSE;
4775 LPWSTR requestString = NULL;
4776 INT responseLen;
4777 BOOL loop_next;
4778 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4779 static const WCHAR szContentLength[] =
4780 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4781 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4782 DWORD res;
4784 TRACE("--> %p\n", request);
4786 assert(request->hdr.htype == WH_HHTTPREQ);
4788 /* if the verb is NULL default to GET */
4789 if (!request->verb)
4790 request->verb = heap_strdupW(szGET);
4792 if (dwContentLength || strcmpW(request->verb, szGET))
4794 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4795 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4796 request->bytesToWrite = dwContentLength;
4798 if (request->session->appInfo->agent)
4800 WCHAR *agent_header;
4801 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4802 int len;
4804 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4805 agent_header = heap_alloc(len * sizeof(WCHAR));
4806 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4808 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4809 heap_free(agent_header);
4811 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4813 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4814 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4816 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4818 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4819 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4820 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4823 /* add the headers the caller supplied */
4824 if( lpszHeaders && dwHeaderLength )
4825 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4829 DWORD len;
4830 BOOL reusing_connection;
4831 char *ascii_req;
4833 loop_next = FALSE;
4834 reusing_connection = request->netconn != NULL;
4836 if(redirected) {
4837 request->contentLength = ~0u;
4838 request->bytesToWrite = 0;
4841 if (TRACE_ON(wininet))
4843 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4844 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4847 HTTP_FixURL(request);
4848 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4850 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4852 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4853 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4855 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4856 HTTP_InsertCookies(request);
4858 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4860 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4861 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4862 heap_free(url);
4864 else
4865 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4868 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4870 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4871 break;
4873 /* send the request as ASCII, tack on the optional data */
4874 if (!lpOptional || redirected)
4875 dwOptionalLength = 0;
4876 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4877 NULL, 0, NULL, NULL );
4878 ascii_req = heap_alloc(len + dwOptionalLength);
4879 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4880 ascii_req, len, NULL, NULL );
4881 if( lpOptional )
4882 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4883 len = (len + dwOptionalLength - 1);
4884 ascii_req[len] = 0;
4885 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4887 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4888 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4890 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4891 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4892 heap_free( ascii_req );
4893 if(res != ERROR_SUCCESS) {
4894 TRACE("send failed: %u\n", res);
4895 if(!reusing_connection)
4896 break;
4897 http_release_netconn(request, FALSE);
4898 loop_next = TRUE;
4899 continue;
4902 request->bytesWritten = dwOptionalLength;
4904 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4905 INTERNET_STATUS_REQUEST_SENT,
4906 &len, sizeof(DWORD));
4908 if (bEndRequest)
4910 DWORD dwBufferSize;
4912 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4913 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4915 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4916 /* FIXME: We should know that connection is closed before sending
4917 * headers. Otherwise wrong callbacks are executed */
4918 if(!responseLen && reusing_connection) {
4919 TRACE("Connection closed by server, reconnecting\n");
4920 http_release_netconn(request, FALSE);
4921 loop_next = TRUE;
4922 continue;
4925 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4926 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4927 sizeof(DWORD));
4929 http_process_keep_alive(request);
4930 HTTP_ProcessCookies(request);
4931 HTTP_ProcessExpires(request);
4932 HTTP_ProcessLastModified(request);
4934 res = set_content_length(request);
4935 if(res != ERROR_SUCCESS)
4936 goto lend;
4937 if(!request->contentLength)
4938 http_release_netconn(request, TRUE);
4940 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4942 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4943 dwBufferSize=sizeof(szNewLocation);
4944 switch(request->status_code) {
4945 case HTTP_STATUS_REDIRECT:
4946 case HTTP_STATUS_MOVED:
4947 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4948 case HTTP_STATUS_REDIRECT_METHOD:
4949 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4950 break;
4952 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4953 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4955 heap_free(request->verb);
4956 request->verb = heap_strdupW(szGET);
4958 http_release_netconn(request, drain_content(request, FALSE));
4959 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4961 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4962 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4963 res = HTTP_HandleRedirect(request, new_url);
4964 if (res == ERROR_SUCCESS)
4966 heap_free(requestString);
4967 loop_next = TRUE;
4969 heap_free( new_url );
4971 redirected = TRUE;
4974 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4976 WCHAR szAuthValue[2048];
4977 dwBufferSize=2048;
4978 if (request->status_code == HTTP_STATUS_DENIED)
4980 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4981 DWORD dwIndex = 0;
4982 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4984 if (HTTP_DoAuthorization(request, szAuthValue,
4985 &request->authInfo,
4986 request->session->userName,
4987 request->session->password,
4988 Host->lpszValue))
4990 heap_free(requestString);
4991 if(!drain_content(request, TRUE)) {
4992 FIXME("Could not drain content\n");
4993 http_release_netconn(request, FALSE);
4995 loop_next = TRUE;
4996 break;
5000 if(!loop_next) {
5001 TRACE("Cleaning wrong authorization data\n");
5002 destroy_authinfo(request->authInfo);
5003 request->authInfo = NULL;
5006 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
5008 DWORD dwIndex = 0;
5009 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
5011 if (HTTP_DoAuthorization(request, szAuthValue,
5012 &request->proxyAuthInfo,
5013 request->session->appInfo->proxyUsername,
5014 request->session->appInfo->proxyPassword,
5015 NULL))
5017 if(!drain_content(request, TRUE)) {
5018 FIXME("Could not drain content\n");
5019 http_release_netconn(request, FALSE);
5021 loop_next = TRUE;
5022 break;
5026 if(!loop_next) {
5027 TRACE("Cleaning wrong proxy authorization data\n");
5028 destroy_authinfo(request->proxyAuthInfo);
5029 request->proxyAuthInfo = NULL;
5034 else
5035 res = ERROR_SUCCESS;
5037 while (loop_next);
5039 lend:
5040 heap_free(requestString);
5042 /* TODO: send notification for P3P header */
5044 if(res == ERROR_SUCCESS)
5045 create_cache_entry(request);
5047 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5049 if (res == ERROR_SUCCESS) {
5050 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
5051 HTTP_ReceiveRequestData(request, TRUE);
5052 else
5053 send_request_complete(request,
5054 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
5055 }else {
5056 send_request_complete(request, 0, res);
5060 TRACE("<--\n");
5061 return res;
5064 /***********************************************************************
5066 * Helper functions for the HttpSendRequest(Ex) functions
5069 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
5071 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
5072 http_request_t *request = (http_request_t*) workRequest->hdr;
5074 TRACE("%p\n", request);
5076 HTTP_HttpSendRequestW(request, req->lpszHeader,
5077 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5078 req->dwContentLength, req->bEndRequest);
5080 heap_free(req->lpszHeader);
5084 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5086 DWORD dwBufferSize;
5087 INT responseLen;
5088 DWORD res = ERROR_SUCCESS;
5090 if(!request->netconn) {
5091 WARN("Not connected\n");
5092 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5093 return ERROR_INTERNET_OPERATION_CANCELLED;
5096 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5097 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5099 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5100 if (!responseLen)
5101 res = ERROR_HTTP_HEADER_NOT_FOUND;
5103 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5104 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5106 /* process cookies here. Is this right? */
5107 http_process_keep_alive(request);
5108 HTTP_ProcessCookies(request);
5109 HTTP_ProcessExpires(request);
5110 HTTP_ProcessLastModified(request);
5112 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5113 if(!request->contentLength)
5114 http_release_netconn(request, TRUE);
5117 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5119 switch(request->status_code) {
5120 case HTTP_STATUS_REDIRECT:
5121 case HTTP_STATUS_MOVED:
5122 case HTTP_STATUS_REDIRECT_METHOD:
5123 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5124 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5125 dwBufferSize=sizeof(szNewLocation);
5126 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5127 break;
5129 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5130 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5132 heap_free(request->verb);
5133 request->verb = heap_strdupW(szGET);
5135 http_release_netconn(request, drain_content(request, FALSE));
5136 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5138 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5139 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5140 res = HTTP_HandleRedirect(request, new_url);
5141 if (res == ERROR_SUCCESS)
5142 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5143 heap_free( new_url );
5149 if(res == ERROR_SUCCESS)
5150 create_cache_entry(request);
5152 if (res == ERROR_SUCCESS && request->contentLength)
5153 HTTP_ReceiveRequestData(request, TRUE);
5154 else
5155 send_request_complete(request, res == ERROR_SUCCESS, res);
5157 return res;
5160 /***********************************************************************
5161 * HttpEndRequestA (WININET.@)
5163 * Ends an HTTP request that was started by HttpSendRequestEx
5165 * RETURNS
5166 * TRUE if successful
5167 * FALSE on failure
5170 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5171 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5173 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5175 if (lpBuffersOut)
5177 SetLastError(ERROR_INVALID_PARAMETER);
5178 return FALSE;
5181 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5184 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5186 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5187 http_request_t *request = (http_request_t*)work->hdr;
5189 TRACE("%p\n", request);
5191 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5194 /***********************************************************************
5195 * HttpEndRequestW (WININET.@)
5197 * Ends an HTTP request that was started by HttpSendRequestEx
5199 * RETURNS
5200 * TRUE if successful
5201 * FALSE on failure
5204 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5205 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5207 http_request_t *request;
5208 DWORD res;
5210 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5212 if (lpBuffersOut)
5214 SetLastError(ERROR_INVALID_PARAMETER);
5215 return FALSE;
5218 request = (http_request_t*) get_handle_object( hRequest );
5220 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5222 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5223 if (request)
5224 WININET_Release( &request->hdr );
5225 return FALSE;
5227 request->hdr.dwFlags |= dwFlags;
5229 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5231 WORKREQUEST work;
5232 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5234 work.asyncproc = AsyncHttpEndRequestProc;
5235 work.hdr = WININET_AddRef( &request->hdr );
5237 work_endrequest = &work.u.HttpEndRequestW;
5238 work_endrequest->dwFlags = dwFlags;
5239 work_endrequest->dwContext = dwContext;
5241 INTERNET_AsyncCall(&work);
5242 res = ERROR_IO_PENDING;
5244 else
5245 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5247 WININET_Release( &request->hdr );
5248 TRACE("%u <--\n", res);
5249 if(res != ERROR_SUCCESS)
5250 SetLastError(res);
5251 return res == ERROR_SUCCESS;
5254 /***********************************************************************
5255 * HttpSendRequestExA (WININET.@)
5257 * Sends the specified request to the HTTP server and allows chunked
5258 * transfers.
5260 * RETURNS
5261 * Success: TRUE
5262 * Failure: FALSE, call GetLastError() for more information.
5264 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5265 LPINTERNET_BUFFERSA lpBuffersIn,
5266 LPINTERNET_BUFFERSA lpBuffersOut,
5267 DWORD dwFlags, DWORD_PTR dwContext)
5269 INTERNET_BUFFERSW BuffersInW;
5270 BOOL rc = FALSE;
5271 DWORD headerlen;
5272 LPWSTR header = NULL;
5274 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5275 lpBuffersOut, dwFlags, dwContext);
5277 if (lpBuffersIn)
5279 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5280 if (lpBuffersIn->lpcszHeader)
5282 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5283 lpBuffersIn->dwHeadersLength,0,0);
5284 header = heap_alloc(headerlen*sizeof(WCHAR));
5285 if (!(BuffersInW.lpcszHeader = header))
5287 SetLastError(ERROR_OUTOFMEMORY);
5288 return FALSE;
5290 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5291 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5292 header, headerlen);
5294 else
5295 BuffersInW.lpcszHeader = NULL;
5296 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5297 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5298 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5299 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5300 BuffersInW.Next = NULL;
5303 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5305 heap_free(header);
5306 return rc;
5309 /***********************************************************************
5310 * HttpSendRequestExW (WININET.@)
5312 * Sends the specified request to the HTTP server and allows chunked
5313 * transfers
5315 * RETURNS
5316 * Success: TRUE
5317 * Failure: FALSE, call GetLastError() for more information.
5319 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5320 LPINTERNET_BUFFERSW lpBuffersIn,
5321 LPINTERNET_BUFFERSW lpBuffersOut,
5322 DWORD dwFlags, DWORD_PTR dwContext)
5324 http_request_t *request;
5325 http_session_t *session;
5326 appinfo_t *hIC;
5327 DWORD res;
5329 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5330 lpBuffersOut, dwFlags, dwContext);
5332 request = (http_request_t*) get_handle_object( hRequest );
5334 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5336 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5337 goto lend;
5340 session = request->session;
5341 assert(session->hdr.htype == WH_HHTTPSESSION);
5342 hIC = session->appInfo;
5343 assert(hIC->hdr.htype == WH_HINIT);
5345 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5347 WORKREQUEST workRequest;
5348 struct WORKREQ_HTTPSENDREQUESTW *req;
5350 workRequest.asyncproc = AsyncHttpSendRequestProc;
5351 workRequest.hdr = WININET_AddRef( &request->hdr );
5352 req = &workRequest.u.HttpSendRequestW;
5353 if (lpBuffersIn)
5355 DWORD size = 0;
5357 if (lpBuffersIn->lpcszHeader)
5359 if (lpBuffersIn->dwHeadersLength == ~0u)
5360 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5361 else
5362 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5364 req->lpszHeader = heap_alloc(size);
5365 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5367 else req->lpszHeader = NULL;
5369 req->dwHeaderLength = size / sizeof(WCHAR);
5370 req->lpOptional = lpBuffersIn->lpvBuffer;
5371 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5372 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5374 else
5376 req->lpszHeader = NULL;
5377 req->dwHeaderLength = 0;
5378 req->lpOptional = NULL;
5379 req->dwOptionalLength = 0;
5380 req->dwContentLength = 0;
5383 req->bEndRequest = FALSE;
5385 INTERNET_AsyncCall(&workRequest);
5387 * This is from windows.
5389 res = ERROR_IO_PENDING;
5391 else
5393 if (lpBuffersIn)
5394 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5395 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5396 lpBuffersIn->dwBufferTotal, FALSE);
5397 else
5398 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5401 lend:
5402 if ( request )
5403 WININET_Release( &request->hdr );
5405 TRACE("<---\n");
5406 SetLastError(res);
5407 return res == ERROR_SUCCESS;
5410 /***********************************************************************
5411 * HttpSendRequestW (WININET.@)
5413 * Sends the specified request to the HTTP server
5415 * RETURNS
5416 * TRUE on success
5417 * FALSE on failure
5420 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5421 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5423 http_request_t *request;
5424 http_session_t *session = NULL;
5425 appinfo_t *hIC = NULL;
5426 DWORD res = ERROR_SUCCESS;
5428 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5429 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5431 request = (http_request_t*) get_handle_object( hHttpRequest );
5432 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5434 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5435 goto lend;
5438 session = request->session;
5439 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5441 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5442 goto lend;
5445 hIC = session->appInfo;
5446 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5448 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5449 goto lend;
5452 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5454 WORKREQUEST workRequest;
5455 struct WORKREQ_HTTPSENDREQUESTW *req;
5457 workRequest.asyncproc = AsyncHttpSendRequestProc;
5458 workRequest.hdr = WININET_AddRef( &request->hdr );
5459 req = &workRequest.u.HttpSendRequestW;
5460 if (lpszHeaders)
5462 DWORD size;
5464 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5465 else size = dwHeaderLength * sizeof(WCHAR);
5467 req->lpszHeader = heap_alloc(size);
5468 memcpy(req->lpszHeader, lpszHeaders, size);
5470 else
5471 req->lpszHeader = 0;
5472 req->dwHeaderLength = dwHeaderLength;
5473 req->lpOptional = lpOptional;
5474 req->dwOptionalLength = dwOptionalLength;
5475 req->dwContentLength = dwOptionalLength;
5476 req->bEndRequest = TRUE;
5478 INTERNET_AsyncCall(&workRequest);
5480 * This is from windows.
5482 res = ERROR_IO_PENDING;
5484 else
5486 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5487 dwHeaderLength, lpOptional, dwOptionalLength,
5488 dwOptionalLength, TRUE);
5490 lend:
5491 if( request )
5492 WININET_Release( &request->hdr );
5494 SetLastError(res);
5495 return res == ERROR_SUCCESS;
5498 /***********************************************************************
5499 * HttpSendRequestA (WININET.@)
5501 * Sends the specified request to the HTTP server
5503 * RETURNS
5504 * TRUE on success
5505 * FALSE on failure
5508 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5509 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5511 BOOL result;
5512 LPWSTR szHeaders=NULL;
5513 DWORD nLen=dwHeaderLength;
5514 if(lpszHeaders!=NULL)
5516 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5517 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5518 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5520 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5521 heap_free(szHeaders);
5522 return result;
5525 /***********************************************************************
5526 * HTTPSESSION_Destroy (internal)
5528 * Deallocate session handle
5531 static void HTTPSESSION_Destroy(object_header_t *hdr)
5533 http_session_t *session = (http_session_t*) hdr;
5535 TRACE("%p\n", session);
5537 WININET_Release(&session->appInfo->hdr);
5539 heap_free(session->hostName);
5540 heap_free(session->password);
5541 heap_free(session->userName);
5544 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5546 http_session_t *ses = (http_session_t *)hdr;
5548 switch(option) {
5549 case INTERNET_OPTION_HANDLE_TYPE:
5550 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5552 if (*size < sizeof(ULONG))
5553 return ERROR_INSUFFICIENT_BUFFER;
5555 *size = sizeof(DWORD);
5556 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5557 return ERROR_SUCCESS;
5558 case INTERNET_OPTION_CONNECT_TIMEOUT:
5559 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5561 if (*size < sizeof(DWORD))
5562 return ERROR_INSUFFICIENT_BUFFER;
5564 *size = sizeof(DWORD);
5565 *(DWORD *)buffer = ses->connect_timeout;
5566 return ERROR_SUCCESS;
5568 case INTERNET_OPTION_SEND_TIMEOUT:
5569 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5571 if (*size < sizeof(DWORD))
5572 return ERROR_INSUFFICIENT_BUFFER;
5574 *size = sizeof(DWORD);
5575 *(DWORD *)buffer = ses->send_timeout;
5576 return ERROR_SUCCESS;
5578 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5579 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5581 if (*size < sizeof(DWORD))
5582 return ERROR_INSUFFICIENT_BUFFER;
5584 *size = sizeof(DWORD);
5585 *(DWORD *)buffer = ses->receive_timeout;
5586 return ERROR_SUCCESS;
5589 return INET_QueryOption(hdr, option, buffer, size, unicode);
5592 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5594 http_session_t *ses = (http_session_t*)hdr;
5596 switch(option) {
5597 case INTERNET_OPTION_USERNAME:
5599 heap_free(ses->userName);
5600 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5601 return ERROR_SUCCESS;
5603 case INTERNET_OPTION_PASSWORD:
5605 heap_free(ses->password);
5606 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5607 return ERROR_SUCCESS;
5609 case INTERNET_OPTION_CONNECT_TIMEOUT:
5611 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5612 ses->connect_timeout = *(DWORD *)buffer;
5613 return ERROR_SUCCESS;
5615 case INTERNET_OPTION_SEND_TIMEOUT:
5617 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5618 ses->send_timeout = *(DWORD *)buffer;
5619 return ERROR_SUCCESS;
5621 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5623 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5624 ses->receive_timeout = *(DWORD *)buffer;
5625 return ERROR_SUCCESS;
5627 default: break;
5630 return INET_SetOption(hdr, option, buffer, size);
5633 static const object_vtbl_t HTTPSESSIONVtbl = {
5634 HTTPSESSION_Destroy,
5635 NULL,
5636 HTTPSESSION_QueryOption,
5637 HTTPSESSION_SetOption,
5638 NULL,
5639 NULL,
5640 NULL,
5641 NULL,
5642 NULL
5646 /***********************************************************************
5647 * HTTP_Connect (internal)
5649 * Create http session handle
5651 * RETURNS
5652 * HINTERNET a session handle on success
5653 * NULL on failure
5656 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5657 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5658 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5659 DWORD dwInternalFlags, HINTERNET *ret)
5661 http_session_t *session = NULL;
5663 TRACE("-->\n");
5665 if (!lpszServerName || !lpszServerName[0])
5666 return ERROR_INVALID_PARAMETER;
5668 assert( hIC->hdr.htype == WH_HINIT );
5670 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5671 if (!session)
5672 return ERROR_OUTOFMEMORY;
5675 * According to my tests. The name is not resolved until a request is sent
5678 session->hdr.htype = WH_HHTTPSESSION;
5679 session->hdr.dwFlags = dwFlags;
5680 session->hdr.dwContext = dwContext;
5681 session->hdr.dwInternalFlags |= dwInternalFlags;
5683 WININET_AddRef( &hIC->hdr );
5684 session->appInfo = hIC;
5685 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5687 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5688 if(hIC->proxyBypass)
5689 FIXME("Proxy bypass is ignored.\n");
5691 session->hostName = heap_strdupW(lpszServerName);
5692 if (lpszUserName && lpszUserName[0])
5693 session->userName = heap_strdupW(lpszUserName);
5694 if (lpszPassword && lpszPassword[0])
5695 session->password = heap_strdupW(lpszPassword);
5696 session->hostPort = serverPort;
5697 session->connect_timeout = hIC->connect_timeout;
5698 session->send_timeout = INFINITE;
5699 session->receive_timeout = INFINITE;
5701 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5702 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5704 INTERNET_SendCallback(&hIC->hdr, dwContext,
5705 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5706 sizeof(HINTERNET));
5710 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5711 * windows
5714 TRACE("%p --> %p\n", hIC, session);
5716 *ret = session->hdr.hInternet;
5717 return ERROR_SUCCESS;
5720 /***********************************************************************
5721 * HTTP_clear_response_headers (internal)
5723 * clear out any old response headers
5725 static void HTTP_clear_response_headers( http_request_t *request )
5727 DWORD i;
5729 for( i=0; i<request->nCustHeaders; i++)
5731 if( !request->custHeaders[i].lpszField )
5732 continue;
5733 if( !request->custHeaders[i].lpszValue )
5734 continue;
5735 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5736 continue;
5737 HTTP_DeleteCustomHeader( request, i );
5738 i--;
5742 /***********************************************************************
5743 * HTTP_GetResponseHeaders (internal)
5745 * Read server response
5747 * RETURNS
5749 * TRUE on success
5750 * FALSE on error
5752 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5754 INT cbreaks = 0;
5755 WCHAR buffer[MAX_REPLY_LEN];
5756 DWORD buflen = MAX_REPLY_LEN;
5757 BOOL bSuccess = FALSE;
5758 INT rc = 0;
5759 char bufferA[MAX_REPLY_LEN];
5760 LPWSTR status_code = NULL, status_text = NULL;
5761 DWORD cchMaxRawHeaders = 1024;
5762 LPWSTR lpszRawHeaders = NULL;
5763 LPWSTR temp;
5764 DWORD cchRawHeaders = 0;
5765 BOOL codeHundred = FALSE;
5767 TRACE("-->\n");
5769 if(!request->netconn)
5770 goto lend;
5772 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5773 do {
5774 static const WCHAR szHundred[] = {'1','0','0',0};
5776 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5778 buflen = MAX_REPLY_LEN;
5779 if (!read_line(request, bufferA, &buflen))
5780 goto lend;
5782 /* clear old response headers (eg. from a redirect response) */
5783 if (clear) {
5784 HTTP_clear_response_headers( request );
5785 clear = FALSE;
5788 rc += buflen;
5789 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5790 /* check is this a status code line? */
5791 if (!strncmpW(buffer, g_szHttp1_0, 4))
5793 /* split the version from the status code */
5794 status_code = strchrW( buffer, ' ' );
5795 if( !status_code )
5796 goto lend;
5797 *status_code++=0;
5799 /* split the status code from the status text */
5800 status_text = strchrW( status_code, ' ' );
5801 if( !status_text )
5802 goto lend;
5803 *status_text++=0;
5805 request->status_code = atoiW(status_code);
5807 TRACE("version [%s] status code [%s] status text [%s]\n",
5808 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5810 codeHundred = (!strcmpW(status_code, szHundred));
5812 else if (!codeHundred)
5814 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5816 heap_free(request->version);
5817 heap_free(request->statusText);
5819 request->status_code = HTTP_STATUS_OK;
5820 request->version = heap_strdupW(g_szHttp1_0);
5821 request->statusText = heap_strdupW(szOK);
5823 heap_free(request->rawHeaders);
5824 request->rawHeaders = heap_strdupW(szDefaultHeader);
5826 bSuccess = TRUE;
5827 goto lend;
5829 } while (codeHundred);
5831 /* Add status code */
5832 HTTP_ProcessHeader(request, szStatus, status_code,
5833 HTTP_ADDHDR_FLAG_REPLACE);
5835 heap_free(request->version);
5836 heap_free(request->statusText);
5838 request->version = heap_strdupW(buffer);
5839 request->statusText = heap_strdupW(status_text);
5841 /* Restore the spaces */
5842 *(status_code-1) = ' ';
5843 *(status_text-1) = ' ';
5845 /* regenerate raw headers */
5846 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5847 if (!lpszRawHeaders) goto lend;
5849 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5850 cchMaxRawHeaders *= 2;
5851 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5852 if (temp == NULL) goto lend;
5853 lpszRawHeaders = temp;
5854 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5855 cchRawHeaders += (buflen-1);
5856 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5857 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5858 lpszRawHeaders[cchRawHeaders] = '\0';
5860 /* Parse each response line */
5863 buflen = MAX_REPLY_LEN;
5864 if (read_line(request, bufferA, &buflen))
5866 LPWSTR * pFieldAndValue;
5868 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5870 if (!bufferA[0]) break;
5871 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5873 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5874 if (pFieldAndValue)
5876 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5877 cchMaxRawHeaders *= 2;
5878 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5879 if (temp == NULL) goto lend;
5880 lpszRawHeaders = temp;
5881 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5882 cchRawHeaders += (buflen-1);
5883 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5884 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5885 lpszRawHeaders[cchRawHeaders] = '\0';
5887 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5888 HTTP_ADDREQ_FLAG_ADD );
5890 HTTP_FreeTokens(pFieldAndValue);
5893 else
5895 cbreaks++;
5896 if (cbreaks >= 2)
5897 break;
5899 }while(1);
5901 /* make sure the response header is terminated with an empty line. Some apps really
5902 truly care about that empty line being there for some reason. Just add it to the
5903 header. */
5904 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5906 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5907 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5908 if (temp == NULL) goto lend;
5909 lpszRawHeaders = temp;
5912 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5914 heap_free(request->rawHeaders);
5915 request->rawHeaders = lpszRawHeaders;
5916 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5917 bSuccess = TRUE;
5919 lend:
5921 TRACE("<--\n");
5922 if (bSuccess)
5923 return rc;
5924 else
5926 heap_free(lpszRawHeaders);
5927 return 0;
5931 /***********************************************************************
5932 * HTTP_InterpretHttpHeader (internal)
5934 * Parse server response
5936 * RETURNS
5938 * Pointer to array of field, value, NULL on success.
5939 * NULL on error.
5941 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5943 LPWSTR * pTokenPair;
5944 LPWSTR pszColon;
5945 INT len;
5947 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5949 pszColon = strchrW(buffer, ':');
5950 /* must have two tokens */
5951 if (!pszColon)
5953 HTTP_FreeTokens(pTokenPair);
5954 if (buffer[0])
5955 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5956 return NULL;
5959 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5960 if (!pTokenPair[0])
5962 HTTP_FreeTokens(pTokenPair);
5963 return NULL;
5965 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5966 pTokenPair[0][pszColon - buffer] = '\0';
5968 /* skip colon */
5969 pszColon++;
5970 len = strlenW(pszColon);
5971 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5972 if (!pTokenPair[1])
5974 HTTP_FreeTokens(pTokenPair);
5975 return NULL;
5977 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5979 strip_spaces(pTokenPair[0]);
5980 strip_spaces(pTokenPair[1]);
5982 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5983 return pTokenPair;
5986 /***********************************************************************
5987 * HTTP_ProcessHeader (internal)
5989 * Stuff header into header tables according to <dwModifier>
5993 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5995 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5997 LPHTTPHEADERW lphttpHdr = NULL;
5998 INT index = -1;
5999 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
6000 DWORD res = ERROR_HTTP_INVALID_HEADER;
6002 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
6004 /* REPLACE wins out over ADD */
6005 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6006 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
6008 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
6009 index = -1;
6010 else
6011 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
6013 if (index >= 0)
6015 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
6016 return ERROR_HTTP_INVALID_HEADER;
6017 lphttpHdr = &request->custHeaders[index];
6019 else if (value)
6021 HTTPHEADERW hdr;
6023 hdr.lpszField = (LPWSTR)field;
6024 hdr.lpszValue = (LPWSTR)value;
6025 hdr.wFlags = hdr.wCount = 0;
6027 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6028 hdr.wFlags |= HDR_ISREQUEST;
6030 return HTTP_InsertCustomHeader(request, &hdr);
6032 /* no value to delete */
6033 else return ERROR_SUCCESS;
6035 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6036 lphttpHdr->wFlags |= HDR_ISREQUEST;
6037 else
6038 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
6040 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6042 HTTP_DeleteCustomHeader( request, index );
6044 if (value)
6046 HTTPHEADERW hdr;
6048 hdr.lpszField = (LPWSTR)field;
6049 hdr.lpszValue = (LPWSTR)value;
6050 hdr.wFlags = hdr.wCount = 0;
6052 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6053 hdr.wFlags |= HDR_ISREQUEST;
6055 return HTTP_InsertCustomHeader(request, &hdr);
6058 return ERROR_SUCCESS;
6060 else if (dwModifier & COALESCEFLAGS)
6062 LPWSTR lpsztmp;
6063 WCHAR ch = 0;
6064 INT len = 0;
6065 INT origlen = strlenW(lphttpHdr->lpszValue);
6066 INT valuelen = strlenW(value);
6068 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6070 ch = ',';
6071 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6073 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6075 ch = ';';
6076 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6079 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6081 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6082 if (lpsztmp)
6084 lphttpHdr->lpszValue = lpsztmp;
6085 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6086 if (ch > 0)
6088 lphttpHdr->lpszValue[origlen] = ch;
6089 origlen++;
6090 lphttpHdr->lpszValue[origlen] = ' ';
6091 origlen++;
6094 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6095 lphttpHdr->lpszValue[len] = '\0';
6096 res = ERROR_SUCCESS;
6098 else
6100 WARN("heap_realloc (%d bytes) failed\n",len+1);
6101 res = ERROR_OUTOFMEMORY;
6104 TRACE("<-- %d\n", res);
6105 return res;
6108 /***********************************************************************
6109 * HTTP_GetCustomHeaderIndex (internal)
6111 * Return index of custom header from header array
6114 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6115 int requested_index, BOOL request_only)
6117 DWORD index;
6119 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6121 for (index = 0; index < request->nCustHeaders; index++)
6123 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6124 continue;
6126 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6127 continue;
6129 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6130 continue;
6132 if (requested_index == 0)
6133 break;
6134 requested_index --;
6137 if (index >= request->nCustHeaders)
6138 index = -1;
6140 TRACE("Return: %d\n", index);
6141 return index;
6145 /***********************************************************************
6146 * HTTP_InsertCustomHeader (internal)
6148 * Insert header into array
6151 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6153 INT count;
6154 LPHTTPHEADERW lph = NULL;
6156 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6157 count = request->nCustHeaders + 1;
6158 if (count > 1)
6159 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6160 else
6161 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6163 if (!lph)
6164 return ERROR_OUTOFMEMORY;
6166 request->custHeaders = lph;
6167 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6168 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6169 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6170 request->custHeaders[count-1].wCount= lpHdr->wCount;
6171 request->nCustHeaders++;
6173 return ERROR_SUCCESS;
6177 /***********************************************************************
6178 * HTTP_DeleteCustomHeader (internal)
6180 * Delete header from array
6181 * If this function is called, the indexs may change.
6183 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6185 if( request->nCustHeaders <= 0 )
6186 return FALSE;
6187 if( index >= request->nCustHeaders )
6188 return FALSE;
6189 request->nCustHeaders--;
6191 heap_free(request->custHeaders[index].lpszField);
6192 heap_free(request->custHeaders[index].lpszValue);
6194 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6195 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6196 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6198 return TRUE;
6202 /***********************************************************************
6203 * HTTP_VerifyValidHeader (internal)
6205 * Verify the given header is not invalid for the given http request
6208 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6210 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6211 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6212 return ERROR_HTTP_INVALID_HEADER;
6214 return ERROR_SUCCESS;
6217 /***********************************************************************
6218 * IsHostInProxyBypassList (@)
6220 * Undocumented
6223 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6225 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6226 return FALSE;
6229 /***********************************************************************
6230 * InternetShowSecurityInfoByURLA (@)
6232 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6234 FIXME("stub: %s %p\n", url, window);
6235 return FALSE;
6238 /***********************************************************************
6239 * InternetShowSecurityInfoByURLW (@)
6241 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6243 FIXME("stub: %s %p\n", debugstr_w(url), window);
6244 return FALSE;
6247 /***********************************************************************
6248 * ShowX509EncodedCertificate (@)
6250 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6252 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6253 cert, len);
6254 DWORD ret;
6256 if (certContext)
6258 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6260 memset(&view, 0, sizeof(view));
6261 view.hwndParent = parent;
6262 view.pCertContext = certContext;
6263 if (CryptUIDlgViewCertificateW(&view, NULL))
6264 ret = ERROR_SUCCESS;
6265 else
6266 ret = GetLastError();
6267 CertFreeCertificateContext(certContext);
6269 else
6270 ret = GetLastError();
6271 return ret;