winex11: Create contexts at initialization time to avoid the need for locks.
[wine/multimedia.git] / dlls / wininet / http.c
blob28f82a365325ceb89f1423276d51625abb1beda9
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_SYSTEM_DEFAULT, 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 server_release(request->server);
1733 request->server = new_server;
1735 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1736 return TRUE;
1739 static DWORD HTTP_ResolveName(http_request_t *request)
1741 server_t *server = request->server;
1742 socklen_t addr_len;
1743 void *addr;
1745 if(server->addr_len)
1746 return ERROR_SUCCESS;
1748 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1749 INTERNET_STATUS_RESOLVING_NAME,
1750 server->name,
1751 (strlenW(server->name)+1) * sizeof(WCHAR));
1753 addr_len = sizeof(server->addr);
1754 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1755 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1757 switch(server->addr.ss_family) {
1758 case AF_INET:
1759 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1760 break;
1761 case AF_INET6:
1762 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1763 break;
1764 default:
1765 WARN("unsupported family %d\n", server->addr.ss_family);
1766 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1769 server->addr_len = addr_len;
1770 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1771 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1772 INTERNET_STATUS_NAME_RESOLVED,
1773 server->addr_str, strlen(server->addr_str)+1);
1775 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1776 return ERROR_SUCCESS;
1779 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1781 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1782 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1783 static const WCHAR slash[] = { '/',0 };
1784 LPHTTPHEADERW host_header;
1785 LPCWSTR scheme;
1787 host_header = HTTP_GetHeader(req, hostW);
1788 if(!host_header)
1789 return FALSE;
1791 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1792 scheme = https;
1793 else
1794 scheme = http;
1795 strcpyW(buf, scheme);
1796 strcatW(buf, host_header->lpszValue);
1797 if (req->path[0] != '/')
1798 strcatW(buf, slash);
1799 strcatW(buf, req->path);
1800 return TRUE;
1804 /***********************************************************************
1805 * HTTPREQ_Destroy (internal)
1807 * Deallocate request handle
1810 static void HTTPREQ_Destroy(object_header_t *hdr)
1812 http_request_t *request = (http_request_t*) hdr;
1813 DWORD i;
1815 TRACE("\n");
1817 if(request->hCacheFile) {
1818 WCHAR url[INTERNET_MAX_URL_LENGTH];
1820 CloseHandle(request->hCacheFile);
1822 if(HTTP_GetRequestURL(request, url)) {
1823 DWORD headersLen;
1825 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1826 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1827 request->last_modified, NORMAL_CACHE_ENTRY,
1828 request->rawHeaders, headersLen, NULL, 0);
1831 heap_free(request->cacheFile);
1833 request->read_section.DebugInfo->Spare[0] = 0;
1834 DeleteCriticalSection( &request->read_section );
1835 WININET_Release(&request->session->hdr);
1837 destroy_authinfo(request->authInfo);
1838 destroy_authinfo(request->proxyAuthInfo);
1840 if(request->server)
1841 server_release(request->server);
1843 heap_free(request->path);
1844 heap_free(request->verb);
1845 heap_free(request->rawHeaders);
1846 heap_free(request->version);
1847 heap_free(request->statusText);
1849 for (i = 0; i < request->nCustHeaders; i++)
1851 heap_free(request->custHeaders[i].lpszField);
1852 heap_free(request->custHeaders[i].lpszValue);
1854 destroy_data_stream(request->data_stream);
1855 heap_free(request->custHeaders);
1858 static void http_release_netconn(http_request_t *req, BOOL reuse)
1860 TRACE("%p %p\n",req, req->netconn);
1862 if(!req->netconn)
1863 return;
1865 if(reuse && req->netconn->keep_alive) {
1866 BOOL run_collector;
1868 EnterCriticalSection(&connection_pool_cs);
1870 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1871 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1872 req->netconn = NULL;
1874 run_collector = !collector_running;
1875 collector_running = TRUE;
1877 LeaveCriticalSection(&connection_pool_cs);
1879 if(run_collector) {
1880 HANDLE thread = NULL;
1881 HMODULE module;
1883 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1884 if(module)
1885 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1886 if(!thread) {
1887 EnterCriticalSection(&connection_pool_cs);
1888 collector_running = FALSE;
1889 LeaveCriticalSection(&connection_pool_cs);
1891 if(module)
1892 FreeLibrary(module);
1894 else
1895 CloseHandle(thread);
1897 return;
1900 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1901 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1903 free_netconn(req->netconn);
1904 req->netconn = NULL;
1906 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1907 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1910 static BOOL HTTP_KeepAlive(http_request_t *request)
1912 WCHAR szVersion[10];
1913 WCHAR szConnectionResponse[20];
1914 DWORD dwBufferSize = sizeof(szVersion);
1915 BOOL keepalive = FALSE;
1917 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1918 * the connection is keep-alive by default */
1919 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1920 && !strcmpiW(szVersion, g_szHttp1_1))
1922 keepalive = TRUE;
1925 dwBufferSize = sizeof(szConnectionResponse);
1926 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1927 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1929 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1932 return keepalive;
1935 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1937 http_request_t *req = (http_request_t*)hdr;
1939 http_release_netconn(req, drain_content(req, FALSE));
1942 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1944 http_request_t *req = (http_request_t*)hdr;
1946 switch(option) {
1947 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1949 http_session_t *session = req->session;
1950 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1952 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1954 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1955 return ERROR_INSUFFICIENT_BUFFER;
1956 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1957 /* FIXME: can't get a SOCKET from our connection since we don't use
1958 * winsock
1960 info->Socket = 0;
1961 /* FIXME: get source port from req->netConnection */
1962 info->SourcePort = 0;
1963 info->DestPort = session->hostPort;
1964 info->Flags = 0;
1965 if (HTTP_KeepAlive(req))
1966 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1967 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1968 info->Flags |= IDSI_FLAG_PROXY;
1969 if (req->netconn->useSSL)
1970 info->Flags |= IDSI_FLAG_SECURE;
1972 return ERROR_SUCCESS;
1975 case 98:
1976 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1977 /* fall through */
1978 case INTERNET_OPTION_SECURITY_FLAGS:
1980 DWORD flags;
1982 if (*size < sizeof(ULONG))
1983 return ERROR_INSUFFICIENT_BUFFER;
1985 *size = sizeof(DWORD);
1986 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
1987 *(DWORD *)buffer = flags;
1989 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
1990 return ERROR_SUCCESS;
1993 case INTERNET_OPTION_HANDLE_TYPE:
1994 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1996 if (*size < sizeof(ULONG))
1997 return ERROR_INSUFFICIENT_BUFFER;
1999 *size = sizeof(DWORD);
2000 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2001 return ERROR_SUCCESS;
2003 case INTERNET_OPTION_URL: {
2004 WCHAR url[INTERNET_MAX_URL_LENGTH];
2005 HTTPHEADERW *host;
2006 DWORD len;
2007 WCHAR *pch;
2009 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2011 TRACE("INTERNET_OPTION_URL\n");
2013 host = HTTP_GetHeader(req, hostW);
2014 strcpyW(url, httpW);
2015 strcatW(url, host->lpszValue);
2016 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2017 *pch = 0;
2018 strcatW(url, req->path);
2020 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2022 if(unicode) {
2023 len = (strlenW(url)+1) * sizeof(WCHAR);
2024 if(*size < len)
2025 return ERROR_INSUFFICIENT_BUFFER;
2027 *size = len;
2028 strcpyW(buffer, url);
2029 return ERROR_SUCCESS;
2030 }else {
2031 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2032 if(len > *size)
2033 return ERROR_INSUFFICIENT_BUFFER;
2035 *size = len;
2036 return ERROR_SUCCESS;
2040 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2041 INTERNET_CACHE_ENTRY_INFOW *info;
2042 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2043 WCHAR url[INTERNET_MAX_URL_LENGTH];
2044 DWORD nbytes, error;
2045 BOOL ret;
2047 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2049 if (*size < sizeof(*ts))
2051 *size = sizeof(*ts);
2052 return ERROR_INSUFFICIENT_BUFFER;
2054 nbytes = 0;
2055 HTTP_GetRequestURL(req, url);
2056 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2057 error = GetLastError();
2058 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2060 if (!(info = heap_alloc(nbytes)))
2061 return ERROR_OUTOFMEMORY;
2063 GetUrlCacheEntryInfoW(url, info, &nbytes);
2065 ts->ftExpires = info->ExpireTime;
2066 ts->ftLastModified = info->LastModifiedTime;
2068 heap_free(info);
2069 *size = sizeof(*ts);
2070 return ERROR_SUCCESS;
2072 return error;
2075 case INTERNET_OPTION_DATAFILE_NAME: {
2076 DWORD req_size;
2078 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2080 if(!req->cacheFile) {
2081 *size = 0;
2082 return ERROR_INTERNET_ITEM_NOT_FOUND;
2085 if(unicode) {
2086 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2087 if(*size < req_size)
2088 return ERROR_INSUFFICIENT_BUFFER;
2090 *size = req_size;
2091 memcpy(buffer, req->cacheFile, *size);
2092 return ERROR_SUCCESS;
2093 }else {
2094 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2095 if (req_size > *size)
2096 return ERROR_INSUFFICIENT_BUFFER;
2098 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2099 -1, buffer, *size, NULL, NULL);
2100 return ERROR_SUCCESS;
2104 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2105 PCCERT_CONTEXT context;
2107 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2108 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2109 return ERROR_INSUFFICIENT_BUFFER;
2112 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2113 if(context) {
2114 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2115 DWORD len;
2117 memset(info, 0, sizeof(*info));
2118 info->ftExpiry = context->pCertInfo->NotAfter;
2119 info->ftStart = context->pCertInfo->NotBefore;
2120 len = CertNameToStrA(context->dwCertEncodingType,
2121 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2122 info->lpszSubjectInfo = LocalAlloc(0, len);
2123 if(info->lpszSubjectInfo)
2124 CertNameToStrA(context->dwCertEncodingType,
2125 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2126 info->lpszSubjectInfo, len);
2127 len = CertNameToStrA(context->dwCertEncodingType,
2128 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2129 info->lpszIssuerInfo = LocalAlloc(0, len);
2130 if(info->lpszIssuerInfo)
2131 CertNameToStrA(context->dwCertEncodingType,
2132 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2133 info->lpszIssuerInfo, len);
2134 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2135 CertFreeCertificateContext(context);
2136 return ERROR_SUCCESS;
2138 return ERROR_NOT_SUPPORTED;
2140 case INTERNET_OPTION_CONNECT_TIMEOUT:
2141 if (*size < sizeof(DWORD))
2142 return ERROR_INSUFFICIENT_BUFFER;
2144 *size = sizeof(DWORD);
2145 *(DWORD *)buffer = req->connect_timeout;
2146 return ERROR_SUCCESS;
2147 case INTERNET_OPTION_REQUEST_FLAGS: {
2148 DWORD flags = 0;
2150 if (*size < sizeof(DWORD))
2151 return ERROR_INSUFFICIENT_BUFFER;
2153 /* FIXME: Add support for:
2154 * INTERNET_REQFLAG_FROM_CACHE
2155 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2158 if(req->session->appInfo->proxy)
2159 flags |= INTERNET_REQFLAG_VIA_PROXY;
2160 if(!req->rawHeaders)
2161 flags |= INTERNET_REQFLAG_NO_HEADERS;
2163 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2165 *size = sizeof(DWORD);
2166 *(DWORD*)buffer = flags;
2167 return ERROR_SUCCESS;
2171 return INET_QueryOption(hdr, option, buffer, size, unicode);
2174 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2176 http_request_t *req = (http_request_t*)hdr;
2178 switch(option) {
2179 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2180 TRACE("Undocumented option 99\n");
2182 if (!buffer || size != sizeof(DWORD))
2183 return ERROR_INVALID_PARAMETER;
2184 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2185 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2187 /* fall through */
2188 case INTERNET_OPTION_SECURITY_FLAGS:
2190 DWORD flags;
2192 if (!buffer || size != sizeof(DWORD))
2193 return ERROR_INVALID_PARAMETER;
2194 flags = *(DWORD *)buffer;
2195 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2196 flags &= SECURITY_SET_MASK;
2197 req->security_flags |= flags;
2198 if(req->netconn)
2199 req->netconn->security_flags |= flags;
2200 return ERROR_SUCCESS;
2202 case INTERNET_OPTION_CONNECT_TIMEOUT:
2203 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2204 req->connect_timeout = *(DWORD *)buffer;
2205 return ERROR_SUCCESS;
2207 case INTERNET_OPTION_SEND_TIMEOUT:
2208 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2209 req->send_timeout = *(DWORD *)buffer;
2210 return ERROR_SUCCESS;
2212 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2213 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2214 req->receive_timeout = *(DWORD *)buffer;
2215 return ERROR_SUCCESS;
2217 case INTERNET_OPTION_USERNAME:
2218 heap_free(req->session->userName);
2219 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2220 return ERROR_SUCCESS;
2222 case INTERNET_OPTION_PASSWORD:
2223 heap_free(req->session->password);
2224 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2225 return ERROR_SUCCESS;
2226 case INTERNET_OPTION_HTTP_DECODING:
2227 if(size != sizeof(BOOL))
2228 return ERROR_INVALID_PARAMETER;
2229 req->decoding = *(BOOL*)buffer;
2230 return ERROR_SUCCESS;
2233 return INET_SetOption(hdr, option, buffer, size);
2236 /* read some more data into the read buffer (the read section must be held) */
2237 static DWORD read_more_data( http_request_t *req, int maxlen )
2239 DWORD res;
2240 int len;
2242 if (req->read_pos)
2244 /* move existing data to the start of the buffer */
2245 if(req->read_size)
2246 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2247 req->read_pos = 0;
2250 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2252 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2253 maxlen - req->read_size, 0, &len );
2254 if(res == ERROR_SUCCESS)
2255 req->read_size += len;
2257 return res;
2260 /* remove some amount of data from the read buffer (the read section must be held) */
2261 static void remove_data( http_request_t *req, int count )
2263 if (!(req->read_size -= count)) req->read_pos = 0;
2264 else req->read_pos += count;
2267 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2269 int count, bytes_read, pos = 0;
2270 DWORD res;
2272 EnterCriticalSection( &req->read_section );
2273 for (;;)
2275 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2277 if (eol)
2279 count = eol - (req->read_buf + req->read_pos);
2280 bytes_read = count + 1;
2282 else count = bytes_read = req->read_size;
2284 count = min( count, *len - pos );
2285 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2286 pos += count;
2287 remove_data( req, bytes_read );
2288 if (eol) break;
2290 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2292 *len = 0;
2293 TRACE( "returning empty string %u\n", res);
2294 LeaveCriticalSection( &req->read_section );
2295 INTERNET_SetLastError(res);
2296 return FALSE;
2299 LeaveCriticalSection( &req->read_section );
2301 if (pos < *len)
2303 if (pos && buffer[pos - 1] == '\r') pos--;
2304 *len = pos + 1;
2306 buffer[*len - 1] = 0;
2307 TRACE( "returning %s\n", debugstr_a(buffer));
2308 return TRUE;
2311 /* check if we have reached the end of the data to read (the read section must be held) */
2312 static BOOL end_of_read_data( http_request_t *req )
2314 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2317 /* fetch some more data into the read buffer (the read section must be held) */
2318 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2320 DWORD res, read=0, want;
2322 if(req->read_size == sizeof(req->read_buf))
2323 return ERROR_SUCCESS;
2325 if(req->read_pos) {
2326 if(req->read_size)
2327 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2328 req->read_pos = 0;
2331 want = sizeof(req->read_buf) - req->read_size;
2332 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2333 want, &read, read_mode);
2334 assert(read <= want);
2335 req->read_size += read;
2337 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2338 if(read_bytes)
2339 *read_bytes = read;
2340 return res;
2343 /* return the size of data available to be read immediately (the read section must be held) */
2344 static DWORD get_avail_data( http_request_t *req )
2346 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2349 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2351 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2352 DWORD avail = 0;
2354 if(req->netconn)
2355 NETCON_query_data_available(req->netconn, &avail);
2356 return netconn_stream->content_length == ~0u
2357 ? avail
2358 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2361 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2363 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2364 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2367 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2368 DWORD *read, read_mode_t read_mode)
2370 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2371 int len = 0;
2373 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2375 if(read_mode == READMODE_NOBLOCK) {
2376 DWORD avail = netconn_get_avail_data(stream, req);
2377 if (size > avail)
2378 size = avail;
2381 if(size && req->netconn) {
2382 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2383 len = 0;
2384 if(!len)
2385 netconn_stream->content_length = netconn_stream->content_read;
2388 netconn_stream->content_read += *read = len;
2389 TRACE("read %u bytes\n", len);
2390 return ERROR_SUCCESS;
2393 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2395 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2396 BYTE buf[1024];
2397 DWORD avail;
2398 int len;
2400 if(netconn_end_of_data(stream, req))
2401 return TRUE;
2403 do {
2404 avail = netconn_get_avail_data(stream, req);
2405 if(!avail)
2406 return FALSE;
2408 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2409 return FALSE;
2411 netconn_stream->content_read += len;
2412 }while(netconn_stream->content_read < netconn_stream->content_length);
2414 return TRUE;
2417 static void netconn_destroy(data_stream_t *stream)
2421 static const data_stream_vtbl_t netconn_stream_vtbl = {
2422 netconn_get_avail_data,
2423 netconn_end_of_data,
2424 netconn_read,
2425 netconn_drain_content,
2426 netconn_destroy
2429 /* read some more data into the read buffer (the read section must be held) */
2430 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2432 DWORD res;
2433 int len;
2435 if (stream->buf_pos)
2437 /* move existing data to the start of the buffer */
2438 if(stream->buf_size)
2439 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2440 stream->buf_pos = 0;
2443 if (maxlen == -1) maxlen = sizeof(stream->buf);
2445 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2446 maxlen - stream->buf_size, 0, &len );
2447 if(res == ERROR_SUCCESS)
2448 stream->buf_size += len;
2450 return res;
2453 /* remove some amount of data from the read buffer (the read section must be held) */
2454 static void remove_chunked_data(chunked_stream_t *stream, int count)
2456 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2457 else stream->buf_pos += count;
2460 /* discard data contents until we reach end of line (the read section must be held) */
2461 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2463 DWORD res;
2467 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2468 if (eol)
2470 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2471 break;
2473 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2474 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2475 } while (stream->buf_size);
2476 return ERROR_SUCCESS;
2479 /* read the size of the next chunk (the read section must be held) */
2480 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2482 /* TODOO */
2483 DWORD chunk_size = 0, res;
2485 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2486 return res;
2488 for (;;)
2490 while (stream->buf_size)
2492 char ch = stream->buf[stream->buf_pos];
2493 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2494 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2495 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2496 else if (ch == ';' || ch == '\r' || ch == '\n')
2498 TRACE( "reading %u byte chunk\n", chunk_size );
2499 stream->chunk_size = chunk_size;
2500 req->contentLength += chunk_size;
2501 return discard_chunked_eol(stream, req);
2503 remove_chunked_data(stream, 1);
2505 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2506 if (!stream->buf_size)
2508 stream->chunk_size = 0;
2509 return ERROR_SUCCESS;
2514 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2516 /* Allow reading only from read buffer */
2517 return 0;
2520 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2522 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2523 return !chunked_stream->chunk_size;
2526 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2527 DWORD *read, read_mode_t read_mode)
2529 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2530 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2532 if(chunked_stream->chunk_size == ~0u) {
2533 res = start_next_chunk(chunked_stream, req);
2534 if(res != ERROR_SUCCESS)
2535 return res;
2538 while(size && chunked_stream->chunk_size) {
2539 if(chunked_stream->buf_size) {
2540 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2542 /* this could block */
2543 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2544 break;
2546 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2547 remove_chunked_data(chunked_stream, read_bytes);
2548 }else {
2549 read_bytes = min(size, chunked_stream->chunk_size);
2551 if(read_mode == READMODE_NOBLOCK) {
2552 DWORD avail;
2554 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2555 break;
2556 if(read_bytes > avail)
2557 read_bytes = avail;
2559 /* this could block */
2560 if(read_bytes == chunked_stream->chunk_size)
2561 break;
2564 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2565 if(res != ERROR_SUCCESS)
2566 break;
2569 chunked_stream->chunk_size -= read_bytes;
2570 size -= read_bytes;
2571 ret_read += read_bytes;
2572 if(!chunked_stream->chunk_size) {
2573 assert(read_mode != READMODE_NOBLOCK);
2574 res = start_next_chunk(chunked_stream, req);
2575 if(res != ERROR_SUCCESS)
2576 break;
2579 if(read_mode == READMODE_ASYNC)
2580 read_mode = READMODE_NOBLOCK;
2583 TRACE("read %u bytes\n", ret_read);
2584 *read = ret_read;
2585 return res;
2588 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2590 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2592 /* FIXME: we can do better */
2593 return !chunked_stream->chunk_size;
2596 static void chunked_destroy(data_stream_t *stream)
2598 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2599 heap_free(chunked_stream);
2602 static const data_stream_vtbl_t chunked_stream_vtbl = {
2603 chunked_get_avail_data,
2604 chunked_end_of_data,
2605 chunked_read,
2606 chunked_drain_content,
2607 chunked_destroy
2610 /* set the request content length based on the headers */
2611 static DWORD set_content_length(http_request_t *request)
2613 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2614 WCHAR encoding[20];
2615 DWORD size;
2617 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2618 request->contentLength = request->netconn_stream.content_length = 0;
2619 return ERROR_SUCCESS;
2622 size = sizeof(request->contentLength);
2623 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2624 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2625 request->contentLength = ~0u;
2626 request->netconn_stream.content_length = request->contentLength;
2627 request->netconn_stream.content_read = request->read_size;
2629 size = sizeof(encoding);
2630 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2631 !strcmpiW(encoding, szChunked))
2633 chunked_stream_t *chunked_stream;
2635 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2636 if(!chunked_stream)
2637 return ERROR_OUTOFMEMORY;
2639 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2640 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2641 chunked_stream->chunk_size = ~0u;
2643 if(request->read_size) {
2644 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2645 chunked_stream->buf_size = request->read_size;
2646 request->read_size = request->read_pos = 0;
2649 request->data_stream = &chunked_stream->data_stream;
2650 request->contentLength = ~0u;
2651 request->read_chunked = TRUE;
2654 if(request->decoding) {
2655 int encoding_idx;
2657 static const WCHAR gzipW[] = {'g','z','i','p',0};
2659 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2660 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2661 return init_gzip_stream(request);
2664 return ERROR_SUCCESS;
2667 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2669 INTERNET_ASYNC_RESULT iar;
2671 iar.dwResult = result;
2672 iar.dwError = error;
2674 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2675 sizeof(INTERNET_ASYNC_RESULT));
2678 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2680 DWORD res, read = 0, avail = 0;
2681 read_mode_t mode;
2683 TRACE("%p\n", req);
2685 EnterCriticalSection( &req->read_section );
2687 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2688 res = refill_read_buffer(req, mode, &read);
2689 if(res == ERROR_SUCCESS && !first_notif)
2690 avail = get_avail_data(req);
2692 LeaveCriticalSection( &req->read_section );
2694 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2695 WARN("res %u read %u, closing connection\n", res, read);
2696 http_release_netconn(req, FALSE);
2699 if(res == ERROR_SUCCESS)
2700 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2701 else
2702 send_request_complete(req, 0, res);
2705 /* read data from the http connection (the read section must be held) */
2706 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2708 DWORD current_read = 0, ret_read = 0;
2709 read_mode_t read_mode;
2710 DWORD res = ERROR_SUCCESS;
2712 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2714 EnterCriticalSection( &req->read_section );
2716 if(req->read_size) {
2717 ret_read = min(size, req->read_size);
2718 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2719 req->read_size -= ret_read;
2720 req->read_pos += ret_read;
2721 if(read_mode == READMODE_ASYNC)
2722 read_mode = READMODE_NOBLOCK;
2725 if(ret_read < size) {
2726 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2727 ret_read += current_read;
2730 LeaveCriticalSection( &req->read_section );
2732 *read = ret_read;
2733 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2735 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2736 BOOL res;
2737 DWORD written;
2739 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2740 if(!res)
2741 WARN("WriteFile failed: %u\n", GetLastError());
2744 if(size && !ret_read)
2745 http_release_netconn(req, res == ERROR_SUCCESS);
2747 return res;
2750 static BOOL drain_content(http_request_t *req, BOOL blocking)
2752 BOOL ret;
2754 if(!req->netconn || req->contentLength == -1)
2755 return FALSE;
2757 if(!strcmpW(req->verb, szHEAD))
2758 return TRUE;
2760 if(!blocking)
2761 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2763 EnterCriticalSection( &req->read_section );
2765 while(1) {
2766 DWORD bytes_read, res;
2767 BYTE buf[4096];
2769 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2770 if(res != ERROR_SUCCESS) {
2771 ret = FALSE;
2772 break;
2774 if(!bytes_read) {
2775 ret = TRUE;
2776 break;
2780 LeaveCriticalSection( &req->read_section );
2781 return ret;
2784 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2786 http_request_t *req = (http_request_t*)hdr;
2787 DWORD res;
2789 EnterCriticalSection( &req->read_section );
2790 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2791 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2793 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2794 if(res == ERROR_SUCCESS)
2795 res = hdr->dwError;
2796 LeaveCriticalSection( &req->read_section );
2798 return res;
2801 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2803 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2804 http_request_t *req = (http_request_t*)workRequest->hdr;
2805 DWORD res;
2807 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2809 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2810 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2812 send_request_complete(req, res == ERROR_SUCCESS, res);
2815 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2816 DWORD flags, DWORD_PTR context)
2818 http_request_t *req = (http_request_t*)hdr;
2819 DWORD res, size, read, error = ERROR_SUCCESS;
2821 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2822 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2824 if (buffers->dwStructSize != sizeof(*buffers))
2825 return ERROR_INVALID_PARAMETER;
2827 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2829 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2831 WORKREQUEST workRequest;
2833 if (TryEnterCriticalSection( &req->read_section ))
2835 if (get_avail_data(req))
2837 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2838 &buffers->dwBufferLength, FALSE);
2839 size = buffers->dwBufferLength;
2840 LeaveCriticalSection( &req->read_section );
2841 goto done;
2843 LeaveCriticalSection( &req->read_section );
2846 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2847 workRequest.hdr = WININET_AddRef(&req->hdr);
2848 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2850 INTERNET_AsyncCall(&workRequest);
2852 return ERROR_IO_PENDING;
2855 read = 0;
2856 size = buffers->dwBufferLength;
2858 EnterCriticalSection( &req->read_section );
2859 if(hdr->dwError == ERROR_SUCCESS)
2860 hdr->dwError = INTERNET_HANDLE_IN_USE;
2861 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2862 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2864 while(1) {
2865 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2866 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2867 if(res != ERROR_SUCCESS)
2868 break;
2870 read += buffers->dwBufferLength;
2871 if(read == size || end_of_read_data(req))
2872 break;
2874 LeaveCriticalSection( &req->read_section );
2876 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2877 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2878 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2879 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2881 EnterCriticalSection( &req->read_section );
2884 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2885 hdr->dwError = ERROR_SUCCESS;
2886 else
2887 error = hdr->dwError;
2889 LeaveCriticalSection( &req->read_section );
2890 size = buffers->dwBufferLength;
2891 buffers->dwBufferLength = read;
2893 done:
2894 if (res == ERROR_SUCCESS) {
2895 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2896 &size, sizeof(size));
2899 return res==ERROR_SUCCESS ? error : res;
2902 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2904 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2905 http_request_t *req = (http_request_t*)workRequest->hdr;
2906 DWORD res;
2908 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2910 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2911 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2913 send_request_complete(req, res == ERROR_SUCCESS, res);
2916 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2917 DWORD flags, DWORD_PTR context)
2920 http_request_t *req = (http_request_t*)hdr;
2921 DWORD res, size, read, error = ERROR_SUCCESS;
2923 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2924 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2926 if (buffers->dwStructSize != sizeof(*buffers))
2927 return ERROR_INVALID_PARAMETER;
2929 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2931 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2933 WORKREQUEST workRequest;
2935 if (TryEnterCriticalSection( &req->read_section ))
2937 if (get_avail_data(req))
2939 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2940 &buffers->dwBufferLength, FALSE);
2941 size = buffers->dwBufferLength;
2942 LeaveCriticalSection( &req->read_section );
2943 goto done;
2945 LeaveCriticalSection( &req->read_section );
2948 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2949 workRequest.hdr = WININET_AddRef(&req->hdr);
2950 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2952 INTERNET_AsyncCall(&workRequest);
2954 return ERROR_IO_PENDING;
2957 read = 0;
2958 size = buffers->dwBufferLength;
2960 EnterCriticalSection( &req->read_section );
2961 if(hdr->dwError == ERROR_SUCCESS)
2962 hdr->dwError = INTERNET_HANDLE_IN_USE;
2963 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2964 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2966 while(1) {
2967 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2968 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2969 if(res != ERROR_SUCCESS)
2970 break;
2972 read += buffers->dwBufferLength;
2973 if(read == size || end_of_read_data(req))
2974 break;
2976 LeaveCriticalSection( &req->read_section );
2978 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2979 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2980 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2981 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2983 EnterCriticalSection( &req->read_section );
2986 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2987 hdr->dwError = ERROR_SUCCESS;
2988 else
2989 error = hdr->dwError;
2991 LeaveCriticalSection( &req->read_section );
2992 size = buffers->dwBufferLength;
2993 buffers->dwBufferLength = read;
2995 done:
2996 if (res == ERROR_SUCCESS) {
2997 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2998 &size, sizeof(size));
3001 return res==ERROR_SUCCESS ? error : res;
3004 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3006 DWORD res;
3007 http_request_t *request = (http_request_t*)hdr;
3009 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3011 *written = 0;
3012 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3013 if (res == ERROR_SUCCESS)
3014 request->bytesWritten += *written;
3016 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3017 return res;
3020 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
3022 http_request_t *req = (http_request_t*)workRequest->hdr;
3024 HTTP_ReceiveRequestData(req, FALSE);
3027 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3029 http_request_t *req = (http_request_t*)hdr;
3031 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3033 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3035 WORKREQUEST workRequest;
3037 /* never wait, if we can't enter the section we queue an async request right away */
3038 if (TryEnterCriticalSection( &req->read_section ))
3040 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3041 if ((*available = get_avail_data( req ))) goto done;
3042 if (end_of_read_data( req )) goto done;
3043 LeaveCriticalSection( &req->read_section );
3046 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3047 workRequest.hdr = WININET_AddRef( &req->hdr );
3049 INTERNET_AsyncCall(&workRequest);
3051 return ERROR_IO_PENDING;
3054 EnterCriticalSection( &req->read_section );
3056 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3058 refill_read_buffer( req, READMODE_ASYNC, NULL );
3059 *available = get_avail_data( req );
3062 done:
3063 LeaveCriticalSection( &req->read_section );
3065 TRACE( "returning %u\n", *available );
3066 return ERROR_SUCCESS;
3069 static const object_vtbl_t HTTPREQVtbl = {
3070 HTTPREQ_Destroy,
3071 HTTPREQ_CloseConnection,
3072 HTTPREQ_QueryOption,
3073 HTTPREQ_SetOption,
3074 HTTPREQ_ReadFile,
3075 HTTPREQ_ReadFileExA,
3076 HTTPREQ_ReadFileExW,
3077 HTTPREQ_WriteFile,
3078 HTTPREQ_QueryDataAvailable,
3079 NULL
3082 /***********************************************************************
3083 * HTTP_HttpOpenRequestW (internal)
3085 * Open a HTTP request handle
3087 * RETURNS
3088 * HINTERNET a HTTP request handle on success
3089 * NULL on failure
3092 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3093 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3094 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3095 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3097 appinfo_t *hIC = session->appInfo;
3098 http_request_t *request;
3099 INTERNET_PORT port;
3100 DWORD len, res = ERROR_SUCCESS;
3102 TRACE("-->\n");
3104 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3105 if(!request)
3106 return ERROR_OUTOFMEMORY;
3108 request->hdr.htype = WH_HHTTPREQ;
3109 request->hdr.dwFlags = dwFlags;
3110 request->hdr.dwContext = dwContext;
3111 request->contentLength = ~0u;
3113 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3114 request->data_stream = &request->netconn_stream.data_stream;
3115 request->connect_timeout = session->connect_timeout;
3116 request->send_timeout = session->send_timeout;
3117 request->receive_timeout = session->receive_timeout;
3119 InitializeCriticalSection( &request->read_section );
3120 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3122 WININET_AddRef( &session->hdr );
3123 request->session = session;
3124 list_add_head( &session->hdr.children, &request->hdr.entry );
3126 port = session->hostPort;
3127 if(port == INTERNET_INVALID_PORT_NUMBER)
3128 port = dwFlags & INTERNET_FLAG_SECURE ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3130 request->server = get_server(session->hostName, port, TRUE);
3131 if(!request->server) {
3132 WININET_Release(&request->hdr);
3133 return ERROR_OUTOFMEMORY;
3136 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3137 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3138 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3139 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3141 if (lpszObjectName && *lpszObjectName) {
3142 HRESULT rc;
3144 len = 0;
3145 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3146 if (rc != E_POINTER)
3147 len = strlenW(lpszObjectName)+1;
3148 request->path = heap_alloc(len*sizeof(WCHAR));
3149 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3150 URL_ESCAPE_SPACES_ONLY);
3151 if (rc != S_OK)
3153 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3154 strcpyW(request->path,lpszObjectName);
3156 }else {
3157 static const WCHAR slashW[] = {'/',0};
3159 request->path = heap_strdupW(slashW);
3162 if (lpszReferrer && *lpszReferrer)
3163 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3165 if (lpszAcceptTypes)
3167 int i;
3168 for (i = 0; lpszAcceptTypes[i]; i++)
3170 if (!*lpszAcceptTypes[i]) continue;
3171 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3172 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3173 HTTP_ADDHDR_FLAG_REQ |
3174 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3178 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3179 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3181 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3182 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3183 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3185 WCHAR *host_name;
3187 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3189 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3190 if (!host_name) {
3191 res = ERROR_OUTOFMEMORY;
3192 goto lend;
3195 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3196 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3197 heap_free(host_name);
3199 else
3200 HTTP_ProcessHeader(request, hostW, session->hostName,
3201 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3203 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3204 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3205 INTERNET_DEFAULT_HTTPS_PORT :
3206 INTERNET_DEFAULT_HTTP_PORT);
3208 if (hIC->proxy && hIC->proxy[0])
3209 HTTP_DealWithProxy( hIC, session, request );
3211 INTERNET_SendCallback(&session->hdr, dwContext,
3212 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3213 sizeof(HINTERNET));
3215 lend:
3216 TRACE("<-- %u (%p)\n", res, request);
3218 if(res != ERROR_SUCCESS) {
3219 WININET_Release( &request->hdr );
3220 *ret = NULL;
3221 return res;
3224 *ret = request->hdr.hInternet;
3225 return ERROR_SUCCESS;
3228 /***********************************************************************
3229 * HttpOpenRequestW (WININET.@)
3231 * Open a HTTP request handle
3233 * RETURNS
3234 * HINTERNET a HTTP request handle on success
3235 * NULL on failure
3238 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3239 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3240 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3241 DWORD dwFlags, DWORD_PTR dwContext)
3243 http_session_t *session;
3244 HINTERNET handle = NULL;
3245 DWORD res;
3247 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3248 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3249 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3250 dwFlags, dwContext);
3251 if(lpszAcceptTypes!=NULL)
3253 int i;
3254 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3255 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3258 session = (http_session_t*) get_handle_object( hHttpSession );
3259 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3261 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3262 goto lend;
3266 * My tests seem to show that the windows version does not
3267 * become asynchronous until after this point. And anyhow
3268 * if this call was asynchronous then how would you get the
3269 * necessary HINTERNET pointer returned by this function.
3272 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3273 lpszVersion, lpszReferrer, lpszAcceptTypes,
3274 dwFlags, dwContext, &handle);
3275 lend:
3276 if( session )
3277 WININET_Release( &session->hdr );
3278 TRACE("returning %p\n", handle);
3279 if(res != ERROR_SUCCESS)
3280 SetLastError(res);
3281 return handle;
3284 static const LPCWSTR header_lookup[] = {
3285 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3286 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3287 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3288 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3289 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3290 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3291 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3292 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3293 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3294 szDate, /* HTTP_QUERY_DATE = 9 */
3295 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3296 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3297 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3298 szURI, /* HTTP_QUERY_URI = 13 */
3299 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3300 NULL, /* HTTP_QUERY_COST = 15 */
3301 NULL, /* HTTP_QUERY_LINK = 16 */
3302 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3303 NULL, /* HTTP_QUERY_VERSION = 18 */
3304 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3305 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3306 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3307 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3308 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3309 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3310 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3311 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3312 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3313 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3314 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3315 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3316 NULL, /* HTTP_QUERY_FROM = 31 */
3317 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3318 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3319 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3320 szReferer, /* HTTP_QUERY_REFERER = 35 */
3321 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3322 szServer, /* HTTP_QUERY_SERVER = 37 */
3323 NULL, /* HTTP_TITLE = 38 */
3324 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3325 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3326 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3327 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3328 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3329 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3330 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3331 NULL, /* HTTP_QUERY_REFRESH = 46 */
3332 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3333 szAge, /* HTTP_QUERY_AGE = 48 */
3334 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3335 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3336 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3337 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3338 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3339 szETag, /* HTTP_QUERY_ETAG = 54 */
3340 hostW, /* HTTP_QUERY_HOST = 55 */
3341 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3342 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3343 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3344 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3345 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3346 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3347 szRange, /* HTTP_QUERY_RANGE = 62 */
3348 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3349 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3350 szVary, /* HTTP_QUERY_VARY = 65 */
3351 szVia, /* HTTP_QUERY_VIA = 66 */
3352 szWarning, /* HTTP_QUERY_WARNING = 67 */
3353 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3354 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3355 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3358 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3360 /***********************************************************************
3361 * HTTP_HttpQueryInfoW (internal)
3363 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3364 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3366 LPHTTPHEADERW lphttpHdr = NULL;
3367 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3368 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3369 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3370 INT index = -1;
3372 /* Find requested header structure */
3373 switch (level)
3375 case HTTP_QUERY_CUSTOM:
3376 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3377 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3378 break;
3379 case HTTP_QUERY_RAW_HEADERS_CRLF:
3381 LPWSTR headers;
3382 DWORD len = 0;
3383 DWORD res = ERROR_INVALID_PARAMETER;
3385 if (request_only)
3386 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3387 else
3388 headers = request->rawHeaders;
3390 if (headers)
3391 len = strlenW(headers) * sizeof(WCHAR);
3393 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3395 len += sizeof(WCHAR);
3396 res = ERROR_INSUFFICIENT_BUFFER;
3398 else if (lpBuffer)
3400 if (headers)
3401 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3402 else
3404 len = strlenW(szCrLf) * sizeof(WCHAR);
3405 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3407 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3408 res = ERROR_SUCCESS;
3410 *lpdwBufferLength = len;
3412 if (request_only) heap_free(headers);
3413 return res;
3415 case HTTP_QUERY_RAW_HEADERS:
3417 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3418 DWORD i, size = 0;
3419 LPWSTR pszString = lpBuffer;
3421 for (i = 0; ppszRawHeaderLines[i]; i++)
3422 size += strlenW(ppszRawHeaderLines[i]) + 1;
3424 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3426 HTTP_FreeTokens(ppszRawHeaderLines);
3427 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3428 return ERROR_INSUFFICIENT_BUFFER;
3430 if (pszString)
3432 for (i = 0; ppszRawHeaderLines[i]; i++)
3434 DWORD len = strlenW(ppszRawHeaderLines[i]);
3435 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3436 pszString += len+1;
3438 *pszString = '\0';
3439 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3441 *lpdwBufferLength = size * sizeof(WCHAR);
3442 HTTP_FreeTokens(ppszRawHeaderLines);
3444 return ERROR_SUCCESS;
3446 case HTTP_QUERY_STATUS_TEXT:
3447 if (request->statusText)
3449 DWORD len = strlenW(request->statusText);
3450 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3452 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3453 return ERROR_INSUFFICIENT_BUFFER;
3455 if (lpBuffer)
3457 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3458 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3460 *lpdwBufferLength = len * sizeof(WCHAR);
3461 return ERROR_SUCCESS;
3463 break;
3464 case HTTP_QUERY_VERSION:
3465 if (request->version)
3467 DWORD len = strlenW(request->version);
3468 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3470 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3471 return ERROR_INSUFFICIENT_BUFFER;
3473 if (lpBuffer)
3475 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3476 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3478 *lpdwBufferLength = len * sizeof(WCHAR);
3479 return ERROR_SUCCESS;
3481 break;
3482 case HTTP_QUERY_CONTENT_ENCODING:
3483 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3484 requested_index,request_only);
3485 break;
3486 case HTTP_QUERY_STATUS_CODE: {
3487 DWORD res = ERROR_SUCCESS;
3489 if(request_only)
3490 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3492 if(requested_index)
3493 break;
3495 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3496 if(*lpdwBufferLength >= sizeof(DWORD))
3497 *(DWORD*)lpBuffer = request->status_code;
3498 else
3499 res = ERROR_INSUFFICIENT_BUFFER;
3500 *lpdwBufferLength = sizeof(DWORD);
3501 }else {
3502 WCHAR buf[12];
3503 DWORD size;
3504 static const WCHAR formatW[] = {'%','u',0};
3506 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3508 if(size <= *lpdwBufferLength) {
3509 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3510 }else {
3511 size += sizeof(WCHAR);
3512 res = ERROR_INSUFFICIENT_BUFFER;
3515 *lpdwBufferLength = size;
3517 return res;
3519 default:
3520 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3522 if (level < LAST_TABLE_HEADER && header_lookup[level])
3523 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3524 requested_index,request_only);
3527 if (index >= 0)
3528 lphttpHdr = &request->custHeaders[index];
3530 /* Ensure header satisfies requested attributes */
3531 if (!lphttpHdr ||
3532 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3533 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3535 return ERROR_HTTP_HEADER_NOT_FOUND;
3538 if (lpdwIndex) (*lpdwIndex)++;
3540 /* coalesce value to requested type */
3541 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3543 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3544 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3546 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3548 time_t tmpTime;
3549 struct tm tmpTM;
3550 SYSTEMTIME *STHook;
3552 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3554 tmpTM = *gmtime(&tmpTime);
3555 STHook = (SYSTEMTIME *)lpBuffer;
3556 STHook->wDay = tmpTM.tm_mday;
3557 STHook->wHour = tmpTM.tm_hour;
3558 STHook->wMilliseconds = 0;
3559 STHook->wMinute = tmpTM.tm_min;
3560 STHook->wDayOfWeek = tmpTM.tm_wday;
3561 STHook->wMonth = tmpTM.tm_mon + 1;
3562 STHook->wSecond = tmpTM.tm_sec;
3563 STHook->wYear = tmpTM.tm_year;
3565 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3566 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3567 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3569 else if (lphttpHdr->lpszValue)
3571 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3573 if (len > *lpdwBufferLength)
3575 *lpdwBufferLength = len;
3576 return ERROR_INSUFFICIENT_BUFFER;
3578 if (lpBuffer)
3580 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3581 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3583 *lpdwBufferLength = len - sizeof(WCHAR);
3585 return ERROR_SUCCESS;
3588 /***********************************************************************
3589 * HttpQueryInfoW (WININET.@)
3591 * Queries for information about an HTTP request
3593 * RETURNS
3594 * TRUE on success
3595 * FALSE on failure
3598 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3599 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3601 http_request_t *request;
3602 DWORD res;
3604 if (TRACE_ON(wininet)) {
3605 #define FE(x) { x, #x }
3606 static const wininet_flag_info query_flags[] = {
3607 FE(HTTP_QUERY_MIME_VERSION),
3608 FE(HTTP_QUERY_CONTENT_TYPE),
3609 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3610 FE(HTTP_QUERY_CONTENT_ID),
3611 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3612 FE(HTTP_QUERY_CONTENT_LENGTH),
3613 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3614 FE(HTTP_QUERY_ALLOW),
3615 FE(HTTP_QUERY_PUBLIC),
3616 FE(HTTP_QUERY_DATE),
3617 FE(HTTP_QUERY_EXPIRES),
3618 FE(HTTP_QUERY_LAST_MODIFIED),
3619 FE(HTTP_QUERY_MESSAGE_ID),
3620 FE(HTTP_QUERY_URI),
3621 FE(HTTP_QUERY_DERIVED_FROM),
3622 FE(HTTP_QUERY_COST),
3623 FE(HTTP_QUERY_LINK),
3624 FE(HTTP_QUERY_PRAGMA),
3625 FE(HTTP_QUERY_VERSION),
3626 FE(HTTP_QUERY_STATUS_CODE),
3627 FE(HTTP_QUERY_STATUS_TEXT),
3628 FE(HTTP_QUERY_RAW_HEADERS),
3629 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3630 FE(HTTP_QUERY_CONNECTION),
3631 FE(HTTP_QUERY_ACCEPT),
3632 FE(HTTP_QUERY_ACCEPT_CHARSET),
3633 FE(HTTP_QUERY_ACCEPT_ENCODING),
3634 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3635 FE(HTTP_QUERY_AUTHORIZATION),
3636 FE(HTTP_QUERY_CONTENT_ENCODING),
3637 FE(HTTP_QUERY_FORWARDED),
3638 FE(HTTP_QUERY_FROM),
3639 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3640 FE(HTTP_QUERY_LOCATION),
3641 FE(HTTP_QUERY_ORIG_URI),
3642 FE(HTTP_QUERY_REFERER),
3643 FE(HTTP_QUERY_RETRY_AFTER),
3644 FE(HTTP_QUERY_SERVER),
3645 FE(HTTP_QUERY_TITLE),
3646 FE(HTTP_QUERY_USER_AGENT),
3647 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3648 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3649 FE(HTTP_QUERY_ACCEPT_RANGES),
3650 FE(HTTP_QUERY_SET_COOKIE),
3651 FE(HTTP_QUERY_COOKIE),
3652 FE(HTTP_QUERY_REQUEST_METHOD),
3653 FE(HTTP_QUERY_REFRESH),
3654 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3655 FE(HTTP_QUERY_AGE),
3656 FE(HTTP_QUERY_CACHE_CONTROL),
3657 FE(HTTP_QUERY_CONTENT_BASE),
3658 FE(HTTP_QUERY_CONTENT_LOCATION),
3659 FE(HTTP_QUERY_CONTENT_MD5),
3660 FE(HTTP_QUERY_CONTENT_RANGE),
3661 FE(HTTP_QUERY_ETAG),
3662 FE(HTTP_QUERY_HOST),
3663 FE(HTTP_QUERY_IF_MATCH),
3664 FE(HTTP_QUERY_IF_NONE_MATCH),
3665 FE(HTTP_QUERY_IF_RANGE),
3666 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3667 FE(HTTP_QUERY_MAX_FORWARDS),
3668 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3669 FE(HTTP_QUERY_RANGE),
3670 FE(HTTP_QUERY_TRANSFER_ENCODING),
3671 FE(HTTP_QUERY_UPGRADE),
3672 FE(HTTP_QUERY_VARY),
3673 FE(HTTP_QUERY_VIA),
3674 FE(HTTP_QUERY_WARNING),
3675 FE(HTTP_QUERY_CUSTOM)
3677 static const wininet_flag_info modifier_flags[] = {
3678 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3679 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3680 FE(HTTP_QUERY_FLAG_NUMBER),
3681 FE(HTTP_QUERY_FLAG_COALESCE)
3683 #undef FE
3684 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3685 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3686 DWORD i;
3688 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3689 TRACE(" Attribute:");
3690 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3691 if (query_flags[i].val == info) {
3692 TRACE(" %s", query_flags[i].name);
3693 break;
3696 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3697 TRACE(" Unknown (%08x)", info);
3700 TRACE(" Modifier:");
3701 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3702 if (modifier_flags[i].val & info_mod) {
3703 TRACE(" %s", modifier_flags[i].name);
3704 info_mod &= ~ modifier_flags[i].val;
3708 if (info_mod) {
3709 TRACE(" Unknown (%08x)", info_mod);
3711 TRACE("\n");
3714 request = (http_request_t*) get_handle_object( hHttpRequest );
3715 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3717 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3718 goto lend;
3721 if (lpBuffer == NULL)
3722 *lpdwBufferLength = 0;
3723 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3724 lpBuffer, lpdwBufferLength, lpdwIndex);
3726 lend:
3727 if( request )
3728 WININET_Release( &request->hdr );
3730 TRACE("%u <--\n", res);
3731 if(res != ERROR_SUCCESS)
3732 SetLastError(res);
3733 return res == ERROR_SUCCESS;
3736 /***********************************************************************
3737 * HttpQueryInfoA (WININET.@)
3739 * Queries for information about an HTTP request
3741 * RETURNS
3742 * TRUE on success
3743 * FALSE on failure
3746 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3747 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3749 BOOL result;
3750 DWORD len;
3751 WCHAR* bufferW;
3753 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3754 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3756 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3757 lpdwBufferLength, lpdwIndex );
3760 if (lpBuffer)
3762 DWORD alloclen;
3763 len = (*lpdwBufferLength)*sizeof(WCHAR);
3764 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3766 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3767 if (alloclen < len)
3768 alloclen = len;
3770 else
3771 alloclen = len;
3772 bufferW = heap_alloc(alloclen);
3773 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3774 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3775 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3776 } else
3778 bufferW = NULL;
3779 len = 0;
3782 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3783 &len, lpdwIndex );
3784 if( result )
3786 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3787 lpBuffer, *lpdwBufferLength, NULL, NULL );
3788 *lpdwBufferLength = len - 1;
3790 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3792 else
3793 /* since the strings being returned from HttpQueryInfoW should be
3794 * only ASCII characters, it is reasonable to assume that all of
3795 * the Unicode characters can be reduced to a single byte */
3796 *lpdwBufferLength = len / sizeof(WCHAR);
3798 heap_free( bufferW );
3799 return result;
3802 /***********************************************************************
3803 * HTTP_GetRedirectURL (internal)
3805 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3807 static WCHAR szHttp[] = {'h','t','t','p',0};
3808 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3809 http_session_t *session = request->session;
3810 URL_COMPONENTSW urlComponents;
3811 DWORD url_length = 0;
3812 LPWSTR orig_url;
3813 LPWSTR combined_url;
3815 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3816 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3817 urlComponents.dwSchemeLength = 0;
3818 urlComponents.lpszHostName = session->hostName;
3819 urlComponents.dwHostNameLength = 0;
3820 urlComponents.nPort = session->hostPort;
3821 urlComponents.lpszUserName = session->userName;
3822 urlComponents.dwUserNameLength = 0;
3823 urlComponents.lpszPassword = NULL;
3824 urlComponents.dwPasswordLength = 0;
3825 urlComponents.lpszUrlPath = request->path;
3826 urlComponents.dwUrlPathLength = 0;
3827 urlComponents.lpszExtraInfo = NULL;
3828 urlComponents.dwExtraInfoLength = 0;
3830 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3831 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3832 return NULL;
3834 orig_url = heap_alloc(url_length);
3836 /* convert from bytes to characters */
3837 url_length = url_length / sizeof(WCHAR) - 1;
3838 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3840 heap_free(orig_url);
3841 return NULL;
3844 url_length = 0;
3845 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3846 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3848 heap_free(orig_url);
3849 return NULL;
3851 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3853 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3855 heap_free(orig_url);
3856 heap_free(combined_url);
3857 return NULL;
3859 heap_free(orig_url);
3860 return combined_url;
3864 /***********************************************************************
3865 * HTTP_HandleRedirect (internal)
3867 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3869 http_session_t *session = request->session;
3870 appinfo_t *hIC = session->appInfo;
3871 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3872 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3873 int index;
3875 if(lpszUrl[0]=='/')
3877 /* if it's an absolute path, keep the same session info */
3878 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3880 else
3882 URL_COMPONENTSW urlComponents;
3883 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3884 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3885 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3886 BOOL custom_port = FALSE;
3888 static WCHAR httpW[] = {'h','t','t','p',0};
3889 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3891 userName[0] = 0;
3892 hostName[0] = 0;
3893 protocol[0] = 0;
3895 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3896 urlComponents.lpszScheme = protocol;
3897 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3898 urlComponents.lpszHostName = hostName;
3899 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3900 urlComponents.lpszUserName = userName;
3901 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3902 urlComponents.lpszPassword = NULL;
3903 urlComponents.dwPasswordLength = 0;
3904 urlComponents.lpszUrlPath = path;
3905 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3906 urlComponents.lpszExtraInfo = NULL;
3907 urlComponents.dwExtraInfoLength = 0;
3908 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3909 return INTERNET_GetLastError();
3911 if(!strcmpiW(protocol, httpW)) {
3912 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3913 TRACE("redirect from secure page to non-secure page\n");
3914 /* FIXME: warn about from secure redirect to non-secure page */
3915 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3918 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3919 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3920 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3921 custom_port = TRUE;
3922 }else if(!strcmpiW(protocol, httpsW)) {
3923 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3924 TRACE("redirect from non-secure page to secure page\n");
3925 /* FIXME: notify about redirect to secure page */
3926 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3929 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3930 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3931 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3932 custom_port = TRUE;
3935 heap_free(session->hostName);
3937 if(custom_port) {
3938 int len;
3939 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3940 len = lstrlenW(hostName);
3941 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3942 session->hostName = heap_alloc(len*sizeof(WCHAR));
3943 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3945 else
3946 session->hostName = heap_strdupW(hostName);
3948 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3950 heap_free(session->userName);
3951 session->userName = NULL;
3952 if (userName[0])
3953 session->userName = heap_strdupW(userName);
3955 reset_data_stream(request);
3957 if(!using_proxy && (strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort)) {
3958 server_t *new_server;
3960 new_server = get_server(hostName, urlComponents.nPort, TRUE);
3961 server_release(request->server);
3962 request->server = new_server;
3965 heap_free(request->path);
3966 request->path=NULL;
3967 if (*path)
3969 DWORD needed = 0;
3970 HRESULT rc;
3972 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3973 if (rc != E_POINTER)
3974 needed = strlenW(path)+1;
3975 request->path = heap_alloc(needed*sizeof(WCHAR));
3976 rc = UrlEscapeW(path, request->path, &needed,
3977 URL_ESCAPE_SPACES_ONLY);
3978 if (rc != S_OK)
3980 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3981 strcpyW(request->path,path);
3985 /* Remove custom content-type/length headers on redirects. */
3986 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3987 if (0 <= index)
3988 HTTP_DeleteCustomHeader(request, index);
3989 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3990 if (0 <= index)
3991 HTTP_DeleteCustomHeader(request, index);
3993 return ERROR_SUCCESS;
3996 /***********************************************************************
3997 * HTTP_build_req (internal)
3999 * concatenate all the strings in the request together
4001 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
4003 LPCWSTR *t;
4004 LPWSTR str;
4006 for( t = list; *t ; t++ )
4007 len += strlenW( *t );
4008 len++;
4010 str = heap_alloc(len*sizeof(WCHAR));
4011 *str = 0;
4013 for( t = list; *t ; t++ )
4014 strcatW( str, *t );
4016 return str;
4019 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
4021 LPWSTR lpszPath;
4022 LPWSTR requestString;
4023 INT len;
4024 INT cnt;
4025 INT responseLen;
4026 char *ascii_req;
4027 DWORD res;
4028 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
4029 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
4030 http_session_t *session = request->session;
4032 TRACE("\n");
4034 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
4035 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
4036 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
4037 heap_free( lpszPath );
4039 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4040 NULL, 0, NULL, NULL );
4041 len--; /* the nul terminator isn't needed */
4042 ascii_req = heap_alloc(len);
4043 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4044 heap_free( requestString );
4046 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4048 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4049 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4050 heap_free( ascii_req );
4051 if (res != ERROR_SUCCESS)
4052 return res;
4054 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4055 if (!responseLen)
4056 return ERROR_HTTP_INVALID_HEADER;
4058 return ERROR_SUCCESS;
4061 static void HTTP_InsertCookies(http_request_t *request)
4063 DWORD cookie_size, size, cnt = 0;
4064 HTTPHEADERW *host;
4065 WCHAR *cookies;
4067 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4069 host = HTTP_GetHeader(request, hostW);
4070 if(!host)
4071 return;
4073 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4074 return;
4076 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4077 if(!(cookies = heap_alloc(size)))
4078 return;
4080 cnt += sprintfW(cookies, cookieW);
4081 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4082 strcatW(cookies, szCrLf);
4084 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4086 heap_free(cookies);
4089 static WORD HTTP_ParseWkday(LPCWSTR day)
4091 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4092 { 'm','o','n',0 },
4093 { 't','u','e',0 },
4094 { 'w','e','d',0 },
4095 { 't','h','u',0 },
4096 { 'f','r','i',0 },
4097 { 's','a','t',0 }};
4098 int i;
4099 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4100 if (!strcmpiW(day, days[i]))
4101 return i;
4103 /* Invalid */
4104 return 7;
4107 static WORD HTTP_ParseMonth(LPCWSTR month)
4109 static const WCHAR jan[] = { 'j','a','n',0 };
4110 static const WCHAR feb[] = { 'f','e','b',0 };
4111 static const WCHAR mar[] = { 'm','a','r',0 };
4112 static const WCHAR apr[] = { 'a','p','r',0 };
4113 static const WCHAR may[] = { 'm','a','y',0 };
4114 static const WCHAR jun[] = { 'j','u','n',0 };
4115 static const WCHAR jul[] = { 'j','u','l',0 };
4116 static const WCHAR aug[] = { 'a','u','g',0 };
4117 static const WCHAR sep[] = { 's','e','p',0 };
4118 static const WCHAR oct[] = { 'o','c','t',0 };
4119 static const WCHAR nov[] = { 'n','o','v',0 };
4120 static const WCHAR dec[] = { 'd','e','c',0 };
4122 if (!strcmpiW(month, jan)) return 1;
4123 if (!strcmpiW(month, feb)) return 2;
4124 if (!strcmpiW(month, mar)) return 3;
4125 if (!strcmpiW(month, apr)) return 4;
4126 if (!strcmpiW(month, may)) return 5;
4127 if (!strcmpiW(month, jun)) return 6;
4128 if (!strcmpiW(month, jul)) return 7;
4129 if (!strcmpiW(month, aug)) return 8;
4130 if (!strcmpiW(month, sep)) return 9;
4131 if (!strcmpiW(month, oct)) return 10;
4132 if (!strcmpiW(month, nov)) return 11;
4133 if (!strcmpiW(month, dec)) return 12;
4134 /* Invalid */
4135 return 0;
4138 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4139 * optionally preceded by whitespace.
4140 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4141 * st, and sets *str to the first character after the time format.
4143 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4145 LPCWSTR ptr = *str;
4146 WCHAR *nextPtr;
4147 unsigned long num;
4149 while (isspaceW(*ptr))
4150 ptr++;
4152 num = strtoulW(ptr, &nextPtr, 10);
4153 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4155 ERR("unexpected time format %s\n", debugstr_w(ptr));
4156 return FALSE;
4158 if (num > 23)
4160 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4161 return FALSE;
4163 ptr = nextPtr + 1;
4164 st->wHour = (WORD)num;
4165 num = strtoulW(ptr, &nextPtr, 10);
4166 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4168 ERR("unexpected time format %s\n", debugstr_w(ptr));
4169 return FALSE;
4171 if (num > 59)
4173 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4174 return FALSE;
4176 ptr = nextPtr + 1;
4177 st->wMinute = (WORD)num;
4178 num = strtoulW(ptr, &nextPtr, 10);
4179 if (!nextPtr || nextPtr <= ptr)
4181 ERR("unexpected time format %s\n", debugstr_w(ptr));
4182 return FALSE;
4184 if (num > 59)
4186 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4187 return FALSE;
4189 ptr = nextPtr + 1;
4190 *str = ptr;
4191 st->wSecond = (WORD)num;
4192 return TRUE;
4195 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4197 static const WCHAR gmt[]= { 'G','M','T',0 };
4198 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4199 LPCWSTR ptr;
4200 SYSTEMTIME st = { 0 };
4201 unsigned long num;
4203 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4204 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4205 *dayPtr = *ptr;
4206 *dayPtr = 0;
4207 st.wDayOfWeek = HTTP_ParseWkday(day);
4208 if (st.wDayOfWeek >= 7)
4210 ERR("unexpected weekday %s\n", debugstr_w(day));
4211 return FALSE;
4214 while (isspaceW(*ptr))
4215 ptr++;
4217 for (monthPtr = month; !isspace(*ptr) &&
4218 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4219 monthPtr++, ptr++)
4220 *monthPtr = *ptr;
4221 *monthPtr = 0;
4222 st.wMonth = HTTP_ParseMonth(month);
4223 if (!st.wMonth || st.wMonth > 12)
4225 ERR("unexpected month %s\n", debugstr_w(month));
4226 return FALSE;
4229 while (isspaceW(*ptr))
4230 ptr++;
4232 num = strtoulW(ptr, &nextPtr, 10);
4233 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4235 ERR("unexpected day %s\n", debugstr_w(ptr));
4236 return FALSE;
4238 ptr = nextPtr;
4239 st.wDay = (WORD)num;
4241 while (isspaceW(*ptr))
4242 ptr++;
4244 if (!HTTP_ParseTime(&st, &ptr))
4245 return FALSE;
4247 while (isspaceW(*ptr))
4248 ptr++;
4250 num = strtoulW(ptr, &nextPtr, 10);
4251 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4253 ERR("unexpected year %s\n", debugstr_w(ptr));
4254 return FALSE;
4256 ptr = nextPtr;
4257 st.wYear = (WORD)num;
4259 while (isspaceW(*ptr))
4260 ptr++;
4262 /* asctime() doesn't report a timezone, but some web servers do, so accept
4263 * with or without GMT.
4265 if (*ptr && strcmpW(ptr, gmt))
4267 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4268 return FALSE;
4270 return SystemTimeToFileTime(&st, ft);
4273 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4275 static const WCHAR gmt[]= { 'G','M','T',0 };
4276 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4277 LPCWSTR ptr;
4278 unsigned long num;
4279 SYSTEMTIME st = { 0 };
4281 ptr = strchrW(value, ',');
4282 if (!ptr)
4283 return FALSE;
4284 if (ptr - value != 3)
4286 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4287 return FALSE;
4289 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4290 day[3] = 0;
4291 st.wDayOfWeek = HTTP_ParseWkday(day);
4292 if (st.wDayOfWeek > 6)
4294 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4295 return FALSE;
4297 ptr++;
4299 while (isspaceW(*ptr))
4300 ptr++;
4302 num = strtoulW(ptr, &nextPtr, 10);
4303 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4305 WARN("unexpected day %s\n", debugstr_w(value));
4306 return FALSE;
4308 ptr = nextPtr;
4309 st.wDay = (WORD)num;
4311 while (isspaceW(*ptr))
4312 ptr++;
4314 for (monthPtr = month; !isspace(*ptr) &&
4315 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4316 monthPtr++, ptr++)
4317 *monthPtr = *ptr;
4318 *monthPtr = 0;
4319 st.wMonth = HTTP_ParseMonth(month);
4320 if (!st.wMonth || st.wMonth > 12)
4322 WARN("unexpected month %s\n", debugstr_w(month));
4323 return FALSE;
4326 while (isspaceW(*ptr))
4327 ptr++;
4329 num = strtoulW(ptr, &nextPtr, 10);
4330 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4332 ERR("unexpected year %s\n", debugstr_w(value));
4333 return FALSE;
4335 ptr = nextPtr;
4336 st.wYear = (WORD)num;
4338 if (!HTTP_ParseTime(&st, &ptr))
4339 return FALSE;
4341 while (isspaceW(*ptr))
4342 ptr++;
4344 if (strcmpW(ptr, gmt))
4346 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4347 return FALSE;
4349 return SystemTimeToFileTime(&st, ft);
4352 static WORD HTTP_ParseWeekday(LPCWSTR day)
4354 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4355 { 'm','o','n','d','a','y',0 },
4356 { 't','u','e','s','d','a','y',0 },
4357 { 'w','e','d','n','e','s','d','a','y',0 },
4358 { 't','h','u','r','s','d','a','y',0 },
4359 { 'f','r','i','d','a','y',0 },
4360 { 's','a','t','u','r','d','a','y',0 }};
4361 int i;
4362 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4363 if (!strcmpiW(day, days[i]))
4364 return i;
4366 /* Invalid */
4367 return 7;
4370 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4372 static const WCHAR gmt[]= { 'G','M','T',0 };
4373 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4374 LPCWSTR ptr;
4375 unsigned long num;
4376 SYSTEMTIME st = { 0 };
4378 ptr = strchrW(value, ',');
4379 if (!ptr)
4380 return FALSE;
4381 if (ptr - value == 3)
4383 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4384 day[3] = 0;
4385 st.wDayOfWeek = HTTP_ParseWkday(day);
4386 if (st.wDayOfWeek > 6)
4388 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4389 return FALSE;
4392 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4394 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4395 day[ptr - value + 1] = 0;
4396 st.wDayOfWeek = HTTP_ParseWeekday(day);
4397 if (st.wDayOfWeek > 6)
4399 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4400 return FALSE;
4403 else
4405 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4406 return FALSE;
4408 ptr++;
4410 while (isspaceW(*ptr))
4411 ptr++;
4413 num = strtoulW(ptr, &nextPtr, 10);
4414 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4416 ERR("unexpected day %s\n", debugstr_w(value));
4417 return FALSE;
4419 ptr = nextPtr;
4420 st.wDay = (WORD)num;
4422 if (*ptr != '-')
4424 ERR("unexpected month format %s\n", debugstr_w(ptr));
4425 return FALSE;
4427 ptr++;
4429 for (monthPtr = month; *ptr != '-' &&
4430 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4431 monthPtr++, ptr++)
4432 *monthPtr = *ptr;
4433 *monthPtr = 0;
4434 st.wMonth = HTTP_ParseMonth(month);
4435 if (!st.wMonth || st.wMonth > 12)
4437 ERR("unexpected month %s\n", debugstr_w(month));
4438 return FALSE;
4441 if (*ptr != '-')
4443 ERR("unexpected year format %s\n", debugstr_w(ptr));
4444 return FALSE;
4446 ptr++;
4448 num = strtoulW(ptr, &nextPtr, 10);
4449 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4451 ERR("unexpected year %s\n", debugstr_w(value));
4452 return FALSE;
4454 ptr = nextPtr;
4455 st.wYear = (WORD)num;
4457 if (!HTTP_ParseTime(&st, &ptr))
4458 return FALSE;
4460 while (isspaceW(*ptr))
4461 ptr++;
4463 if (strcmpW(ptr, gmt))
4465 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4466 return FALSE;
4468 return SystemTimeToFileTime(&st, ft);
4471 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4473 static const WCHAR zero[] = { '0',0 };
4474 BOOL ret;
4476 if (!strcmpW(value, zero))
4478 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4479 ret = TRUE;
4481 else if (strchrW(value, ','))
4483 ret = HTTP_ParseRfc1123Date(value, ft);
4484 if (!ret)
4486 ret = HTTP_ParseRfc850Date(value, ft);
4487 if (!ret)
4488 ERR("unexpected date format %s\n", debugstr_w(value));
4491 else
4493 ret = HTTP_ParseDateAsAsctime(value, ft);
4494 if (!ret)
4495 ERR("unexpected date format %s\n", debugstr_w(value));
4497 return ret;
4500 static void HTTP_ProcessExpires(http_request_t *request)
4502 BOOL expirationFound = FALSE;
4503 int headerIndex;
4505 /* Look for a Cache-Control header with a max-age directive, as it takes
4506 * precedence over the Expires header.
4508 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4509 if (headerIndex != -1)
4511 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4512 LPWSTR ptr;
4514 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4516 LPWSTR comma = strchrW(ptr, ','), end, equal;
4518 if (comma)
4519 end = comma;
4520 else
4521 end = ptr + strlenW(ptr);
4522 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4524 if (*equal == '=')
4526 static const WCHAR max_age[] = {
4527 'm','a','x','-','a','g','e',0 };
4529 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4531 LPWSTR nextPtr;
4532 unsigned long age;
4534 age = strtoulW(equal + 1, &nextPtr, 10);
4535 if (nextPtr > equal + 1)
4537 LARGE_INTEGER ft;
4539 NtQuerySystemTime( &ft );
4540 /* Age is in seconds, FILETIME resolution is in
4541 * 100 nanosecond intervals.
4543 ft.QuadPart += age * (ULONGLONG)1000000;
4544 request->expires.dwLowDateTime = ft.u.LowPart;
4545 request->expires.dwHighDateTime = ft.u.HighPart;
4546 expirationFound = TRUE;
4550 if (comma)
4552 ptr = comma + 1;
4553 while (isspaceW(*ptr))
4554 ptr++;
4556 else
4557 ptr = NULL;
4560 if (!expirationFound)
4562 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4563 if (headerIndex != -1)
4565 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4566 FILETIME ft;
4568 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4570 expirationFound = TRUE;
4571 request->expires = ft;
4575 if (!expirationFound)
4577 LARGE_INTEGER t;
4579 /* With no known age, default to 10 minutes until expiration. */
4580 NtQuerySystemTime( &t );
4581 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4582 request->expires.dwLowDateTime = t.u.LowPart;
4583 request->expires.dwHighDateTime = t.u.HighPart;
4587 static void HTTP_ProcessLastModified(http_request_t *request)
4589 int headerIndex;
4591 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4592 if (headerIndex != -1)
4594 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4595 FILETIME ft;
4597 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4598 request->last_modified = ft;
4602 static void http_process_keep_alive(http_request_t *req)
4604 int index;
4606 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4607 if(index != -1)
4608 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4609 else
4610 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4613 static void HTTP_CacheRequest(http_request_t *request)
4615 WCHAR url[INTERNET_MAX_URL_LENGTH];
4616 WCHAR cacheFileName[MAX_PATH+1];
4617 BOOL b;
4619 b = HTTP_GetRequestURL(request, url);
4620 if(!b) {
4621 WARN("Could not get URL\n");
4622 return;
4625 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4626 if(b) {
4627 heap_free(request->cacheFile);
4628 CloseHandle(request->hCacheFile);
4630 request->cacheFile = heap_strdupW(cacheFileName);
4631 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4632 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4633 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4634 WARN("Could not create file: %u\n", GetLastError());
4635 request->hCacheFile = NULL;
4637 }else {
4638 WARN("Could not create cache entry: %08x\n", GetLastError());
4642 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4644 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4645 netconn_t *netconn = NULL;
4646 DWORD res;
4648 assert(!request->netconn);
4649 reset_data_stream(request);
4651 res = HTTP_ResolveName(request);
4652 if(res != ERROR_SUCCESS)
4653 return res;
4655 EnterCriticalSection(&connection_pool_cs);
4657 while(!list_empty(&request->server->conn_pool)) {
4658 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4659 list_remove(&netconn->pool_entry);
4661 if(NETCON_is_alive(netconn))
4662 break;
4664 TRACE("connection %p closed during idle\n", netconn);
4665 free_netconn(netconn);
4666 netconn = NULL;
4669 LeaveCriticalSection(&connection_pool_cs);
4671 if(netconn) {
4672 TRACE("<-- reusing %p netconn\n", netconn);
4673 request->netconn = netconn;
4674 *reusing = TRUE;
4675 return ERROR_SUCCESS;
4678 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4679 INTERNET_STATUS_CONNECTING_TO_SERVER,
4680 request->server->addr_str,
4681 strlen(request->server->addr_str)+1);
4683 res = create_netconn(is_https, request->server, request->security_flags,
4684 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4685 request->connect_timeout, &netconn);
4686 if(res != ERROR_SUCCESS) {
4687 ERR("create_netconn failed: %u\n", res);
4688 return res;
4691 request->netconn = netconn;
4693 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4694 INTERNET_STATUS_CONNECTED_TO_SERVER,
4695 request->server->addr_str, strlen(request->server->addr_str)+1);
4697 if(is_https) {
4698 /* Note: we differ from Microsoft's WinINet here. they seem to have
4699 * a bug that causes no status callbacks to be sent when starting
4700 * a tunnel to a proxy server using the CONNECT verb. i believe our
4701 * behaviour to be more correct and to not cause any incompatibilities
4702 * because using a secure connection through a proxy server is a rare
4703 * case that would be hard for anyone to depend on */
4704 if(request->session->appInfo->proxy)
4705 res = HTTP_SecureProxyConnect(request);
4706 if(res == ERROR_SUCCESS)
4707 res = NETCON_secure_connect(request->netconn);
4710 if(res != ERROR_SUCCESS) {
4711 http_release_netconn(request, FALSE);
4712 return res;
4715 *reusing = FALSE;
4716 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4717 return ERROR_SUCCESS;
4720 /***********************************************************************
4721 * HTTP_HttpSendRequestW (internal)
4723 * Sends the specified request to the HTTP server
4725 * RETURNS
4726 * ERROR_SUCCESS on success
4727 * win32 error code on failure
4730 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4731 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4732 DWORD dwContentLength, BOOL bEndRequest)
4734 INT cnt;
4735 BOOL redirected = FALSE;
4736 LPWSTR requestString = NULL;
4737 INT responseLen;
4738 BOOL loop_next;
4739 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4740 static const WCHAR szContentLength[] =
4741 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4742 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4743 DWORD res;
4745 TRACE("--> %p\n", request);
4747 assert(request->hdr.htype == WH_HHTTPREQ);
4749 /* if the verb is NULL default to GET */
4750 if (!request->verb)
4751 request->verb = heap_strdupW(szGET);
4753 if (dwContentLength || strcmpW(request->verb, szGET))
4755 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4756 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4757 request->bytesToWrite = dwContentLength;
4759 if (request->session->appInfo->agent)
4761 WCHAR *agent_header;
4762 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4763 int len;
4765 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4766 agent_header = heap_alloc(len * sizeof(WCHAR));
4767 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4769 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4770 heap_free(agent_header);
4772 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4774 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4775 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4777 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4779 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4780 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4781 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4784 /* add the headers the caller supplied */
4785 if( lpszHeaders && dwHeaderLength )
4786 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4790 DWORD len;
4791 BOOL reusing_connection;
4792 char *ascii_req;
4794 loop_next = FALSE;
4795 reusing_connection = request->netconn != NULL;
4797 if(redirected) {
4798 request->contentLength = ~0u;
4799 request->bytesToWrite = 0;
4802 if (TRACE_ON(wininet))
4804 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4805 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4808 HTTP_FixURL(request);
4809 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4811 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4813 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4814 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4816 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4817 HTTP_InsertCookies(request);
4819 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4821 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4822 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4823 heap_free(url);
4825 else
4826 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4829 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4831 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4832 break;
4834 /* send the request as ASCII, tack on the optional data */
4835 if (!lpOptional || redirected)
4836 dwOptionalLength = 0;
4837 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4838 NULL, 0, NULL, NULL );
4839 ascii_req = heap_alloc(len + dwOptionalLength);
4840 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4841 ascii_req, len, NULL, NULL );
4842 if( lpOptional )
4843 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4844 len = (len + dwOptionalLength - 1);
4845 ascii_req[len] = 0;
4846 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4848 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4849 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4851 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4852 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4853 heap_free( ascii_req );
4854 if(res != ERROR_SUCCESS) {
4855 TRACE("send failed: %u\n", res);
4856 if(!reusing_connection)
4857 break;
4858 http_release_netconn(request, FALSE);
4859 loop_next = TRUE;
4860 continue;
4863 request->bytesWritten = dwOptionalLength;
4865 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4866 INTERNET_STATUS_REQUEST_SENT,
4867 &len, sizeof(DWORD));
4869 if (bEndRequest)
4871 DWORD dwBufferSize;
4873 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4874 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4876 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4877 /* FIXME: We should know that connection is closed before sending
4878 * headers. Otherwise wrong callbacks are executed */
4879 if(!responseLen && reusing_connection) {
4880 TRACE("Connection closed by server, reconnecting\n");
4881 http_release_netconn(request, FALSE);
4882 loop_next = TRUE;
4883 continue;
4886 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4887 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4888 sizeof(DWORD));
4890 http_process_keep_alive(request);
4891 HTTP_ProcessCookies(request);
4892 HTTP_ProcessExpires(request);
4893 HTTP_ProcessLastModified(request);
4895 res = set_content_length(request);
4896 if(res != ERROR_SUCCESS)
4897 goto lend;
4898 if(!request->contentLength)
4899 http_release_netconn(request, TRUE);
4901 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4903 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4904 dwBufferSize=sizeof(szNewLocation);
4905 switch(request->status_code) {
4906 case HTTP_STATUS_REDIRECT:
4907 case HTTP_STATUS_MOVED:
4908 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4909 case HTTP_STATUS_REDIRECT_METHOD:
4910 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4911 break;
4913 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4914 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4916 heap_free(request->verb);
4917 request->verb = heap_strdupW(szGET);
4919 http_release_netconn(request, drain_content(request, FALSE));
4920 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4922 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4923 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4924 res = HTTP_HandleRedirect(request, new_url);
4925 if (res == ERROR_SUCCESS)
4927 heap_free(requestString);
4928 loop_next = TRUE;
4930 heap_free( new_url );
4932 redirected = TRUE;
4935 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4937 WCHAR szAuthValue[2048];
4938 dwBufferSize=2048;
4939 if (request->status_code == HTTP_STATUS_DENIED)
4941 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4942 DWORD dwIndex = 0;
4943 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4945 if (HTTP_DoAuthorization(request, szAuthValue,
4946 &request->authInfo,
4947 request->session->userName,
4948 request->session->password,
4949 Host->lpszValue))
4951 heap_free(requestString);
4952 if(!drain_content(request, TRUE)) {
4953 FIXME("Could not drain content\n");
4954 http_release_netconn(request, FALSE);
4956 loop_next = TRUE;
4957 break;
4961 if(!loop_next) {
4962 TRACE("Cleaning wrong authorization data\n");
4963 destroy_authinfo(request->authInfo);
4964 request->authInfo = NULL;
4967 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4969 DWORD dwIndex = 0;
4970 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4972 if (HTTP_DoAuthorization(request, szAuthValue,
4973 &request->proxyAuthInfo,
4974 request->session->appInfo->proxyUsername,
4975 request->session->appInfo->proxyPassword,
4976 NULL))
4978 if(!drain_content(request, TRUE)) {
4979 FIXME("Could not drain content\n");
4980 http_release_netconn(request, FALSE);
4982 loop_next = TRUE;
4983 break;
4987 if(!loop_next) {
4988 TRACE("Cleaning wrong proxy authorization data\n");
4989 destroy_authinfo(request->proxyAuthInfo);
4990 request->proxyAuthInfo = NULL;
4995 else
4996 res = ERROR_SUCCESS;
4998 while (loop_next);
5000 if(res == ERROR_SUCCESS)
5001 HTTP_CacheRequest(request);
5003 lend:
5004 heap_free(requestString);
5006 /* TODO: send notification for P3P header */
5008 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5010 if (res == ERROR_SUCCESS) {
5011 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
5012 HTTP_ReceiveRequestData(request, TRUE);
5013 else
5014 send_request_complete(request,
5015 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
5016 }else {
5017 send_request_complete(request, 0, res);
5021 TRACE("<--\n");
5022 return res;
5025 /***********************************************************************
5027 * Helper functions for the HttpSendRequest(Ex) functions
5030 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
5032 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
5033 http_request_t *request = (http_request_t*) workRequest->hdr;
5035 TRACE("%p\n", request);
5037 HTTP_HttpSendRequestW(request, req->lpszHeader,
5038 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5039 req->dwContentLength, req->bEndRequest);
5041 heap_free(req->lpszHeader);
5045 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5047 DWORD dwBufferSize;
5048 INT responseLen;
5049 DWORD res = ERROR_SUCCESS;
5051 if(!request->netconn) {
5052 WARN("Not connected\n");
5053 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5054 return ERROR_INTERNET_OPERATION_CANCELLED;
5057 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5058 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5060 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5061 if (!responseLen)
5062 res = ERROR_HTTP_HEADER_NOT_FOUND;
5064 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5065 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5067 /* process cookies here. Is this right? */
5068 http_process_keep_alive(request);
5069 HTTP_ProcessCookies(request);
5070 HTTP_ProcessExpires(request);
5071 HTTP_ProcessLastModified(request);
5073 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5074 if(!request->contentLength)
5075 http_release_netconn(request, TRUE);
5078 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5080 switch(request->status_code) {
5081 case HTTP_STATUS_REDIRECT:
5082 case HTTP_STATUS_MOVED:
5083 case HTTP_STATUS_REDIRECT_METHOD:
5084 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5085 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5086 dwBufferSize=sizeof(szNewLocation);
5087 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5088 break;
5090 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5091 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5093 heap_free(request->verb);
5094 request->verb = heap_strdupW(szGET);
5096 http_release_netconn(request, drain_content(request, FALSE));
5097 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5099 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5100 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5101 res = HTTP_HandleRedirect(request, new_url);
5102 if (res == ERROR_SUCCESS)
5103 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5104 heap_free( new_url );
5110 if (res == ERROR_SUCCESS && request->contentLength)
5111 HTTP_ReceiveRequestData(request, TRUE);
5112 else
5113 send_request_complete(request, res == ERROR_SUCCESS, res);
5115 return res;
5118 /***********************************************************************
5119 * HttpEndRequestA (WININET.@)
5121 * Ends an HTTP request that was started by HttpSendRequestEx
5123 * RETURNS
5124 * TRUE if successful
5125 * FALSE on failure
5128 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5129 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5131 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5133 if (lpBuffersOut)
5135 SetLastError(ERROR_INVALID_PARAMETER);
5136 return FALSE;
5139 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5142 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5144 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5145 http_request_t *request = (http_request_t*)work->hdr;
5147 TRACE("%p\n", request);
5149 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5152 /***********************************************************************
5153 * HttpEndRequestW (WININET.@)
5155 * Ends an HTTP request that was started by HttpSendRequestEx
5157 * RETURNS
5158 * TRUE if successful
5159 * FALSE on failure
5162 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5163 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5165 http_request_t *request;
5166 DWORD res;
5168 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5170 if (lpBuffersOut)
5172 SetLastError(ERROR_INVALID_PARAMETER);
5173 return FALSE;
5176 request = (http_request_t*) get_handle_object( hRequest );
5178 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5180 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5181 if (request)
5182 WININET_Release( &request->hdr );
5183 return FALSE;
5185 request->hdr.dwFlags |= dwFlags;
5187 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5189 WORKREQUEST work;
5190 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5192 work.asyncproc = AsyncHttpEndRequestProc;
5193 work.hdr = WININET_AddRef( &request->hdr );
5195 work_endrequest = &work.u.HttpEndRequestW;
5196 work_endrequest->dwFlags = dwFlags;
5197 work_endrequest->dwContext = dwContext;
5199 INTERNET_AsyncCall(&work);
5200 res = ERROR_IO_PENDING;
5202 else
5203 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5205 WININET_Release( &request->hdr );
5206 TRACE("%u <--\n", res);
5207 if(res != ERROR_SUCCESS)
5208 SetLastError(res);
5209 return res == ERROR_SUCCESS;
5212 /***********************************************************************
5213 * HttpSendRequestExA (WININET.@)
5215 * Sends the specified request to the HTTP server and allows chunked
5216 * transfers.
5218 * RETURNS
5219 * Success: TRUE
5220 * Failure: FALSE, call GetLastError() for more information.
5222 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5223 LPINTERNET_BUFFERSA lpBuffersIn,
5224 LPINTERNET_BUFFERSA lpBuffersOut,
5225 DWORD dwFlags, DWORD_PTR dwContext)
5227 INTERNET_BUFFERSW BuffersInW;
5228 BOOL rc = FALSE;
5229 DWORD headerlen;
5230 LPWSTR header = NULL;
5232 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5233 lpBuffersOut, dwFlags, dwContext);
5235 if (lpBuffersIn)
5237 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5238 if (lpBuffersIn->lpcszHeader)
5240 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5241 lpBuffersIn->dwHeadersLength,0,0);
5242 header = heap_alloc(headerlen*sizeof(WCHAR));
5243 if (!(BuffersInW.lpcszHeader = header))
5245 SetLastError(ERROR_OUTOFMEMORY);
5246 return FALSE;
5248 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5249 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5250 header, headerlen);
5252 else
5253 BuffersInW.lpcszHeader = NULL;
5254 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5255 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5256 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5257 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5258 BuffersInW.Next = NULL;
5261 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5263 heap_free(header);
5264 return rc;
5267 /***********************************************************************
5268 * HttpSendRequestExW (WININET.@)
5270 * Sends the specified request to the HTTP server and allows chunked
5271 * transfers
5273 * RETURNS
5274 * Success: TRUE
5275 * Failure: FALSE, call GetLastError() for more information.
5277 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5278 LPINTERNET_BUFFERSW lpBuffersIn,
5279 LPINTERNET_BUFFERSW lpBuffersOut,
5280 DWORD dwFlags, DWORD_PTR dwContext)
5282 http_request_t *request;
5283 http_session_t *session;
5284 appinfo_t *hIC;
5285 DWORD res;
5287 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5288 lpBuffersOut, dwFlags, dwContext);
5290 request = (http_request_t*) get_handle_object( hRequest );
5292 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5294 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5295 goto lend;
5298 session = request->session;
5299 assert(session->hdr.htype == WH_HHTTPSESSION);
5300 hIC = session->appInfo;
5301 assert(hIC->hdr.htype == WH_HINIT);
5303 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5305 WORKREQUEST workRequest;
5306 struct WORKREQ_HTTPSENDREQUESTW *req;
5308 workRequest.asyncproc = AsyncHttpSendRequestProc;
5309 workRequest.hdr = WININET_AddRef( &request->hdr );
5310 req = &workRequest.u.HttpSendRequestW;
5311 if (lpBuffersIn)
5313 DWORD size = 0;
5315 if (lpBuffersIn->lpcszHeader)
5317 if (lpBuffersIn->dwHeadersLength == ~0u)
5318 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5319 else
5320 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5322 req->lpszHeader = heap_alloc(size);
5323 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5325 else req->lpszHeader = NULL;
5327 req->dwHeaderLength = size / sizeof(WCHAR);
5328 req->lpOptional = lpBuffersIn->lpvBuffer;
5329 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5330 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5332 else
5334 req->lpszHeader = NULL;
5335 req->dwHeaderLength = 0;
5336 req->lpOptional = NULL;
5337 req->dwOptionalLength = 0;
5338 req->dwContentLength = 0;
5341 req->bEndRequest = FALSE;
5343 INTERNET_AsyncCall(&workRequest);
5345 * This is from windows.
5347 res = ERROR_IO_PENDING;
5349 else
5351 if (lpBuffersIn)
5352 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5353 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5354 lpBuffersIn->dwBufferTotal, FALSE);
5355 else
5356 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5359 lend:
5360 if ( request )
5361 WININET_Release( &request->hdr );
5363 TRACE("<---\n");
5364 SetLastError(res);
5365 return res == ERROR_SUCCESS;
5368 /***********************************************************************
5369 * HttpSendRequestW (WININET.@)
5371 * Sends the specified request to the HTTP server
5373 * RETURNS
5374 * TRUE on success
5375 * FALSE on failure
5378 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5379 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5381 http_request_t *request;
5382 http_session_t *session = NULL;
5383 appinfo_t *hIC = NULL;
5384 DWORD res = ERROR_SUCCESS;
5386 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5387 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5389 request = (http_request_t*) get_handle_object( hHttpRequest );
5390 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5392 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5393 goto lend;
5396 session = request->session;
5397 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5399 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5400 goto lend;
5403 hIC = session->appInfo;
5404 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5406 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5407 goto lend;
5410 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5412 WORKREQUEST workRequest;
5413 struct WORKREQ_HTTPSENDREQUESTW *req;
5415 workRequest.asyncproc = AsyncHttpSendRequestProc;
5416 workRequest.hdr = WININET_AddRef( &request->hdr );
5417 req = &workRequest.u.HttpSendRequestW;
5418 if (lpszHeaders)
5420 DWORD size;
5422 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5423 else size = dwHeaderLength * sizeof(WCHAR);
5425 req->lpszHeader = heap_alloc(size);
5426 memcpy(req->lpszHeader, lpszHeaders, size);
5428 else
5429 req->lpszHeader = 0;
5430 req->dwHeaderLength = dwHeaderLength;
5431 req->lpOptional = lpOptional;
5432 req->dwOptionalLength = dwOptionalLength;
5433 req->dwContentLength = dwOptionalLength;
5434 req->bEndRequest = TRUE;
5436 INTERNET_AsyncCall(&workRequest);
5438 * This is from windows.
5440 res = ERROR_IO_PENDING;
5442 else
5444 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5445 dwHeaderLength, lpOptional, dwOptionalLength,
5446 dwOptionalLength, TRUE);
5448 lend:
5449 if( request )
5450 WININET_Release( &request->hdr );
5452 SetLastError(res);
5453 return res == ERROR_SUCCESS;
5456 /***********************************************************************
5457 * HttpSendRequestA (WININET.@)
5459 * Sends the specified request to the HTTP server
5461 * RETURNS
5462 * TRUE on success
5463 * FALSE on failure
5466 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5467 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5469 BOOL result;
5470 LPWSTR szHeaders=NULL;
5471 DWORD nLen=dwHeaderLength;
5472 if(lpszHeaders!=NULL)
5474 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5475 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5476 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5478 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5479 heap_free(szHeaders);
5480 return result;
5483 /***********************************************************************
5484 * HTTPSESSION_Destroy (internal)
5486 * Deallocate session handle
5489 static void HTTPSESSION_Destroy(object_header_t *hdr)
5491 http_session_t *session = (http_session_t*) hdr;
5493 TRACE("%p\n", session);
5495 WININET_Release(&session->appInfo->hdr);
5497 heap_free(session->hostName);
5498 heap_free(session->password);
5499 heap_free(session->userName);
5502 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5504 http_session_t *ses = (http_session_t *)hdr;
5506 switch(option) {
5507 case INTERNET_OPTION_HANDLE_TYPE:
5508 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5510 if (*size < sizeof(ULONG))
5511 return ERROR_INSUFFICIENT_BUFFER;
5513 *size = sizeof(DWORD);
5514 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5515 return ERROR_SUCCESS;
5516 case INTERNET_OPTION_CONNECT_TIMEOUT:
5517 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5519 if (*size < sizeof(DWORD))
5520 return ERROR_INSUFFICIENT_BUFFER;
5522 *size = sizeof(DWORD);
5523 *(DWORD *)buffer = ses->connect_timeout;
5524 return ERROR_SUCCESS;
5526 case INTERNET_OPTION_SEND_TIMEOUT:
5527 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5529 if (*size < sizeof(DWORD))
5530 return ERROR_INSUFFICIENT_BUFFER;
5532 *size = sizeof(DWORD);
5533 *(DWORD *)buffer = ses->send_timeout;
5534 return ERROR_SUCCESS;
5536 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5537 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5539 if (*size < sizeof(DWORD))
5540 return ERROR_INSUFFICIENT_BUFFER;
5542 *size = sizeof(DWORD);
5543 *(DWORD *)buffer = ses->receive_timeout;
5544 return ERROR_SUCCESS;
5547 return INET_QueryOption(hdr, option, buffer, size, unicode);
5550 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5552 http_session_t *ses = (http_session_t*)hdr;
5554 switch(option) {
5555 case INTERNET_OPTION_USERNAME:
5557 heap_free(ses->userName);
5558 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5559 return ERROR_SUCCESS;
5561 case INTERNET_OPTION_PASSWORD:
5563 heap_free(ses->password);
5564 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5565 return ERROR_SUCCESS;
5567 case INTERNET_OPTION_CONNECT_TIMEOUT:
5569 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5570 ses->connect_timeout = *(DWORD *)buffer;
5571 return ERROR_SUCCESS;
5573 case INTERNET_OPTION_SEND_TIMEOUT:
5575 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5576 ses->send_timeout = *(DWORD *)buffer;
5577 return ERROR_SUCCESS;
5579 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5581 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5582 ses->receive_timeout = *(DWORD *)buffer;
5583 return ERROR_SUCCESS;
5585 default: break;
5588 return INET_SetOption(hdr, option, buffer, size);
5591 static const object_vtbl_t HTTPSESSIONVtbl = {
5592 HTTPSESSION_Destroy,
5593 NULL,
5594 HTTPSESSION_QueryOption,
5595 HTTPSESSION_SetOption,
5596 NULL,
5597 NULL,
5598 NULL,
5599 NULL,
5600 NULL
5604 /***********************************************************************
5605 * HTTP_Connect (internal)
5607 * Create http session handle
5609 * RETURNS
5610 * HINTERNET a session handle on success
5611 * NULL on failure
5614 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5615 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5616 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5617 DWORD dwInternalFlags, HINTERNET *ret)
5619 http_session_t *session = NULL;
5621 TRACE("-->\n");
5623 if (!lpszServerName || !lpszServerName[0])
5624 return ERROR_INVALID_PARAMETER;
5626 assert( hIC->hdr.htype == WH_HINIT );
5628 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5629 if (!session)
5630 return ERROR_OUTOFMEMORY;
5633 * According to my tests. The name is not resolved until a request is sent
5636 session->hdr.htype = WH_HHTTPSESSION;
5637 session->hdr.dwFlags = dwFlags;
5638 session->hdr.dwContext = dwContext;
5639 session->hdr.dwInternalFlags |= dwInternalFlags;
5641 WININET_AddRef( &hIC->hdr );
5642 session->appInfo = hIC;
5643 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5645 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5646 if(hIC->proxyBypass)
5647 FIXME("Proxy bypass is ignored.\n");
5649 session->hostName = heap_strdupW(lpszServerName);
5650 if (lpszUserName && lpszUserName[0])
5651 session->userName = heap_strdupW(lpszUserName);
5652 if (lpszPassword && lpszPassword[0])
5653 session->password = heap_strdupW(lpszPassword);
5654 session->hostPort = serverPort;
5655 session->connect_timeout = hIC->connect_timeout;
5656 session->send_timeout = INFINITE;
5657 session->receive_timeout = INFINITE;
5659 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5660 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5662 INTERNET_SendCallback(&hIC->hdr, dwContext,
5663 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5664 sizeof(HINTERNET));
5668 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5669 * windows
5672 TRACE("%p --> %p\n", hIC, session);
5674 *ret = session->hdr.hInternet;
5675 return ERROR_SUCCESS;
5678 /***********************************************************************
5679 * HTTP_clear_response_headers (internal)
5681 * clear out any old response headers
5683 static void HTTP_clear_response_headers( http_request_t *request )
5685 DWORD i;
5687 for( i=0; i<request->nCustHeaders; i++)
5689 if( !request->custHeaders[i].lpszField )
5690 continue;
5691 if( !request->custHeaders[i].lpszValue )
5692 continue;
5693 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5694 continue;
5695 HTTP_DeleteCustomHeader( request, i );
5696 i--;
5700 /***********************************************************************
5701 * HTTP_GetResponseHeaders (internal)
5703 * Read server response
5705 * RETURNS
5707 * TRUE on success
5708 * FALSE on error
5710 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5712 INT cbreaks = 0;
5713 WCHAR buffer[MAX_REPLY_LEN];
5714 DWORD buflen = MAX_REPLY_LEN;
5715 BOOL bSuccess = FALSE;
5716 INT rc = 0;
5717 char bufferA[MAX_REPLY_LEN];
5718 LPWSTR status_code = NULL, status_text = NULL;
5719 DWORD cchMaxRawHeaders = 1024;
5720 LPWSTR lpszRawHeaders = NULL;
5721 LPWSTR temp;
5722 DWORD cchRawHeaders = 0;
5723 BOOL codeHundred = FALSE;
5725 TRACE("-->\n");
5727 if(!request->netconn)
5728 goto lend;
5730 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5731 do {
5732 static const WCHAR szHundred[] = {'1','0','0',0};
5734 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5736 buflen = MAX_REPLY_LEN;
5737 if (!read_line(request, bufferA, &buflen))
5738 goto lend;
5740 /* clear old response headers (eg. from a redirect response) */
5741 if (clear) {
5742 HTTP_clear_response_headers( request );
5743 clear = FALSE;
5746 rc += buflen;
5747 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5748 /* check is this a status code line? */
5749 if (!strncmpW(buffer, g_szHttp1_0, 4))
5751 /* split the version from the status code */
5752 status_code = strchrW( buffer, ' ' );
5753 if( !status_code )
5754 goto lend;
5755 *status_code++=0;
5757 /* split the status code from the status text */
5758 status_text = strchrW( status_code, ' ' );
5759 if( !status_text )
5760 goto lend;
5761 *status_text++=0;
5763 request->status_code = atoiW(status_code);
5765 TRACE("version [%s] status code [%s] status text [%s]\n",
5766 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5768 codeHundred = (!strcmpW(status_code, szHundred));
5770 else if (!codeHundred)
5772 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5774 heap_free(request->version);
5775 heap_free(request->statusText);
5777 request->status_code = HTTP_STATUS_OK;
5778 request->version = heap_strdupW(g_szHttp1_0);
5779 request->statusText = heap_strdupW(szOK);
5781 heap_free(request->rawHeaders);
5782 request->rawHeaders = heap_strdupW(szDefaultHeader);
5784 bSuccess = TRUE;
5785 goto lend;
5787 } while (codeHundred);
5789 /* Add status code */
5790 HTTP_ProcessHeader(request, szStatus, status_code,
5791 HTTP_ADDHDR_FLAG_REPLACE);
5793 heap_free(request->version);
5794 heap_free(request->statusText);
5796 request->version = heap_strdupW(buffer);
5797 request->statusText = heap_strdupW(status_text);
5799 /* Restore the spaces */
5800 *(status_code-1) = ' ';
5801 *(status_text-1) = ' ';
5803 /* regenerate raw headers */
5804 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5805 if (!lpszRawHeaders) goto lend;
5807 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5808 cchMaxRawHeaders *= 2;
5809 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5810 if (temp == NULL) goto lend;
5811 lpszRawHeaders = temp;
5812 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5813 cchRawHeaders += (buflen-1);
5814 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5815 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5816 lpszRawHeaders[cchRawHeaders] = '\0';
5818 /* Parse each response line */
5821 buflen = MAX_REPLY_LEN;
5822 if (read_line(request, bufferA, &buflen))
5824 LPWSTR * pFieldAndValue;
5826 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5828 if (!bufferA[0]) break;
5829 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5831 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5832 if (pFieldAndValue)
5834 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5835 cchMaxRawHeaders *= 2;
5836 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5837 if (temp == NULL) goto lend;
5838 lpszRawHeaders = temp;
5839 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5840 cchRawHeaders += (buflen-1);
5841 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5842 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5843 lpszRawHeaders[cchRawHeaders] = '\0';
5845 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5846 HTTP_ADDREQ_FLAG_ADD );
5848 HTTP_FreeTokens(pFieldAndValue);
5851 else
5853 cbreaks++;
5854 if (cbreaks >= 2)
5855 break;
5857 }while(1);
5859 /* make sure the response header is terminated with an empty line. Some apps really
5860 truly care about that empty line being there for some reason. Just add it to the
5861 header. */
5862 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5864 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5865 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5866 if (temp == NULL) goto lend;
5867 lpszRawHeaders = temp;
5870 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5872 heap_free(request->rawHeaders);
5873 request->rawHeaders = lpszRawHeaders;
5874 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5875 bSuccess = TRUE;
5877 lend:
5879 TRACE("<--\n");
5880 if (bSuccess)
5881 return rc;
5882 else
5884 heap_free(lpszRawHeaders);
5885 return 0;
5889 /***********************************************************************
5890 * HTTP_InterpretHttpHeader (internal)
5892 * Parse server response
5894 * RETURNS
5896 * Pointer to array of field, value, NULL on success.
5897 * NULL on error.
5899 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5901 LPWSTR * pTokenPair;
5902 LPWSTR pszColon;
5903 INT len;
5905 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5907 pszColon = strchrW(buffer, ':');
5908 /* must have two tokens */
5909 if (!pszColon)
5911 HTTP_FreeTokens(pTokenPair);
5912 if (buffer[0])
5913 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5914 return NULL;
5917 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5918 if (!pTokenPair[0])
5920 HTTP_FreeTokens(pTokenPair);
5921 return NULL;
5923 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5924 pTokenPair[0][pszColon - buffer] = '\0';
5926 /* skip colon */
5927 pszColon++;
5928 len = strlenW(pszColon);
5929 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5930 if (!pTokenPair[1])
5932 HTTP_FreeTokens(pTokenPair);
5933 return NULL;
5935 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5937 strip_spaces(pTokenPair[0]);
5938 strip_spaces(pTokenPair[1]);
5940 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5941 return pTokenPair;
5944 /***********************************************************************
5945 * HTTP_ProcessHeader (internal)
5947 * Stuff header into header tables according to <dwModifier>
5951 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5953 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5955 LPHTTPHEADERW lphttpHdr = NULL;
5956 INT index = -1;
5957 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5958 DWORD res = ERROR_HTTP_INVALID_HEADER;
5960 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5962 /* REPLACE wins out over ADD */
5963 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5964 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5966 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5967 index = -1;
5968 else
5969 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5971 if (index >= 0)
5973 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5974 return ERROR_HTTP_INVALID_HEADER;
5975 lphttpHdr = &request->custHeaders[index];
5977 else if (value)
5979 HTTPHEADERW hdr;
5981 hdr.lpszField = (LPWSTR)field;
5982 hdr.lpszValue = (LPWSTR)value;
5983 hdr.wFlags = hdr.wCount = 0;
5985 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5986 hdr.wFlags |= HDR_ISREQUEST;
5988 return HTTP_InsertCustomHeader(request, &hdr);
5990 /* no value to delete */
5991 else return ERROR_SUCCESS;
5993 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5994 lphttpHdr->wFlags |= HDR_ISREQUEST;
5995 else
5996 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5998 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6000 HTTP_DeleteCustomHeader( request, index );
6002 if (value)
6004 HTTPHEADERW hdr;
6006 hdr.lpszField = (LPWSTR)field;
6007 hdr.lpszValue = (LPWSTR)value;
6008 hdr.wFlags = hdr.wCount = 0;
6010 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6011 hdr.wFlags |= HDR_ISREQUEST;
6013 return HTTP_InsertCustomHeader(request, &hdr);
6016 return ERROR_SUCCESS;
6018 else if (dwModifier & COALESCEFLAGS)
6020 LPWSTR lpsztmp;
6021 WCHAR ch = 0;
6022 INT len = 0;
6023 INT origlen = strlenW(lphttpHdr->lpszValue);
6024 INT valuelen = strlenW(value);
6026 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6028 ch = ',';
6029 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6031 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6033 ch = ';';
6034 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6037 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6039 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6040 if (lpsztmp)
6042 lphttpHdr->lpszValue = lpsztmp;
6043 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6044 if (ch > 0)
6046 lphttpHdr->lpszValue[origlen] = ch;
6047 origlen++;
6048 lphttpHdr->lpszValue[origlen] = ' ';
6049 origlen++;
6052 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6053 lphttpHdr->lpszValue[len] = '\0';
6054 res = ERROR_SUCCESS;
6056 else
6058 WARN("heap_realloc (%d bytes) failed\n",len+1);
6059 res = ERROR_OUTOFMEMORY;
6062 TRACE("<-- %d\n", res);
6063 return res;
6066 /***********************************************************************
6067 * HTTP_GetCustomHeaderIndex (internal)
6069 * Return index of custom header from header array
6072 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6073 int requested_index, BOOL request_only)
6075 DWORD index;
6077 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6079 for (index = 0; index < request->nCustHeaders; index++)
6081 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6082 continue;
6084 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6085 continue;
6087 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6088 continue;
6090 if (requested_index == 0)
6091 break;
6092 requested_index --;
6095 if (index >= request->nCustHeaders)
6096 index = -1;
6098 TRACE("Return: %d\n", index);
6099 return index;
6103 /***********************************************************************
6104 * HTTP_InsertCustomHeader (internal)
6106 * Insert header into array
6109 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6111 INT count;
6112 LPHTTPHEADERW lph = NULL;
6114 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6115 count = request->nCustHeaders + 1;
6116 if (count > 1)
6117 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6118 else
6119 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6121 if (!lph)
6122 return ERROR_OUTOFMEMORY;
6124 request->custHeaders = lph;
6125 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6126 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6127 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6128 request->custHeaders[count-1].wCount= lpHdr->wCount;
6129 request->nCustHeaders++;
6131 return ERROR_SUCCESS;
6135 /***********************************************************************
6136 * HTTP_DeleteCustomHeader (internal)
6138 * Delete header from array
6139 * If this function is called, the indexs may change.
6141 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6143 if( request->nCustHeaders <= 0 )
6144 return FALSE;
6145 if( index >= request->nCustHeaders )
6146 return FALSE;
6147 request->nCustHeaders--;
6149 heap_free(request->custHeaders[index].lpszField);
6150 heap_free(request->custHeaders[index].lpszValue);
6152 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6153 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6154 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6156 return TRUE;
6160 /***********************************************************************
6161 * HTTP_VerifyValidHeader (internal)
6163 * Verify the given header is not invalid for the given http request
6166 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6168 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6169 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6170 return ERROR_HTTP_INVALID_HEADER;
6172 return ERROR_SUCCESS;
6175 /***********************************************************************
6176 * IsHostInProxyBypassList (@)
6178 * Undocumented
6181 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6183 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6184 return FALSE;
6187 /***********************************************************************
6188 * InternetShowSecurityInfoByURLA (@)
6190 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6192 FIXME("stub: %s %p\n", url, window);
6193 return FALSE;
6196 /***********************************************************************
6197 * InternetShowSecurityInfoByURLW (@)
6199 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6201 FIXME("stub: %s %p\n", debugstr_w(url), window);
6202 return FALSE;
6205 /***********************************************************************
6206 * ShowX509EncodedCertificate (@)
6208 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6210 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6211 cert, len);
6212 DWORD ret;
6214 if (certContext)
6216 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6218 memset(&view, 0, sizeof(view));
6219 view.hwndParent = parent;
6220 view.pCertContext = certContext;
6221 if (CryptUIDlgViewCertificateW(&view, NULL))
6222 ret = ERROR_SUCCESS;
6223 else
6224 ret = GetLastError();
6225 CertFreeCertificateContext(certContext);
6227 else
6228 ret = GetLastError();
6229 return ret;