wininet: Added InternetGetSecurityInfoByURL[AW] implementation.
[wine/multimedia.git] / dlls / wininet / http.c
blob0cec4fba65d721fbf226353c95f7e282d615f039
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);
223 static CRITICAL_SECTION connection_pool_cs;
224 static CRITICAL_SECTION_DEBUG connection_pool_debug =
226 0, 0, &connection_pool_cs,
227 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
228 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
230 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
232 static struct list connection_pool = LIST_INIT(connection_pool);
233 static BOOL collector_running;
235 void server_addref(server_t *server)
237 InterlockedIncrement(&server->ref);
240 void server_release(server_t *server)
242 if(InterlockedDecrement(&server->ref))
243 return;
245 list_remove(&server->entry);
247 if(server->cert_chain)
248 CertFreeCertificateChain(server->cert_chain);
249 heap_free(server->name);
250 heap_free(server);
253 server_t *get_server(const WCHAR *name, INTERNET_PORT port, BOOL do_create)
255 server_t *iter, *server = NULL;
257 EnterCriticalSection(&connection_pool_cs);
259 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
260 if(iter->port == port && !strcmpW(iter->name, name)) {
261 server = iter;
262 server_addref(server);
263 break;
267 if(!server && do_create) {
268 server = heap_alloc_zero(sizeof(*server));
269 if(server) {
270 server->ref = 2; /* list reference and return */
271 server->port = port;
272 list_init(&server->conn_pool);
273 server->name = heap_strdupW(name);
274 if(server->name) {
275 list_add_head(&connection_pool, &server->entry);
276 }else {
277 heap_free(server);
278 server = NULL;
283 LeaveCriticalSection(&connection_pool_cs);
285 return server;
288 BOOL collect_connections(collect_type_t collect_type)
290 netconn_t *netconn, *netconn_safe;
291 server_t *server, *server_safe;
292 BOOL remaining = FALSE;
293 DWORD64 now;
295 now = GetTickCount64();
297 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
298 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
299 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
300 TRACE("freeing %p\n", netconn);
301 list_remove(&netconn->pool_entry);
302 free_netconn(netconn);
303 }else {
304 remaining = TRUE;
308 if(collect_type == COLLECT_CLEANUP) {
309 list_remove(&server->entry);
310 list_init(&server->entry);
311 server_release(server);
315 return remaining;
318 static DWORD WINAPI collect_connections_proc(void *arg)
320 BOOL remaining_conns;
322 do {
323 /* FIXME: Use more sophisticated method */
324 Sleep(5000);
326 EnterCriticalSection(&connection_pool_cs);
328 remaining_conns = collect_connections(COLLECT_TIMEOUT);
329 if(!remaining_conns)
330 collector_running = FALSE;
332 LeaveCriticalSection(&connection_pool_cs);
333 }while(remaining_conns);
335 FreeLibraryAndExitThread(WININET_hModule, 0);
338 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
340 int HeaderIndex = 0;
341 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
342 if (HeaderIndex == -1)
343 return NULL;
344 else
345 return &req->custHeaders[HeaderIndex];
348 typedef enum {
349 READMODE_SYNC,
350 READMODE_ASYNC,
351 READMODE_NOBLOCK
352 } read_mode_t;
354 struct data_stream_vtbl_t {
355 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
356 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
357 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
358 BOOL (*drain_content)(data_stream_t*,http_request_t*);
359 void (*destroy)(data_stream_t*);
362 typedef struct {
363 data_stream_t data_stream;
365 BYTE buf[READ_BUFFER_SIZE];
366 DWORD buf_size;
367 DWORD buf_pos;
368 DWORD chunk_size;
369 } chunked_stream_t;
371 static inline void destroy_data_stream(data_stream_t *stream)
373 stream->vtbl->destroy(stream);
376 static void reset_data_stream(http_request_t *req)
378 destroy_data_stream(req->data_stream);
379 req->data_stream = &req->netconn_stream.data_stream;
380 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
381 req->read_chunked = req->read_gzip = FALSE;
384 #ifdef HAVE_ZLIB
386 typedef struct {
387 data_stream_t stream;
388 data_stream_t *parent_stream;
389 z_stream zstream;
390 BYTE buf[READ_BUFFER_SIZE];
391 DWORD buf_size;
392 DWORD buf_pos;
393 BOOL end_of_data;
394 } gzip_stream_t;
396 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
398 /* Allow reading only from read buffer */
399 return 0;
402 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
404 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
405 return gzip_stream->end_of_data;
408 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
409 DWORD *read, read_mode_t read_mode)
411 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
412 z_stream *zstream = &gzip_stream->zstream;
413 DWORD current_read, ret_read = 0;
414 BOOL end;
415 int zres;
416 DWORD res = ERROR_SUCCESS;
418 while(size && !gzip_stream->end_of_data) {
419 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
421 if(gzip_stream->buf_size <= 64 && !end) {
422 if(gzip_stream->buf_pos) {
423 if(gzip_stream->buf_size)
424 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
425 gzip_stream->buf_pos = 0;
427 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
428 sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
429 gzip_stream->buf_size += current_read;
430 if(res != ERROR_SUCCESS)
431 break;
432 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
433 if(!current_read && !end) {
434 if(read_mode != READMODE_NOBLOCK) {
435 WARN("unexpected end of data\n");
436 gzip_stream->end_of_data = TRUE;
438 break;
440 if(gzip_stream->buf_size <= 64 && !end)
441 continue;
444 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
445 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
446 zstream->next_out = buf+ret_read;
447 zstream->avail_out = size;
448 zres = inflate(&gzip_stream->zstream, 0);
449 current_read = size - zstream->avail_out;
450 size -= current_read;
451 ret_read += current_read;
452 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
453 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
454 if(zres == Z_STREAM_END) {
455 TRACE("end of data\n");
456 gzip_stream->end_of_data = TRUE;
457 inflateEnd(zstream);
458 }else if(zres != Z_OK) {
459 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
460 if(!ret_read)
461 res = ERROR_INTERNET_DECODING_FAILED;
462 break;
465 if(ret_read && read_mode == READMODE_ASYNC)
466 read_mode = READMODE_NOBLOCK;
469 TRACE("read %u bytes\n", ret_read);
470 *read = ret_read;
471 return res;
474 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
476 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
477 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
480 static void gzip_destroy(data_stream_t *stream)
482 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
484 destroy_data_stream(gzip_stream->parent_stream);
486 if(!gzip_stream->end_of_data)
487 inflateEnd(&gzip_stream->zstream);
488 heap_free(gzip_stream);
491 static const data_stream_vtbl_t gzip_stream_vtbl = {
492 gzip_get_avail_data,
493 gzip_end_of_data,
494 gzip_read,
495 gzip_drain_content,
496 gzip_destroy
499 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
501 return heap_alloc(items*size);
504 static void wininet_zfree(voidpf opaque, voidpf address)
506 heap_free(address);
509 static DWORD init_gzip_stream(http_request_t *req)
511 gzip_stream_t *gzip_stream;
512 int index, zres;
514 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
515 if(!gzip_stream)
516 return ERROR_OUTOFMEMORY;
518 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
519 gzip_stream->zstream.zalloc = wininet_zalloc;
520 gzip_stream->zstream.zfree = wininet_zfree;
522 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
523 if(zres != Z_OK) {
524 ERR("inflateInit failed: %d\n", zres);
525 heap_free(gzip_stream);
526 return ERROR_OUTOFMEMORY;
529 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
530 if(index != -1)
531 HTTP_DeleteCustomHeader(req, index);
533 if(req->read_size) {
534 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
535 gzip_stream->buf_size = req->read_size;
536 req->read_pos = req->read_size = 0;
539 req->read_gzip = TRUE;
540 gzip_stream->parent_stream = req->data_stream;
541 req->data_stream = &gzip_stream->stream;
542 return ERROR_SUCCESS;
545 #else
547 static DWORD init_gzip_stream(http_request_t *req)
549 ERR("gzip stream not supported, missing zlib.\n");
550 return ERROR_SUCCESS;
553 #endif
555 /***********************************************************************
556 * HTTP_Tokenize (internal)
558 * Tokenize a string, allocating memory for the tokens.
560 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
562 LPWSTR * token_array;
563 int tokens = 0;
564 int i;
565 LPCWSTR next_token;
567 if (string)
569 /* empty string has no tokens */
570 if (*string)
571 tokens++;
572 /* count tokens */
573 for (i = 0; string[i]; i++)
575 if (!strncmpW(string+i, token_string, strlenW(token_string)))
577 DWORD j;
578 tokens++;
579 /* we want to skip over separators, but not the null terminator */
580 for (j = 0; j < strlenW(token_string) - 1; j++)
581 if (!string[i+j])
582 break;
583 i += j;
588 /* add 1 for terminating NULL */
589 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
590 token_array[tokens] = NULL;
591 if (!tokens)
592 return token_array;
593 for (i = 0; i < tokens; i++)
595 int len;
596 next_token = strstrW(string, token_string);
597 if (!next_token) next_token = string+strlenW(string);
598 len = next_token - string;
599 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
600 memcpy(token_array[i], string, len*sizeof(WCHAR));
601 token_array[i][len] = '\0';
602 string = next_token+strlenW(token_string);
604 return token_array;
607 /***********************************************************************
608 * HTTP_FreeTokens (internal)
610 * Frees memory returned from HTTP_Tokenize.
612 static void HTTP_FreeTokens(LPWSTR * token_array)
614 int i;
615 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
616 heap_free(token_array);
619 static void HTTP_FixURL(http_request_t *request)
621 static const WCHAR szSlash[] = { '/',0 };
622 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
624 /* If we don't have a path we set it to root */
625 if (NULL == request->path)
626 request->path = heap_strdupW(szSlash);
627 else /* remove \r and \n*/
629 int nLen = strlenW(request->path);
630 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
632 nLen--;
633 request->path[nLen]='\0';
635 /* Replace '\' with '/' */
636 while (nLen>0) {
637 nLen--;
638 if (request->path[nLen] == '\\') request->path[nLen]='/';
642 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
643 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
644 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
646 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
647 *fixurl = '/';
648 strcpyW(fixurl + 1, request->path);
649 heap_free( request->path );
650 request->path = fixurl;
654 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
656 LPWSTR requestString;
657 DWORD len, n;
658 LPCWSTR *req;
659 UINT i;
660 LPWSTR p;
662 static const WCHAR szSpace[] = { ' ',0 };
663 static const WCHAR szColon[] = { ':',' ',0 };
664 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
666 /* allocate space for an array of all the string pointers to be added */
667 len = (request->nCustHeaders)*4 + 10;
668 req = heap_alloc(len*sizeof(LPCWSTR));
670 /* add the verb, path and HTTP version string */
671 n = 0;
672 req[n++] = verb;
673 req[n++] = szSpace;
674 req[n++] = path;
675 req[n++] = szSpace;
676 req[n++] = version;
678 /* Append custom request headers */
679 for (i = 0; i < request->nCustHeaders; i++)
681 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
683 req[n++] = szCrLf;
684 req[n++] = request->custHeaders[i].lpszField;
685 req[n++] = szColon;
686 req[n++] = request->custHeaders[i].lpszValue;
688 TRACE("Adding custom header %s (%s)\n",
689 debugstr_w(request->custHeaders[i].lpszField),
690 debugstr_w(request->custHeaders[i].lpszValue));
694 if( n >= len )
695 ERR("oops. buffer overrun\n");
697 req[n] = NULL;
698 requestString = HTTP_build_req( req, 4 );
699 heap_free( req );
702 * Set (header) termination string for request
703 * Make sure there's exactly two new lines at the end of the request
705 p = &requestString[strlenW(requestString)-1];
706 while ( (*p == '\n') || (*p == '\r') )
707 p--;
708 strcpyW( p+1, sztwocrlf );
710 return requestString;
713 static void HTTP_ProcessCookies( http_request_t *request )
715 int HeaderIndex;
716 int numCookies = 0;
717 LPHTTPHEADERW setCookieHeader;
719 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
720 return;
722 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
724 HTTPHEADERW *host;
725 const WCHAR *data;
726 WCHAR *name;
728 setCookieHeader = &request->custHeaders[HeaderIndex];
730 if (!setCookieHeader->lpszValue)
731 continue;
733 host = HTTP_GetHeader(request, hostW);
734 if(!host)
735 continue;
737 data = strchrW(setCookieHeader->lpszValue, '=');
738 if(!data)
739 continue;
741 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
742 if(!name)
743 continue;
745 data++;
746 set_cookie(host->lpszValue, request->path, name, data);
747 heap_free(name);
751 static void strip_spaces(LPWSTR start)
753 LPWSTR str = start;
754 LPWSTR end;
756 while (*str == ' ' && *str != '\0')
757 str++;
759 if (str != start)
760 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
762 end = start + strlenW(start) - 1;
763 while (end >= start && *end == ' ')
765 *end = '\0';
766 end--;
770 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
772 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
773 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
774 BOOL is_basic;
775 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
776 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
777 if (is_basic && pszRealm)
779 LPCWSTR token;
780 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
781 LPCWSTR realm;
782 ptr++;
783 *pszRealm=NULL;
784 token = strchrW(ptr,'=');
785 if (!token)
786 return TRUE;
787 realm = ptr;
788 while (*realm == ' ' && *realm != '\0')
789 realm++;
790 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
791 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
793 token++;
794 while (*token == ' ' && *token != '\0')
795 token++;
796 if (*token == '\0')
797 return TRUE;
798 *pszRealm = heap_strdupW(token);
799 strip_spaces(*pszRealm);
803 return is_basic;
806 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
808 if (!authinfo) return;
810 if (SecIsValidHandle(&authinfo->ctx))
811 DeleteSecurityContext(&authinfo->ctx);
812 if (SecIsValidHandle(&authinfo->cred))
813 FreeCredentialsHandle(&authinfo->cred);
815 heap_free(authinfo->auth_data);
816 heap_free(authinfo->scheme);
817 heap_free(authinfo);
820 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
822 basicAuthorizationData *ad;
823 UINT rc = 0;
825 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
827 EnterCriticalSection(&authcache_cs);
828 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
830 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
832 TRACE("Authorization found in cache\n");
833 *auth_data = heap_alloc(ad->authorizationLen);
834 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
835 rc = ad->authorizationLen;
836 break;
839 LeaveCriticalSection(&authcache_cs);
840 return rc;
843 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
845 struct list *cursor;
846 basicAuthorizationData* ad = NULL;
848 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
850 EnterCriticalSection(&authcache_cs);
851 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
853 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
854 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
856 ad = check;
857 break;
861 if (ad)
863 TRACE("Found match in cache, replacing\n");
864 heap_free(ad->authorization);
865 ad->authorization = heap_alloc(auth_data_len);
866 memcpy(ad->authorization, auth_data, auth_data_len);
867 ad->authorizationLen = auth_data_len;
869 else
871 ad = heap_alloc(sizeof(basicAuthorizationData));
872 ad->host = heap_strdupW(host);
873 ad->realm = heap_strdupW(realm);
874 ad->authorization = heap_alloc(auth_data_len);
875 memcpy(ad->authorization, auth_data, auth_data_len);
876 ad->authorizationLen = auth_data_len;
877 list_add_head(&basicAuthorizationCache,&ad->entry);
878 TRACE("authorization cached\n");
880 LeaveCriticalSection(&authcache_cs);
883 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
884 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
886 authorizationData *ad;
888 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
890 EnterCriticalSection(&authcache_cs);
891 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
892 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
893 TRACE("Authorization found in cache\n");
895 nt_auth_identity->User = heap_strdupW(ad->user);
896 nt_auth_identity->Password = heap_strdupW(ad->password);
897 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
898 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
899 (!nt_auth_identity->Domain && ad->domain_len)) {
900 heap_free(nt_auth_identity->User);
901 heap_free(nt_auth_identity->Password);
902 heap_free(nt_auth_identity->Domain);
903 break;
906 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
907 nt_auth_identity->UserLength = ad->user_len;
908 nt_auth_identity->PasswordLength = ad->password_len;
909 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
910 nt_auth_identity->DomainLength = ad->domain_len;
911 LeaveCriticalSection(&authcache_cs);
912 return TRUE;
915 LeaveCriticalSection(&authcache_cs);
917 return FALSE;
920 static void cache_authorization(LPWSTR host, LPWSTR scheme,
921 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
923 authorizationData *ad;
924 BOOL found = FALSE;
926 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
928 EnterCriticalSection(&authcache_cs);
929 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
930 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
931 found = TRUE;
932 break;
935 if(found) {
936 heap_free(ad->user);
937 heap_free(ad->password);
938 heap_free(ad->domain);
939 } else {
940 ad = heap_alloc(sizeof(authorizationData));
941 if(!ad) {
942 LeaveCriticalSection(&authcache_cs);
943 return;
946 ad->host = heap_strdupW(host);
947 ad->scheme = heap_strdupW(scheme);
948 list_add_head(&authorizationCache, &ad->entry);
951 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
952 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
953 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
954 ad->user_len = nt_auth_identity->UserLength;
955 ad->password_len = nt_auth_identity->PasswordLength;
956 ad->domain_len = nt_auth_identity->DomainLength;
958 if(!ad->host || !ad->scheme || !ad->user || !ad->password
959 || (nt_auth_identity->Domain && !ad->domain)) {
960 heap_free(ad->host);
961 heap_free(ad->scheme);
962 heap_free(ad->user);
963 heap_free(ad->password);
964 heap_free(ad->domain);
965 list_remove(&ad->entry);
966 heap_free(ad);
969 LeaveCriticalSection(&authcache_cs);
972 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
973 struct HttpAuthInfo **ppAuthInfo,
974 LPWSTR domain_and_username, LPWSTR password,
975 LPWSTR host )
977 SECURITY_STATUS sec_status;
978 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
979 BOOL first = FALSE;
980 LPWSTR szRealm = NULL;
982 TRACE("%s\n", debugstr_w(pszAuthValue));
984 if (!pAuthInfo)
986 TimeStamp exp;
988 first = TRUE;
989 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
990 if (!pAuthInfo)
991 return FALSE;
993 SecInvalidateHandle(&pAuthInfo->cred);
994 SecInvalidateHandle(&pAuthInfo->ctx);
995 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
996 pAuthInfo->attr = 0;
997 pAuthInfo->auth_data = NULL;
998 pAuthInfo->auth_data_len = 0;
999 pAuthInfo->finished = FALSE;
1001 if (is_basic_auth_value(pszAuthValue,NULL))
1003 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1004 pAuthInfo->scheme = heap_strdupW(szBasic);
1005 if (!pAuthInfo->scheme)
1007 heap_free(pAuthInfo);
1008 return FALSE;
1011 else
1013 PVOID pAuthData;
1014 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1016 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1017 if (!pAuthInfo->scheme)
1019 heap_free(pAuthInfo);
1020 return FALSE;
1023 if (domain_and_username)
1025 WCHAR *user = strchrW(domain_and_username, '\\');
1026 WCHAR *domain = domain_and_username;
1028 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1030 pAuthData = &nt_auth_identity;
1032 if (user) user++;
1033 else
1035 user = domain_and_username;
1036 domain = NULL;
1039 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1040 nt_auth_identity.User = user;
1041 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1042 nt_auth_identity.Domain = domain;
1043 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1044 nt_auth_identity.Password = password;
1045 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1047 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1049 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1050 pAuthData = &nt_auth_identity;
1051 else
1052 /* use default credentials */
1053 pAuthData = NULL;
1055 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1056 SECPKG_CRED_OUTBOUND, NULL,
1057 pAuthData, NULL,
1058 NULL, &pAuthInfo->cred,
1059 &exp);
1061 if(pAuthData && !domain_and_username) {
1062 heap_free(nt_auth_identity.User);
1063 heap_free(nt_auth_identity.Domain);
1064 heap_free(nt_auth_identity.Password);
1067 if (sec_status == SEC_E_OK)
1069 PSecPkgInfoW sec_pkg_info;
1070 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1071 if (sec_status == SEC_E_OK)
1073 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1074 FreeContextBuffer(sec_pkg_info);
1077 if (sec_status != SEC_E_OK)
1079 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1080 debugstr_w(pAuthInfo->scheme), sec_status);
1081 heap_free(pAuthInfo->scheme);
1082 heap_free(pAuthInfo);
1083 return FALSE;
1086 *ppAuthInfo = pAuthInfo;
1088 else if (pAuthInfo->finished)
1089 return FALSE;
1091 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1092 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1094 ERR("authentication scheme changed from %s to %s\n",
1095 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1096 return FALSE;
1099 if (is_basic_auth_value(pszAuthValue,&szRealm))
1101 int userlen;
1102 int passlen;
1103 char *auth_data = NULL;
1104 UINT auth_data_len = 0;
1106 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1108 if (!domain_and_username)
1110 if (host && szRealm)
1111 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1112 if (auth_data_len == 0)
1114 heap_free(szRealm);
1115 return FALSE;
1118 else
1120 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1121 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1123 /* length includes a nul terminator, which will be re-used for the ':' */
1124 auth_data = heap_alloc(userlen + 1 + passlen);
1125 if (!auth_data)
1127 heap_free(szRealm);
1128 return FALSE;
1131 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1132 auth_data[userlen] = ':';
1133 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1134 auth_data_len = userlen + 1 + passlen;
1135 if (host && szRealm)
1136 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1139 pAuthInfo->auth_data = auth_data;
1140 pAuthInfo->auth_data_len = auth_data_len;
1141 pAuthInfo->finished = TRUE;
1142 heap_free(szRealm);
1143 return TRUE;
1145 else
1147 LPCWSTR pszAuthData;
1148 SecBufferDesc out_desc, in_desc;
1149 SecBuffer out, in;
1150 unsigned char *buffer;
1151 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1152 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1154 in.BufferType = SECBUFFER_TOKEN;
1155 in.cbBuffer = 0;
1156 in.pvBuffer = NULL;
1158 in_desc.ulVersion = 0;
1159 in_desc.cBuffers = 1;
1160 in_desc.pBuffers = &in;
1162 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1163 if (*pszAuthData == ' ')
1165 pszAuthData++;
1166 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1167 in.pvBuffer = heap_alloc(in.cbBuffer);
1168 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1171 buffer = heap_alloc(pAuthInfo->max_token);
1173 out.BufferType = SECBUFFER_TOKEN;
1174 out.cbBuffer = pAuthInfo->max_token;
1175 out.pvBuffer = buffer;
1177 out_desc.ulVersion = 0;
1178 out_desc.cBuffers = 1;
1179 out_desc.pBuffers = &out;
1181 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1182 first ? NULL : &pAuthInfo->ctx,
1183 first ? request->server->name : NULL,
1184 context_req, 0, SECURITY_NETWORK_DREP,
1185 in.pvBuffer ? &in_desc : NULL,
1186 0, &pAuthInfo->ctx, &out_desc,
1187 &pAuthInfo->attr, &pAuthInfo->exp);
1188 if (sec_status == SEC_E_OK)
1190 pAuthInfo->finished = TRUE;
1191 pAuthInfo->auth_data = out.pvBuffer;
1192 pAuthInfo->auth_data_len = out.cbBuffer;
1193 TRACE("sending last auth packet\n");
1195 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1197 pAuthInfo->auth_data = out.pvBuffer;
1198 pAuthInfo->auth_data_len = out.cbBuffer;
1199 TRACE("sending next auth packet\n");
1201 else
1203 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1204 heap_free(out.pvBuffer);
1205 destroy_authinfo(pAuthInfo);
1206 *ppAuthInfo = NULL;
1207 return FALSE;
1211 return TRUE;
1214 /***********************************************************************
1215 * HTTP_HttpAddRequestHeadersW (internal)
1217 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1218 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1220 LPWSTR lpszStart;
1221 LPWSTR lpszEnd;
1222 LPWSTR buffer;
1223 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1225 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1227 if( dwHeaderLength == ~0U )
1228 len = strlenW(lpszHeader);
1229 else
1230 len = dwHeaderLength;
1231 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1232 lstrcpynW( buffer, lpszHeader, len + 1);
1234 lpszStart = buffer;
1238 LPWSTR * pFieldAndValue;
1240 lpszEnd = lpszStart;
1242 while (*lpszEnd != '\0')
1244 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1245 break;
1246 lpszEnd++;
1249 if (*lpszStart == '\0')
1250 break;
1252 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1254 *lpszEnd = '\0';
1255 lpszEnd++; /* Jump over newline */
1257 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1258 if (*lpszStart == '\0')
1260 /* Skip 0-length headers */
1261 lpszStart = lpszEnd;
1262 res = ERROR_SUCCESS;
1263 continue;
1265 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1266 if (pFieldAndValue)
1268 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1269 if (res == ERROR_SUCCESS)
1270 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1271 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1272 HTTP_FreeTokens(pFieldAndValue);
1275 lpszStart = lpszEnd;
1276 } while (res == ERROR_SUCCESS);
1278 heap_free(buffer);
1279 return res;
1282 /***********************************************************************
1283 * HttpAddRequestHeadersW (WININET.@)
1285 * Adds one or more HTTP header to the request handler
1287 * NOTE
1288 * On Windows if dwHeaderLength includes the trailing '\0', then
1289 * HttpAddRequestHeadersW() adds it too. However this results in an
1290 * invalid HTTP header which is rejected by some servers so we probably
1291 * don't need to match Windows on that point.
1293 * RETURNS
1294 * TRUE on success
1295 * FALSE on failure
1298 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1299 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1301 http_request_t *request;
1302 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1304 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1306 if (!lpszHeader)
1307 return TRUE;
1309 request = (http_request_t*) get_handle_object( hHttpRequest );
1310 if (request && request->hdr.htype == WH_HHTTPREQ)
1311 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1312 if( request )
1313 WININET_Release( &request->hdr );
1315 if(res != ERROR_SUCCESS)
1316 SetLastError(res);
1317 return res == ERROR_SUCCESS;
1320 /***********************************************************************
1321 * HttpAddRequestHeadersA (WININET.@)
1323 * Adds one or more HTTP header to the request handler
1325 * RETURNS
1326 * TRUE on success
1327 * FALSE on failure
1330 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1331 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1333 DWORD len;
1334 LPWSTR hdr;
1335 BOOL r;
1337 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1339 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1340 hdr = heap_alloc(len*sizeof(WCHAR));
1341 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1342 if( dwHeaderLength != ~0U )
1343 dwHeaderLength = len;
1345 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1347 heap_free( hdr );
1348 return r;
1351 static void free_accept_types( WCHAR **accept_types )
1353 WCHAR *ptr, **types = accept_types;
1355 if (!types) return;
1356 while ((ptr = *types))
1358 heap_free( ptr );
1359 types++;
1361 heap_free( accept_types );
1364 static WCHAR **convert_accept_types( const char **accept_types )
1366 unsigned int count;
1367 const char **types = accept_types;
1368 WCHAR **typesW;
1369 BOOL invalid_pointer = FALSE;
1371 if (!types) return NULL;
1372 count = 0;
1373 while (*types)
1375 __TRY
1377 /* find out how many there are */
1378 if (*types && **types)
1380 TRACE("accept type: %s\n", debugstr_a(*types));
1381 count++;
1384 __EXCEPT_PAGE_FAULT
1386 WARN("invalid accept type pointer\n");
1387 invalid_pointer = TRUE;
1389 __ENDTRY;
1390 types++;
1392 if (invalid_pointer) return NULL;
1393 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1394 count = 0;
1395 types = accept_types;
1396 while (*types)
1398 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1399 types++;
1401 typesW[count] = NULL;
1402 return typesW;
1405 /***********************************************************************
1406 * HttpOpenRequestA (WININET.@)
1408 * Open a HTTP request handle
1410 * RETURNS
1411 * HINTERNET a HTTP request handle on success
1412 * NULL on failure
1415 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1416 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1417 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1418 DWORD dwFlags, DWORD_PTR dwContext)
1420 LPWSTR szVerb = NULL, szObjectName = NULL;
1421 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1422 HINTERNET rc = FALSE;
1424 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1425 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1426 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1427 dwFlags, dwContext);
1429 if (lpszVerb)
1431 szVerb = heap_strdupAtoW(lpszVerb);
1432 if ( !szVerb )
1433 goto end;
1436 if (lpszObjectName)
1438 szObjectName = heap_strdupAtoW(lpszObjectName);
1439 if ( !szObjectName )
1440 goto end;
1443 if (lpszVersion)
1445 szVersion = heap_strdupAtoW(lpszVersion);
1446 if ( !szVersion )
1447 goto end;
1450 if (lpszReferrer)
1452 szReferrer = heap_strdupAtoW(lpszReferrer);
1453 if ( !szReferrer )
1454 goto end;
1457 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1458 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1459 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1461 end:
1462 free_accept_types(szAcceptTypes);
1463 heap_free(szReferrer);
1464 heap_free(szVersion);
1465 heap_free(szObjectName);
1466 heap_free(szVerb);
1467 return rc;
1470 /***********************************************************************
1471 * HTTP_EncodeBase64
1473 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1475 UINT n = 0, x;
1476 static const CHAR HTTP_Base64Enc[] =
1477 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1479 while( len > 0 )
1481 /* first 6 bits, all from bin[0] */
1482 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1483 x = (bin[0] & 3) << 4;
1485 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1486 if( len == 1 )
1488 base64[n++] = HTTP_Base64Enc[x];
1489 base64[n++] = '=';
1490 base64[n++] = '=';
1491 break;
1493 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1494 x = ( bin[1] & 0x0f ) << 2;
1496 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1497 if( len == 2 )
1499 base64[n++] = HTTP_Base64Enc[x];
1500 base64[n++] = '=';
1501 break;
1503 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1505 /* last 6 bits, all from bin [2] */
1506 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1507 bin += 3;
1508 len -= 3;
1510 base64[n] = 0;
1511 return n;
1514 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1515 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1516 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1517 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1518 static const signed char HTTP_Base64Dec[256] =
1520 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1521 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1522 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1523 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1524 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1525 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1526 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1527 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1528 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1529 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1530 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1531 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1532 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1533 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1534 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1535 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1536 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1537 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1538 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1539 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1540 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1541 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1542 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1543 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1544 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1545 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1547 #undef CH
1549 /***********************************************************************
1550 * HTTP_DecodeBase64
1552 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1554 unsigned int n = 0;
1556 while(*base64)
1558 signed char in[4];
1560 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1561 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1562 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1563 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1565 WARN("invalid base64: %s\n", debugstr_w(base64));
1566 return 0;
1568 if (bin)
1569 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1570 n++;
1572 if ((base64[2] == '=') && (base64[3] == '='))
1573 break;
1574 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1575 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1577 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1578 return 0;
1580 if (bin)
1581 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1582 n++;
1584 if (base64[3] == '=')
1585 break;
1586 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1587 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1589 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1590 return 0;
1592 if (bin)
1593 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1594 n++;
1596 base64 += 4;
1599 return n;
1602 /***********************************************************************
1603 * HTTP_InsertAuthorization
1605 * Insert or delete the authorization field in the request header.
1607 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1609 if (pAuthInfo)
1611 static const WCHAR wszSpace[] = {' ',0};
1612 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1613 unsigned int len;
1614 WCHAR *authorization = NULL;
1616 if (pAuthInfo->auth_data_len)
1618 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1619 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1620 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1621 if (!authorization)
1622 return FALSE;
1624 strcpyW(authorization, pAuthInfo->scheme);
1625 strcatW(authorization, wszSpace);
1626 HTTP_EncodeBase64(pAuthInfo->auth_data,
1627 pAuthInfo->auth_data_len,
1628 authorization+strlenW(authorization));
1630 /* clear the data as it isn't valid now that it has been sent to the
1631 * server, unless it's Basic authentication which doesn't do
1632 * connection tracking */
1633 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1635 heap_free(pAuthInfo->auth_data);
1636 pAuthInfo->auth_data = NULL;
1637 pAuthInfo->auth_data_len = 0;
1641 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1643 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1644 heap_free(authorization);
1646 return TRUE;
1649 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1651 static const WCHAR slash[] = { '/',0 };
1652 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1653 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1654 http_session_t *session = req->session;
1655 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1656 DWORD size;
1658 size = sizeof(new_location);
1659 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1661 URL_COMPONENTSW UrlComponents;
1663 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1664 strcpyW( url, new_location );
1666 ZeroMemory(&UrlComponents,sizeof(URL_COMPONENTSW));
1667 if(InternetCrackUrlW(url, 0, 0, &UrlComponents)) goto done;
1668 heap_free(url);
1671 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1672 size += strlenW( session->hostName ) + strlenW( req->path );
1674 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1676 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1677 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1678 else
1679 sprintfW( url, format, session->hostName, session->hostPort );
1680 if (req->path[0] != '/') strcatW( url, slash );
1681 strcatW( url, req->path );
1683 done:
1684 TRACE("url=%s\n", debugstr_w(url));
1685 return url;
1688 /***********************************************************************
1689 * HTTP_DealWithProxy
1691 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1693 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1694 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1695 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1696 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1697 static WCHAR szNul[] = { 0 };
1698 URL_COMPONENTSW UrlComponents;
1699 server_t *new_server;
1700 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1701 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1702 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1704 memset( &UrlComponents, 0, sizeof UrlComponents );
1705 UrlComponents.dwStructSize = sizeof UrlComponents;
1706 UrlComponents.lpszHostName = buf;
1707 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1709 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1710 return FALSE;
1711 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1712 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1713 sprintfW(proxy, szFormat, protoProxy);
1714 else
1715 strcpyW(proxy, protoProxy);
1716 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1717 return FALSE;
1718 if( UrlComponents.dwHostNameLength == 0 )
1719 return FALSE;
1721 if( !request->path )
1722 request->path = szNul;
1724 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1725 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1727 new_server = get_server(UrlComponents.lpszHostName, UrlComponents.nPort, TRUE);
1728 if(!new_server)
1729 return FALSE;
1731 server_release(request->server);
1732 request->server = new_server;
1734 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1735 return TRUE;
1738 static DWORD HTTP_ResolveName(http_request_t *request)
1740 server_t *server = request->server;
1741 socklen_t addr_len;
1742 const void *addr;
1744 if(server->addr_len)
1745 return ERROR_SUCCESS;
1747 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1748 INTERNET_STATUS_RESOLVING_NAME,
1749 server->name,
1750 (strlenW(server->name)+1) * sizeof(WCHAR));
1752 addr_len = sizeof(server->addr);
1753 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1754 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1756 switch(server->addr.ss_family) {
1757 case AF_INET:
1758 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1759 break;
1760 case AF_INET6:
1761 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1762 break;
1763 default:
1764 WARN("unsupported family %d\n", server->addr.ss_family);
1765 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1768 server->addr_len = addr_len;
1769 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1770 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1771 INTERNET_STATUS_NAME_RESOLVED,
1772 server->addr_str, strlen(server->addr_str)+1);
1774 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1775 return ERROR_SUCCESS;
1778 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1780 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1781 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1782 static const WCHAR slash[] = { '/',0 };
1783 LPHTTPHEADERW host_header;
1784 LPCWSTR scheme;
1786 host_header = HTTP_GetHeader(req, hostW);
1787 if(!host_header)
1788 return FALSE;
1790 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1791 scheme = https;
1792 else
1793 scheme = http;
1794 strcpyW(buf, scheme);
1795 strcatW(buf, host_header->lpszValue);
1796 if (req->path[0] != '/')
1797 strcatW(buf, slash);
1798 strcatW(buf, req->path);
1799 return TRUE;
1803 /***********************************************************************
1804 * HTTPREQ_Destroy (internal)
1806 * Deallocate request handle
1809 static void HTTPREQ_Destroy(object_header_t *hdr)
1811 http_request_t *request = (http_request_t*) hdr;
1812 DWORD i;
1814 TRACE("\n");
1816 if(request->hCacheFile) {
1817 WCHAR url[INTERNET_MAX_URL_LENGTH];
1819 CloseHandle(request->hCacheFile);
1821 if(HTTP_GetRequestURL(request, url)) {
1822 DWORD headersLen;
1824 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1825 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1826 request->last_modified, NORMAL_CACHE_ENTRY,
1827 request->rawHeaders, headersLen, NULL, 0);
1830 heap_free(request->cacheFile);
1832 request->read_section.DebugInfo->Spare[0] = 0;
1833 DeleteCriticalSection( &request->read_section );
1834 WININET_Release(&request->session->hdr);
1836 destroy_authinfo(request->authInfo);
1837 destroy_authinfo(request->proxyAuthInfo);
1839 if(request->server)
1840 server_release(request->server);
1842 heap_free(request->path);
1843 heap_free(request->verb);
1844 heap_free(request->rawHeaders);
1845 heap_free(request->version);
1846 heap_free(request->statusText);
1848 for (i = 0; i < request->nCustHeaders; i++)
1850 heap_free(request->custHeaders[i].lpszField);
1851 heap_free(request->custHeaders[i].lpszValue);
1853 destroy_data_stream(request->data_stream);
1854 heap_free(request->custHeaders);
1857 static void http_release_netconn(http_request_t *req, BOOL reuse)
1859 TRACE("%p %p\n",req, req->netconn);
1861 if(!req->netconn)
1862 return;
1864 if(reuse && req->netconn->keep_alive) {
1865 BOOL run_collector;
1867 EnterCriticalSection(&connection_pool_cs);
1869 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1870 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1871 req->netconn = NULL;
1873 run_collector = !collector_running;
1874 collector_running = TRUE;
1876 LeaveCriticalSection(&connection_pool_cs);
1878 if(run_collector) {
1879 HANDLE thread = NULL;
1880 HMODULE module;
1882 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1883 if(module)
1884 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1885 if(!thread) {
1886 EnterCriticalSection(&connection_pool_cs);
1887 collector_running = FALSE;
1888 LeaveCriticalSection(&connection_pool_cs);
1890 if(module)
1891 FreeLibrary(module);
1893 else
1894 CloseHandle(thread);
1896 return;
1899 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1900 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1902 free_netconn(req->netconn);
1903 req->netconn = NULL;
1905 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1906 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1909 static void drain_content(http_request_t *req)
1911 BOOL try_reuse;
1913 if (!req->netconn) return;
1915 if (req->contentLength == -1)
1916 try_reuse = FALSE;
1917 else if(!strcmpW(req->verb, szHEAD))
1918 try_reuse = TRUE;
1919 else
1920 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1922 http_release_netconn(req, try_reuse);
1925 static BOOL HTTP_KeepAlive(http_request_t *request)
1927 WCHAR szVersion[10];
1928 WCHAR szConnectionResponse[20];
1929 DWORD dwBufferSize = sizeof(szVersion);
1930 BOOL keepalive = FALSE;
1932 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1933 * the connection is keep-alive by default */
1934 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1935 && !strcmpiW(szVersion, g_szHttp1_1))
1937 keepalive = TRUE;
1940 dwBufferSize = sizeof(szConnectionResponse);
1941 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1942 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1944 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1947 return keepalive;
1950 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1952 http_request_t *req = (http_request_t*)hdr;
1954 drain_content(req);
1957 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1959 http_request_t *req = (http_request_t*)hdr;
1961 switch(option) {
1962 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1964 http_session_t *session = req->session;
1965 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1967 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1969 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1970 return ERROR_INSUFFICIENT_BUFFER;
1971 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1972 /* FIXME: can't get a SOCKET from our connection since we don't use
1973 * winsock
1975 info->Socket = 0;
1976 /* FIXME: get source port from req->netConnection */
1977 info->SourcePort = 0;
1978 info->DestPort = session->hostPort;
1979 info->Flags = 0;
1980 if (HTTP_KeepAlive(req))
1981 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1982 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1983 info->Flags |= IDSI_FLAG_PROXY;
1984 if (req->netconn->useSSL)
1985 info->Flags |= IDSI_FLAG_SECURE;
1987 return ERROR_SUCCESS;
1990 case 98:
1991 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1992 /* fall through */
1993 case INTERNET_OPTION_SECURITY_FLAGS:
1995 DWORD flags;
1997 if (*size < sizeof(ULONG))
1998 return ERROR_INSUFFICIENT_BUFFER;
2000 *size = sizeof(DWORD);
2001 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
2002 *(DWORD *)buffer = flags;
2004 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
2005 return ERROR_SUCCESS;
2008 case INTERNET_OPTION_HANDLE_TYPE:
2009 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2011 if (*size < sizeof(ULONG))
2012 return ERROR_INSUFFICIENT_BUFFER;
2014 *size = sizeof(DWORD);
2015 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2016 return ERROR_SUCCESS;
2018 case INTERNET_OPTION_URL: {
2019 WCHAR url[INTERNET_MAX_URL_LENGTH];
2020 HTTPHEADERW *host;
2021 DWORD len;
2022 WCHAR *pch;
2024 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2026 TRACE("INTERNET_OPTION_URL\n");
2028 host = HTTP_GetHeader(req, hostW);
2029 strcpyW(url, httpW);
2030 strcatW(url, host->lpszValue);
2031 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2032 *pch = 0;
2033 strcatW(url, req->path);
2035 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2037 if(unicode) {
2038 len = (strlenW(url)+1) * sizeof(WCHAR);
2039 if(*size < len)
2040 return ERROR_INSUFFICIENT_BUFFER;
2042 *size = len;
2043 strcpyW(buffer, url);
2044 return ERROR_SUCCESS;
2045 }else {
2046 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2047 if(len > *size)
2048 return ERROR_INSUFFICIENT_BUFFER;
2050 *size = len;
2051 return ERROR_SUCCESS;
2055 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2056 INTERNET_CACHE_ENTRY_INFOW *info;
2057 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2058 WCHAR url[INTERNET_MAX_URL_LENGTH];
2059 DWORD nbytes, error;
2060 BOOL ret;
2062 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2064 if (*size < sizeof(*ts))
2066 *size = sizeof(*ts);
2067 return ERROR_INSUFFICIENT_BUFFER;
2069 nbytes = 0;
2070 HTTP_GetRequestURL(req, url);
2071 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2072 error = GetLastError();
2073 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2075 if (!(info = heap_alloc(nbytes)))
2076 return ERROR_OUTOFMEMORY;
2078 GetUrlCacheEntryInfoW(url, info, &nbytes);
2080 ts->ftExpires = info->ExpireTime;
2081 ts->ftLastModified = info->LastModifiedTime;
2083 heap_free(info);
2084 *size = sizeof(*ts);
2085 return ERROR_SUCCESS;
2087 return error;
2090 case INTERNET_OPTION_DATAFILE_NAME: {
2091 DWORD req_size;
2093 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2095 if(!req->cacheFile) {
2096 *size = 0;
2097 return ERROR_INTERNET_ITEM_NOT_FOUND;
2100 if(unicode) {
2101 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2102 if(*size < req_size)
2103 return ERROR_INSUFFICIENT_BUFFER;
2105 *size = req_size;
2106 memcpy(buffer, req->cacheFile, *size);
2107 return ERROR_SUCCESS;
2108 }else {
2109 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2110 if (req_size > *size)
2111 return ERROR_INSUFFICIENT_BUFFER;
2113 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2114 -1, buffer, *size, NULL, NULL);
2115 return ERROR_SUCCESS;
2119 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2120 PCCERT_CONTEXT context;
2122 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2123 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2124 return ERROR_INSUFFICIENT_BUFFER;
2127 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2128 if(context) {
2129 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2130 DWORD len;
2132 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2133 info->ftExpiry = context->pCertInfo->NotAfter;
2134 info->ftStart = context->pCertInfo->NotBefore;
2135 len = CertNameToStrA(context->dwCertEncodingType,
2136 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2137 info->lpszSubjectInfo = LocalAlloc(0, len);
2138 if(info->lpszSubjectInfo)
2139 CertNameToStrA(context->dwCertEncodingType,
2140 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2141 info->lpszSubjectInfo, len);
2142 len = CertNameToStrA(context->dwCertEncodingType,
2143 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2144 info->lpszIssuerInfo = LocalAlloc(0, len);
2145 if(info->lpszIssuerInfo)
2146 CertNameToStrA(context->dwCertEncodingType,
2147 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2148 info->lpszIssuerInfo, len);
2149 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2150 CertFreeCertificateContext(context);
2151 return ERROR_SUCCESS;
2154 case INTERNET_OPTION_CONNECT_TIMEOUT:
2155 if (*size < sizeof(DWORD))
2156 return ERROR_INSUFFICIENT_BUFFER;
2158 *size = sizeof(DWORD);
2159 *(DWORD *)buffer = req->connect_timeout;
2160 return ERROR_SUCCESS;
2161 case INTERNET_OPTION_REQUEST_FLAGS: {
2162 DWORD flags = 0;
2164 if (*size < sizeof(DWORD))
2165 return ERROR_INSUFFICIENT_BUFFER;
2167 /* FIXME: Add support for:
2168 * INTERNET_REQFLAG_FROM_CACHE
2169 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2172 if(req->session->appInfo->proxy)
2173 flags |= INTERNET_REQFLAG_VIA_PROXY;
2174 if(!req->rawHeaders)
2175 flags |= INTERNET_REQFLAG_NO_HEADERS;
2177 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2179 *size = sizeof(DWORD);
2180 *(DWORD*)buffer = flags;
2181 return ERROR_SUCCESS;
2185 return INET_QueryOption(hdr, option, buffer, size, unicode);
2188 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2190 http_request_t *req = (http_request_t*)hdr;
2192 switch(option) {
2193 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2194 TRACE("Undocumented option 99\n");
2196 if (!buffer || size != sizeof(DWORD))
2197 return ERROR_INVALID_PARAMETER;
2198 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2199 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2201 /* fall through */
2202 case INTERNET_OPTION_SECURITY_FLAGS:
2204 DWORD flags;
2206 if (!buffer || size != sizeof(DWORD))
2207 return ERROR_INVALID_PARAMETER;
2208 flags = *(DWORD *)buffer;
2209 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2210 flags &= SECURITY_SET_MASK;
2211 req->security_flags |= flags;
2212 if(req->netconn)
2213 req->netconn->security_flags |= flags;
2214 return ERROR_SUCCESS;
2216 case INTERNET_OPTION_CONNECT_TIMEOUT:
2217 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2218 req->connect_timeout = *(DWORD *)buffer;
2219 return ERROR_SUCCESS;
2221 case INTERNET_OPTION_SEND_TIMEOUT:
2222 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2223 req->send_timeout = *(DWORD *)buffer;
2224 return ERROR_SUCCESS;
2226 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2227 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2228 req->receive_timeout = *(DWORD *)buffer;
2229 return ERROR_SUCCESS;
2231 case INTERNET_OPTION_USERNAME:
2232 heap_free(req->session->userName);
2233 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2234 return ERROR_SUCCESS;
2236 case INTERNET_OPTION_PASSWORD:
2237 heap_free(req->session->password);
2238 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2239 return ERROR_SUCCESS;
2240 case INTERNET_OPTION_HTTP_DECODING:
2241 if(size != sizeof(BOOL))
2242 return ERROR_INVALID_PARAMETER;
2243 req->decoding = *(BOOL*)buffer;
2244 return ERROR_SUCCESS;
2247 return INET_SetOption(hdr, option, buffer, size);
2250 /* read some more data into the read buffer (the read section must be held) */
2251 static DWORD read_more_data( http_request_t *req, int maxlen )
2253 DWORD res;
2254 int len;
2256 if (req->read_pos)
2258 /* move existing data to the start of the buffer */
2259 if(req->read_size)
2260 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2261 req->read_pos = 0;
2264 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2266 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2267 maxlen - req->read_size, 0, &len );
2268 if(res == ERROR_SUCCESS)
2269 req->read_size += len;
2271 return res;
2274 /* remove some amount of data from the read buffer (the read section must be held) */
2275 static void remove_data( http_request_t *req, int count )
2277 if (!(req->read_size -= count)) req->read_pos = 0;
2278 else req->read_pos += count;
2281 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2283 int count, bytes_read, pos = 0;
2284 DWORD res;
2286 EnterCriticalSection( &req->read_section );
2287 for (;;)
2289 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2291 if (eol)
2293 count = eol - (req->read_buf + req->read_pos);
2294 bytes_read = count + 1;
2296 else count = bytes_read = req->read_size;
2298 count = min( count, *len - pos );
2299 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2300 pos += count;
2301 remove_data( req, bytes_read );
2302 if (eol) break;
2304 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2306 *len = 0;
2307 TRACE( "returning empty string %u\n", res);
2308 LeaveCriticalSection( &req->read_section );
2309 INTERNET_SetLastError(res);
2310 return FALSE;
2313 LeaveCriticalSection( &req->read_section );
2315 if (pos < *len)
2317 if (pos && buffer[pos - 1] == '\r') pos--;
2318 *len = pos + 1;
2320 buffer[*len - 1] = 0;
2321 TRACE( "returning %s\n", debugstr_a(buffer));
2322 return TRUE;
2325 /* check if we have reached the end of the data to read (the read section must be held) */
2326 static BOOL end_of_read_data( http_request_t *req )
2328 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2331 /* fetch some more data into the read buffer (the read section must be held) */
2332 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2334 DWORD res, read=0;
2336 if(req->read_size == sizeof(req->read_buf))
2337 return ERROR_SUCCESS;
2339 if(req->read_pos) {
2340 if(req->read_size)
2341 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2342 req->read_pos = 0;
2345 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2346 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2347 req->read_size += read;
2349 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2350 if(read_bytes)
2351 *read_bytes = read;
2352 return res;
2355 /* return the size of data available to be read immediately (the read section must be held) */
2356 static DWORD get_avail_data( http_request_t *req )
2358 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2361 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2363 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2364 DWORD avail = 0;
2366 if(req->netconn)
2367 NETCON_query_data_available(req->netconn, &avail);
2368 return netconn_stream->content_length == ~0u
2369 ? avail
2370 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2373 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2375 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2376 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2379 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2380 DWORD *read, read_mode_t read_mode)
2382 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2383 int len = 0;
2385 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2387 if(read_mode == READMODE_NOBLOCK)
2388 size = min(size, netconn_get_avail_data(stream, req));
2390 if(size && req->netconn) {
2391 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2392 len = 0;
2393 if(!len)
2394 netconn_stream->content_length = netconn_stream->content_read;
2397 netconn_stream->content_read += *read = len;
2398 TRACE("read %u bytes\n", len);
2399 return ERROR_SUCCESS;
2402 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2404 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2405 BYTE buf[1024];
2406 DWORD avail;
2407 int len;
2409 if(netconn_end_of_data(stream, req))
2410 return TRUE;
2412 do {
2413 avail = netconn_get_avail_data(stream, req);
2414 if(!avail)
2415 return FALSE;
2417 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2418 return FALSE;
2420 netconn_stream->content_read += len;
2421 }while(netconn_stream->content_read < netconn_stream->content_length);
2423 return TRUE;
2426 static void netconn_destroy(data_stream_t *stream)
2430 static const data_stream_vtbl_t netconn_stream_vtbl = {
2431 netconn_get_avail_data,
2432 netconn_end_of_data,
2433 netconn_read,
2434 netconn_drain_content,
2435 netconn_destroy
2438 /* read some more data into the read buffer (the read section must be held) */
2439 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2441 DWORD res;
2442 int len;
2444 if (stream->buf_pos)
2446 /* move existing data to the start of the buffer */
2447 if(stream->buf_size)
2448 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2449 stream->buf_pos = 0;
2452 if (maxlen == -1) maxlen = sizeof(stream->buf);
2454 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2455 maxlen - stream->buf_size, 0, &len );
2456 if(res == ERROR_SUCCESS)
2457 stream->buf_size += len;
2459 return res;
2462 /* remove some amount of data from the read buffer (the read section must be held) */
2463 static void remove_chunked_data(chunked_stream_t *stream, int count)
2465 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2466 else stream->buf_pos += count;
2469 /* discard data contents until we reach end of line (the read section must be held) */
2470 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2472 DWORD res;
2476 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2477 if (eol)
2479 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2480 break;
2482 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2483 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2484 } while (stream->buf_size);
2485 return ERROR_SUCCESS;
2488 /* read the size of the next chunk (the read section must be held) */
2489 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2491 /* TODOO */
2492 DWORD chunk_size = 0, res;
2494 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2495 return res;
2497 for (;;)
2499 while (stream->buf_size)
2501 char ch = stream->buf[stream->buf_pos];
2502 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2503 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2504 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2505 else if (ch == ';' || ch == '\r' || ch == '\n')
2507 TRACE( "reading %u byte chunk\n", chunk_size );
2508 stream->chunk_size = chunk_size;
2509 req->contentLength += chunk_size;
2510 return discard_chunked_eol(stream, req);
2512 remove_chunked_data(stream, 1);
2514 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2515 if (!stream->buf_size)
2517 stream->chunk_size = 0;
2518 return ERROR_SUCCESS;
2523 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2525 /* Allow reading only from read buffer */
2526 return 0;
2529 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2531 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2532 return !chunked_stream->chunk_size;
2535 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2536 DWORD *read, read_mode_t read_mode)
2538 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2539 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2541 if(chunked_stream->chunk_size == ~0u) {
2542 res = start_next_chunk(chunked_stream, req);
2543 if(res != ERROR_SUCCESS)
2544 return res;
2547 while(size && chunked_stream->chunk_size) {
2548 if(chunked_stream->buf_size) {
2549 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2551 /* this could block */
2552 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2553 break;
2555 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2556 remove_chunked_data(chunked_stream, read_bytes);
2557 }else {
2558 read_bytes = min(size, chunked_stream->chunk_size);
2560 if(read_mode == READMODE_NOBLOCK) {
2561 DWORD avail;
2563 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2564 break;
2565 if(read_bytes > avail)
2566 read_bytes = avail;
2568 /* this could block */
2569 if(read_bytes == chunked_stream->chunk_size)
2570 break;
2573 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2574 if(res != ERROR_SUCCESS)
2575 break;
2578 chunked_stream->chunk_size -= read_bytes;
2579 size -= read_bytes;
2580 ret_read += read_bytes;
2581 if(!chunked_stream->chunk_size) {
2582 assert(read_mode != READMODE_NOBLOCK);
2583 res = start_next_chunk(chunked_stream, req);
2584 if(res != ERROR_SUCCESS)
2585 break;
2588 if(read_mode == READMODE_ASYNC)
2589 read_mode = READMODE_NOBLOCK;
2592 TRACE("read %u bytes\n", ret_read);
2593 *read = ret_read;
2594 return res;
2597 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2599 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2601 /* FIXME: we can do better */
2602 return !chunked_stream->chunk_size;
2605 static void chunked_destroy(data_stream_t *stream)
2607 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2608 heap_free(chunked_stream);
2611 static const data_stream_vtbl_t chunked_stream_vtbl = {
2612 chunked_get_avail_data,
2613 chunked_end_of_data,
2614 chunked_read,
2615 chunked_drain_content,
2616 chunked_destroy
2619 /* set the request content length based on the headers */
2620 static DWORD set_content_length(http_request_t *request)
2622 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2623 WCHAR encoding[20];
2624 DWORD size;
2626 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2627 request->contentLength = request->netconn_stream.content_length = 0;
2628 return ERROR_SUCCESS;
2631 size = sizeof(request->contentLength);
2632 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2633 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2634 request->contentLength = ~0u;
2635 request->netconn_stream.content_length = request->contentLength;
2636 request->netconn_stream.content_read = request->read_size;
2638 size = sizeof(encoding);
2639 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2640 !strcmpiW(encoding, szChunked))
2642 chunked_stream_t *chunked_stream;
2644 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2645 if(!chunked_stream)
2646 return ERROR_OUTOFMEMORY;
2648 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2649 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2650 chunked_stream->chunk_size = ~0u;
2652 if(request->read_size) {
2653 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2654 chunked_stream->buf_size = request->read_size;
2655 request->read_size = request->read_pos = 0;
2658 request->data_stream = &chunked_stream->data_stream;
2659 request->contentLength = ~0u;
2660 request->read_chunked = TRUE;
2663 if(request->decoding) {
2664 int encoding_idx;
2666 static const WCHAR gzipW[] = {'g','z','i','p',0};
2668 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2669 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2670 return init_gzip_stream(request);
2673 return ERROR_SUCCESS;
2676 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2678 INTERNET_ASYNC_RESULT iar;
2680 iar.dwResult = result;
2681 iar.dwError = error;
2683 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2684 sizeof(INTERNET_ASYNC_RESULT));
2687 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2689 DWORD res, read = 0, avail = 0;
2690 read_mode_t mode;
2692 TRACE("%p\n", req);
2694 EnterCriticalSection( &req->read_section );
2696 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2697 res = refill_read_buffer(req, mode, &read);
2698 if(res == ERROR_SUCCESS && !first_notif)
2699 avail = get_avail_data(req);
2701 LeaveCriticalSection( &req->read_section );
2703 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2704 WARN("res %u read %u, closing connection\n", res, read);
2705 http_release_netconn(req, FALSE);
2708 if(res == ERROR_SUCCESS)
2709 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2710 else
2711 send_request_complete(req, 0, res);
2714 /* read data from the http connection (the read section must be held) */
2715 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2717 DWORD current_read = 0, ret_read = 0;
2718 read_mode_t read_mode;
2719 DWORD res = ERROR_SUCCESS;
2721 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2723 EnterCriticalSection( &req->read_section );
2725 if(req->read_size) {
2726 ret_read = min(size, req->read_size);
2727 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2728 req->read_size -= ret_read;
2729 req->read_pos += ret_read;
2730 if(read_mode == READMODE_ASYNC)
2731 read_mode = READMODE_NOBLOCK;
2734 if(ret_read < size) {
2735 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2736 ret_read += current_read;
2739 LeaveCriticalSection( &req->read_section );
2741 *read = ret_read;
2742 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2744 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2745 BOOL res;
2746 DWORD written;
2748 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2749 if(!res)
2750 WARN("WriteFile failed: %u\n", GetLastError());
2753 if(size && !ret_read)
2754 http_release_netconn(req, res == ERROR_SUCCESS);
2756 return res;
2760 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2762 http_request_t *req = (http_request_t*)hdr;
2763 DWORD res;
2765 EnterCriticalSection( &req->read_section );
2766 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2767 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2769 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2770 if(res == ERROR_SUCCESS)
2771 res = hdr->dwError;
2772 LeaveCriticalSection( &req->read_section );
2774 return res;
2777 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2779 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2780 http_request_t *req = (http_request_t*)workRequest->hdr;
2781 DWORD res;
2783 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2785 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2786 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2788 send_request_complete(req, res == ERROR_SUCCESS, res);
2791 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2792 DWORD flags, DWORD_PTR context)
2794 http_request_t *req = (http_request_t*)hdr;
2795 DWORD res, size, read, error = ERROR_SUCCESS;
2797 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2798 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2800 if (buffers->dwStructSize != sizeof(*buffers))
2801 return ERROR_INVALID_PARAMETER;
2803 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2805 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2807 WORKREQUEST workRequest;
2809 if (TryEnterCriticalSection( &req->read_section ))
2811 if (get_avail_data(req))
2813 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2814 &buffers->dwBufferLength, FALSE);
2815 size = buffers->dwBufferLength;
2816 LeaveCriticalSection( &req->read_section );
2817 goto done;
2819 LeaveCriticalSection( &req->read_section );
2822 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2823 workRequest.hdr = WININET_AddRef(&req->hdr);
2824 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2826 INTERNET_AsyncCall(&workRequest);
2828 return ERROR_IO_PENDING;
2831 read = 0;
2832 size = buffers->dwBufferLength;
2834 EnterCriticalSection( &req->read_section );
2835 if(hdr->dwError == ERROR_SUCCESS)
2836 hdr->dwError = INTERNET_HANDLE_IN_USE;
2837 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2838 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2840 while(1) {
2841 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2842 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2843 if(res != ERROR_SUCCESS)
2844 break;
2846 read += buffers->dwBufferLength;
2847 if(read == size || end_of_read_data(req))
2848 break;
2850 LeaveCriticalSection( &req->read_section );
2852 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2853 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2854 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2855 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2857 EnterCriticalSection( &req->read_section );
2860 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2861 hdr->dwError = ERROR_SUCCESS;
2862 else
2863 error = hdr->dwError;
2865 LeaveCriticalSection( &req->read_section );
2866 size = buffers->dwBufferLength;
2867 buffers->dwBufferLength = read;
2869 done:
2870 if (res == ERROR_SUCCESS) {
2871 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2872 &size, sizeof(size));
2875 return res==ERROR_SUCCESS ? error : res;
2878 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2880 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2881 http_request_t *req = (http_request_t*)workRequest->hdr;
2882 DWORD res;
2884 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2886 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2887 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2889 send_request_complete(req, res == ERROR_SUCCESS, res);
2892 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2893 DWORD flags, DWORD_PTR context)
2896 http_request_t *req = (http_request_t*)hdr;
2897 DWORD res, size, read, error = ERROR_SUCCESS;
2899 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2900 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2902 if (buffers->dwStructSize != sizeof(*buffers))
2903 return ERROR_INVALID_PARAMETER;
2905 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2907 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2909 WORKREQUEST workRequest;
2911 if (TryEnterCriticalSection( &req->read_section ))
2913 if (get_avail_data(req))
2915 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2916 &buffers->dwBufferLength, FALSE);
2917 size = buffers->dwBufferLength;
2918 LeaveCriticalSection( &req->read_section );
2919 goto done;
2921 LeaveCriticalSection( &req->read_section );
2924 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2925 workRequest.hdr = WININET_AddRef(&req->hdr);
2926 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2928 INTERNET_AsyncCall(&workRequest);
2930 return ERROR_IO_PENDING;
2933 read = 0;
2934 size = buffers->dwBufferLength;
2936 EnterCriticalSection( &req->read_section );
2937 if(hdr->dwError == ERROR_SUCCESS)
2938 hdr->dwError = INTERNET_HANDLE_IN_USE;
2939 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2940 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2942 while(1) {
2943 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2944 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2945 if(res != ERROR_SUCCESS)
2946 break;
2948 read += buffers->dwBufferLength;
2949 if(read == size || end_of_read_data(req))
2950 break;
2952 LeaveCriticalSection( &req->read_section );
2954 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2955 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2956 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2957 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2959 EnterCriticalSection( &req->read_section );
2962 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2963 hdr->dwError = ERROR_SUCCESS;
2964 else
2965 error = hdr->dwError;
2967 LeaveCriticalSection( &req->read_section );
2968 size = buffers->dwBufferLength;
2969 buffers->dwBufferLength = read;
2971 done:
2972 if (res == ERROR_SUCCESS) {
2973 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2974 &size, sizeof(size));
2977 return res==ERROR_SUCCESS ? error : res;
2980 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2982 DWORD res;
2983 http_request_t *request = (http_request_t*)hdr;
2985 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2987 *written = 0;
2988 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2989 if (res == ERROR_SUCCESS)
2990 request->bytesWritten += *written;
2992 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2993 return res;
2996 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2998 http_request_t *req = (http_request_t*)workRequest->hdr;
3000 HTTP_ReceiveRequestData(req, FALSE);
3003 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3005 http_request_t *req = (http_request_t*)hdr;
3007 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3009 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3011 WORKREQUEST workRequest;
3013 /* never wait, if we can't enter the section we queue an async request right away */
3014 if (TryEnterCriticalSection( &req->read_section ))
3016 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3017 if ((*available = get_avail_data( req ))) goto done;
3018 if (end_of_read_data( req )) goto done;
3019 LeaveCriticalSection( &req->read_section );
3022 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3023 workRequest.hdr = WININET_AddRef( &req->hdr );
3025 INTERNET_AsyncCall(&workRequest);
3027 return ERROR_IO_PENDING;
3030 EnterCriticalSection( &req->read_section );
3032 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3034 refill_read_buffer( req, READMODE_ASYNC, NULL );
3035 *available = get_avail_data( req );
3038 done:
3039 LeaveCriticalSection( &req->read_section );
3041 TRACE( "returning %u\n", *available );
3042 return ERROR_SUCCESS;
3045 static const object_vtbl_t HTTPREQVtbl = {
3046 HTTPREQ_Destroy,
3047 HTTPREQ_CloseConnection,
3048 HTTPREQ_QueryOption,
3049 HTTPREQ_SetOption,
3050 HTTPREQ_ReadFile,
3051 HTTPREQ_ReadFileExA,
3052 HTTPREQ_ReadFileExW,
3053 HTTPREQ_WriteFile,
3054 HTTPREQ_QueryDataAvailable,
3055 NULL
3058 /***********************************************************************
3059 * HTTP_HttpOpenRequestW (internal)
3061 * Open a HTTP request handle
3063 * RETURNS
3064 * HINTERNET a HTTP request handle on success
3065 * NULL on failure
3068 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3069 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3070 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3071 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3073 appinfo_t *hIC = session->appInfo;
3074 http_request_t *request;
3075 INTERNET_PORT port;
3076 DWORD len, res = ERROR_SUCCESS;
3078 TRACE("-->\n");
3080 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3081 if(!request)
3082 return ERROR_OUTOFMEMORY;
3084 request->hdr.htype = WH_HHTTPREQ;
3085 request->hdr.dwFlags = dwFlags;
3086 request->hdr.dwContext = dwContext;
3087 request->contentLength = ~0u;
3089 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3090 request->data_stream = &request->netconn_stream.data_stream;
3091 request->connect_timeout = session->connect_timeout;
3092 request->send_timeout = session->send_timeout;
3093 request->receive_timeout = session->receive_timeout;
3095 InitializeCriticalSection( &request->read_section );
3096 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3098 WININET_AddRef( &session->hdr );
3099 request->session = session;
3100 list_add_head( &session->hdr.children, &request->hdr.entry );
3102 port = session->hostPort;
3103 if(port == INTERNET_INVALID_PORT_NUMBER)
3104 port = dwFlags & INTERNET_FLAG_SECURE ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3106 request->server = get_server(session->hostName, port, TRUE);
3107 if(!request->server) {
3108 WININET_Release(&request->hdr);
3109 return ERROR_OUTOFMEMORY;
3112 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3113 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3114 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3115 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3117 if (lpszObjectName && *lpszObjectName) {
3118 HRESULT rc;
3120 len = 0;
3121 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3122 if (rc != E_POINTER)
3123 len = strlenW(lpszObjectName)+1;
3124 request->path = heap_alloc(len*sizeof(WCHAR));
3125 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3126 URL_ESCAPE_SPACES_ONLY);
3127 if (rc != S_OK)
3129 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3130 strcpyW(request->path,lpszObjectName);
3132 }else {
3133 static const WCHAR slashW[] = {'/',0};
3135 request->path = heap_strdupW(slashW);
3138 if (lpszReferrer && *lpszReferrer)
3139 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3141 if (lpszAcceptTypes)
3143 int i;
3144 for (i = 0; lpszAcceptTypes[i]; i++)
3146 if (!*lpszAcceptTypes[i]) continue;
3147 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3148 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3149 HTTP_ADDHDR_FLAG_REQ |
3150 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3154 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3155 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3157 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3158 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3159 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3161 WCHAR *host_name;
3163 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3165 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3166 if (!host_name) {
3167 res = ERROR_OUTOFMEMORY;
3168 goto lend;
3171 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3172 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3173 heap_free(host_name);
3175 else
3176 HTTP_ProcessHeader(request, hostW, session->hostName,
3177 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3179 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3180 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3181 INTERNET_DEFAULT_HTTPS_PORT :
3182 INTERNET_DEFAULT_HTTP_PORT);
3184 if (hIC->proxy && hIC->proxy[0])
3185 HTTP_DealWithProxy( hIC, session, request );
3187 INTERNET_SendCallback(&session->hdr, dwContext,
3188 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3189 sizeof(HINTERNET));
3191 lend:
3192 TRACE("<-- %u (%p)\n", res, request);
3194 if(res != ERROR_SUCCESS) {
3195 WININET_Release( &request->hdr );
3196 *ret = NULL;
3197 return res;
3200 *ret = request->hdr.hInternet;
3201 return ERROR_SUCCESS;
3204 /***********************************************************************
3205 * HttpOpenRequestW (WININET.@)
3207 * Open a HTTP request handle
3209 * RETURNS
3210 * HINTERNET a HTTP request handle on success
3211 * NULL on failure
3214 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3215 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3216 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3217 DWORD dwFlags, DWORD_PTR dwContext)
3219 http_session_t *session;
3220 HINTERNET handle = NULL;
3221 DWORD res;
3223 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3224 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3225 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3226 dwFlags, dwContext);
3227 if(lpszAcceptTypes!=NULL)
3229 int i;
3230 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3231 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3234 session = (http_session_t*) get_handle_object( hHttpSession );
3235 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3237 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3238 goto lend;
3242 * My tests seem to show that the windows version does not
3243 * become asynchronous until after this point. And anyhow
3244 * if this call was asynchronous then how would you get the
3245 * necessary HINTERNET pointer returned by this function.
3248 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3249 lpszVersion, lpszReferrer, lpszAcceptTypes,
3250 dwFlags, dwContext, &handle);
3251 lend:
3252 if( session )
3253 WININET_Release( &session->hdr );
3254 TRACE("returning %p\n", handle);
3255 if(res != ERROR_SUCCESS)
3256 SetLastError(res);
3257 return handle;
3260 static const LPCWSTR header_lookup[] = {
3261 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3262 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3263 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3264 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3265 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3266 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3267 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3268 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3269 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3270 szDate, /* HTTP_QUERY_DATE = 9 */
3271 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3272 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3273 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3274 szURI, /* HTTP_QUERY_URI = 13 */
3275 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3276 NULL, /* HTTP_QUERY_COST = 15 */
3277 NULL, /* HTTP_QUERY_LINK = 16 */
3278 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3279 NULL, /* HTTP_QUERY_VERSION = 18 */
3280 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3281 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3282 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3283 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3284 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3285 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3286 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3287 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3288 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3289 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3290 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3291 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3292 NULL, /* HTTP_QUERY_FROM = 31 */
3293 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3294 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3295 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3296 szReferer, /* HTTP_QUERY_REFERER = 35 */
3297 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3298 szServer, /* HTTP_QUERY_SERVER = 37 */
3299 NULL, /* HTTP_TITLE = 38 */
3300 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3301 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3302 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3303 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3304 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3305 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3306 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3307 NULL, /* HTTP_QUERY_REFRESH = 46 */
3308 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3309 szAge, /* HTTP_QUERY_AGE = 48 */
3310 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3311 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3312 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3313 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3314 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3315 szETag, /* HTTP_QUERY_ETAG = 54 */
3316 hostW, /* HTTP_QUERY_HOST = 55 */
3317 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3318 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3319 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3320 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3321 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3322 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3323 szRange, /* HTTP_QUERY_RANGE = 62 */
3324 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3325 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3326 szVary, /* HTTP_QUERY_VARY = 65 */
3327 szVia, /* HTTP_QUERY_VIA = 66 */
3328 szWarning, /* HTTP_QUERY_WARNING = 67 */
3329 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3330 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3331 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3334 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3336 /***********************************************************************
3337 * HTTP_HttpQueryInfoW (internal)
3339 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3340 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3342 LPHTTPHEADERW lphttpHdr = NULL;
3343 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3344 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3345 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3346 INT index = -1;
3348 /* Find requested header structure */
3349 switch (level)
3351 case HTTP_QUERY_CUSTOM:
3352 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3353 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3354 break;
3355 case HTTP_QUERY_RAW_HEADERS_CRLF:
3357 LPWSTR headers;
3358 DWORD len = 0;
3359 DWORD res = ERROR_INVALID_PARAMETER;
3361 if (request_only)
3362 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3363 else
3364 headers = request->rawHeaders;
3366 if (headers)
3367 len = strlenW(headers) * sizeof(WCHAR);
3369 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3371 len += sizeof(WCHAR);
3372 res = ERROR_INSUFFICIENT_BUFFER;
3374 else if (lpBuffer)
3376 if (headers)
3377 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3378 else
3380 len = strlenW(szCrLf) * sizeof(WCHAR);
3381 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3383 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3384 res = ERROR_SUCCESS;
3386 *lpdwBufferLength = len;
3388 if (request_only) heap_free(headers);
3389 return res;
3391 case HTTP_QUERY_RAW_HEADERS:
3393 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3394 DWORD i, size = 0;
3395 LPWSTR pszString = lpBuffer;
3397 for (i = 0; ppszRawHeaderLines[i]; i++)
3398 size += strlenW(ppszRawHeaderLines[i]) + 1;
3400 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3402 HTTP_FreeTokens(ppszRawHeaderLines);
3403 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3404 return ERROR_INSUFFICIENT_BUFFER;
3406 if (pszString)
3408 for (i = 0; ppszRawHeaderLines[i]; i++)
3410 DWORD len = strlenW(ppszRawHeaderLines[i]);
3411 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3412 pszString += len+1;
3414 *pszString = '\0';
3415 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3417 *lpdwBufferLength = size * sizeof(WCHAR);
3418 HTTP_FreeTokens(ppszRawHeaderLines);
3420 return ERROR_SUCCESS;
3422 case HTTP_QUERY_STATUS_TEXT:
3423 if (request->statusText)
3425 DWORD len = strlenW(request->statusText);
3426 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3428 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3429 return ERROR_INSUFFICIENT_BUFFER;
3431 if (lpBuffer)
3433 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3434 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3436 *lpdwBufferLength = len * sizeof(WCHAR);
3437 return ERROR_SUCCESS;
3439 break;
3440 case HTTP_QUERY_VERSION:
3441 if (request->version)
3443 DWORD len = strlenW(request->version);
3444 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3446 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3447 return ERROR_INSUFFICIENT_BUFFER;
3449 if (lpBuffer)
3451 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3452 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3454 *lpdwBufferLength = len * sizeof(WCHAR);
3455 return ERROR_SUCCESS;
3457 break;
3458 case HTTP_QUERY_CONTENT_ENCODING:
3459 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3460 requested_index,request_only);
3461 break;
3462 case HTTP_QUERY_STATUS_CODE: {
3463 DWORD res = ERROR_SUCCESS;
3465 if(request_only)
3466 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3468 if(requested_index)
3469 break;
3471 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3472 if(*lpdwBufferLength >= sizeof(DWORD))
3473 *(DWORD*)lpBuffer = request->status_code;
3474 else
3475 res = ERROR_INSUFFICIENT_BUFFER;
3476 *lpdwBufferLength = sizeof(DWORD);
3477 }else {
3478 WCHAR buf[12];
3479 DWORD size;
3480 static const WCHAR formatW[] = {'%','u',0};
3482 size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3484 if(size <= *lpdwBufferLength)
3485 memcpy(lpBuffer, buf, size);
3486 else
3487 res = ERROR_INSUFFICIENT_BUFFER;
3489 *lpdwBufferLength = size;
3491 return res;
3493 default:
3494 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3496 if (level < LAST_TABLE_HEADER && header_lookup[level])
3497 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3498 requested_index,request_only);
3501 if (index >= 0)
3502 lphttpHdr = &request->custHeaders[index];
3504 /* Ensure header satisfies requested attributes */
3505 if (!lphttpHdr ||
3506 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3507 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3509 return ERROR_HTTP_HEADER_NOT_FOUND;
3512 if (lpdwIndex) (*lpdwIndex)++;
3514 /* coalesce value to requested type */
3515 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3517 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3518 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3520 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3522 time_t tmpTime;
3523 struct tm tmpTM;
3524 SYSTEMTIME *STHook;
3526 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3528 tmpTM = *gmtime(&tmpTime);
3529 STHook = (SYSTEMTIME *)lpBuffer;
3530 STHook->wDay = tmpTM.tm_mday;
3531 STHook->wHour = tmpTM.tm_hour;
3532 STHook->wMilliseconds = 0;
3533 STHook->wMinute = tmpTM.tm_min;
3534 STHook->wDayOfWeek = tmpTM.tm_wday;
3535 STHook->wMonth = tmpTM.tm_mon + 1;
3536 STHook->wSecond = tmpTM.tm_sec;
3537 STHook->wYear = tmpTM.tm_year;
3539 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3540 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3541 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3543 else if (lphttpHdr->lpszValue)
3545 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3547 if (len > *lpdwBufferLength)
3549 *lpdwBufferLength = len;
3550 return ERROR_INSUFFICIENT_BUFFER;
3552 if (lpBuffer)
3554 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3555 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3557 *lpdwBufferLength = len - sizeof(WCHAR);
3559 return ERROR_SUCCESS;
3562 /***********************************************************************
3563 * HttpQueryInfoW (WININET.@)
3565 * Queries for information about an HTTP request
3567 * RETURNS
3568 * TRUE on success
3569 * FALSE on failure
3572 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3573 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3575 http_request_t *request;
3576 DWORD res;
3578 if (TRACE_ON(wininet)) {
3579 #define FE(x) { x, #x }
3580 static const wininet_flag_info query_flags[] = {
3581 FE(HTTP_QUERY_MIME_VERSION),
3582 FE(HTTP_QUERY_CONTENT_TYPE),
3583 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3584 FE(HTTP_QUERY_CONTENT_ID),
3585 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3586 FE(HTTP_QUERY_CONTENT_LENGTH),
3587 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3588 FE(HTTP_QUERY_ALLOW),
3589 FE(HTTP_QUERY_PUBLIC),
3590 FE(HTTP_QUERY_DATE),
3591 FE(HTTP_QUERY_EXPIRES),
3592 FE(HTTP_QUERY_LAST_MODIFIED),
3593 FE(HTTP_QUERY_MESSAGE_ID),
3594 FE(HTTP_QUERY_URI),
3595 FE(HTTP_QUERY_DERIVED_FROM),
3596 FE(HTTP_QUERY_COST),
3597 FE(HTTP_QUERY_LINK),
3598 FE(HTTP_QUERY_PRAGMA),
3599 FE(HTTP_QUERY_VERSION),
3600 FE(HTTP_QUERY_STATUS_CODE),
3601 FE(HTTP_QUERY_STATUS_TEXT),
3602 FE(HTTP_QUERY_RAW_HEADERS),
3603 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3604 FE(HTTP_QUERY_CONNECTION),
3605 FE(HTTP_QUERY_ACCEPT),
3606 FE(HTTP_QUERY_ACCEPT_CHARSET),
3607 FE(HTTP_QUERY_ACCEPT_ENCODING),
3608 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3609 FE(HTTP_QUERY_AUTHORIZATION),
3610 FE(HTTP_QUERY_CONTENT_ENCODING),
3611 FE(HTTP_QUERY_FORWARDED),
3612 FE(HTTP_QUERY_FROM),
3613 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3614 FE(HTTP_QUERY_LOCATION),
3615 FE(HTTP_QUERY_ORIG_URI),
3616 FE(HTTP_QUERY_REFERER),
3617 FE(HTTP_QUERY_RETRY_AFTER),
3618 FE(HTTP_QUERY_SERVER),
3619 FE(HTTP_QUERY_TITLE),
3620 FE(HTTP_QUERY_USER_AGENT),
3621 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3622 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3623 FE(HTTP_QUERY_ACCEPT_RANGES),
3624 FE(HTTP_QUERY_SET_COOKIE),
3625 FE(HTTP_QUERY_COOKIE),
3626 FE(HTTP_QUERY_REQUEST_METHOD),
3627 FE(HTTP_QUERY_REFRESH),
3628 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3629 FE(HTTP_QUERY_AGE),
3630 FE(HTTP_QUERY_CACHE_CONTROL),
3631 FE(HTTP_QUERY_CONTENT_BASE),
3632 FE(HTTP_QUERY_CONTENT_LOCATION),
3633 FE(HTTP_QUERY_CONTENT_MD5),
3634 FE(HTTP_QUERY_CONTENT_RANGE),
3635 FE(HTTP_QUERY_ETAG),
3636 FE(HTTP_QUERY_HOST),
3637 FE(HTTP_QUERY_IF_MATCH),
3638 FE(HTTP_QUERY_IF_NONE_MATCH),
3639 FE(HTTP_QUERY_IF_RANGE),
3640 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3641 FE(HTTP_QUERY_MAX_FORWARDS),
3642 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3643 FE(HTTP_QUERY_RANGE),
3644 FE(HTTP_QUERY_TRANSFER_ENCODING),
3645 FE(HTTP_QUERY_UPGRADE),
3646 FE(HTTP_QUERY_VARY),
3647 FE(HTTP_QUERY_VIA),
3648 FE(HTTP_QUERY_WARNING),
3649 FE(HTTP_QUERY_CUSTOM)
3651 static const wininet_flag_info modifier_flags[] = {
3652 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3653 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3654 FE(HTTP_QUERY_FLAG_NUMBER),
3655 FE(HTTP_QUERY_FLAG_COALESCE)
3657 #undef FE
3658 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3659 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3660 DWORD i;
3662 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3663 TRACE(" Attribute:");
3664 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3665 if (query_flags[i].val == info) {
3666 TRACE(" %s", query_flags[i].name);
3667 break;
3670 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3671 TRACE(" Unknown (%08x)", info);
3674 TRACE(" Modifier:");
3675 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3676 if (modifier_flags[i].val & info_mod) {
3677 TRACE(" %s", modifier_flags[i].name);
3678 info_mod &= ~ modifier_flags[i].val;
3682 if (info_mod) {
3683 TRACE(" Unknown (%08x)", info_mod);
3685 TRACE("\n");
3688 request = (http_request_t*) get_handle_object( hHttpRequest );
3689 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3691 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3692 goto lend;
3695 if (lpBuffer == NULL)
3696 *lpdwBufferLength = 0;
3697 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3698 lpBuffer, lpdwBufferLength, lpdwIndex);
3700 lend:
3701 if( request )
3702 WININET_Release( &request->hdr );
3704 TRACE("%u <--\n", res);
3705 if(res != ERROR_SUCCESS)
3706 SetLastError(res);
3707 return res == ERROR_SUCCESS;
3710 /***********************************************************************
3711 * HttpQueryInfoA (WININET.@)
3713 * Queries for information about an HTTP request
3715 * RETURNS
3716 * TRUE on success
3717 * FALSE on failure
3720 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3721 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3723 BOOL result;
3724 DWORD len;
3725 WCHAR* bufferW;
3727 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3728 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3730 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3731 lpdwBufferLength, lpdwIndex );
3734 if (lpBuffer)
3736 DWORD alloclen;
3737 len = (*lpdwBufferLength)*sizeof(WCHAR);
3738 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3740 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3741 if (alloclen < len)
3742 alloclen = len;
3744 else
3745 alloclen = len;
3746 bufferW = heap_alloc(alloclen);
3747 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3748 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3749 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3750 } else
3752 bufferW = NULL;
3753 len = 0;
3756 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3757 &len, lpdwIndex );
3758 if( result )
3760 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3761 lpBuffer, *lpdwBufferLength, NULL, NULL );
3762 *lpdwBufferLength = len - 1;
3764 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3766 else
3767 /* since the strings being returned from HttpQueryInfoW should be
3768 * only ASCII characters, it is reasonable to assume that all of
3769 * the Unicode characters can be reduced to a single byte */
3770 *lpdwBufferLength = len / sizeof(WCHAR);
3772 heap_free( bufferW );
3773 return result;
3776 /***********************************************************************
3777 * HTTP_GetRedirectURL (internal)
3779 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3781 static WCHAR szHttp[] = {'h','t','t','p',0};
3782 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3783 http_session_t *session = request->session;
3784 URL_COMPONENTSW urlComponents;
3785 DWORD url_length = 0;
3786 LPWSTR orig_url;
3787 LPWSTR combined_url;
3789 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3790 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3791 urlComponents.dwSchemeLength = 0;
3792 urlComponents.lpszHostName = session->hostName;
3793 urlComponents.dwHostNameLength = 0;
3794 urlComponents.nPort = session->hostPort;
3795 urlComponents.lpszUserName = session->userName;
3796 urlComponents.dwUserNameLength = 0;
3797 urlComponents.lpszPassword = NULL;
3798 urlComponents.dwPasswordLength = 0;
3799 urlComponents.lpszUrlPath = request->path;
3800 urlComponents.dwUrlPathLength = 0;
3801 urlComponents.lpszExtraInfo = NULL;
3802 urlComponents.dwExtraInfoLength = 0;
3804 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3805 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3806 return NULL;
3808 orig_url = heap_alloc(url_length);
3810 /* convert from bytes to characters */
3811 url_length = url_length / sizeof(WCHAR) - 1;
3812 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3814 heap_free(orig_url);
3815 return NULL;
3818 url_length = 0;
3819 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3820 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3822 heap_free(orig_url);
3823 return NULL;
3825 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3827 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3829 heap_free(orig_url);
3830 heap_free(combined_url);
3831 return NULL;
3833 heap_free(orig_url);
3834 return combined_url;
3838 /***********************************************************************
3839 * HTTP_HandleRedirect (internal)
3841 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3843 http_session_t *session = request->session;
3844 appinfo_t *hIC = session->appInfo;
3845 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3846 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3847 int index;
3849 if(lpszUrl[0]=='/')
3851 /* if it's an absolute path, keep the same session info */
3852 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3854 else
3856 URL_COMPONENTSW urlComponents;
3857 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3858 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3859 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3860 BOOL custom_port = FALSE;
3862 static WCHAR httpW[] = {'h','t','t','p',0};
3863 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3865 userName[0] = 0;
3866 hostName[0] = 0;
3867 protocol[0] = 0;
3869 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3870 urlComponents.lpszScheme = protocol;
3871 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3872 urlComponents.lpszHostName = hostName;
3873 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3874 urlComponents.lpszUserName = userName;
3875 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3876 urlComponents.lpszPassword = NULL;
3877 urlComponents.dwPasswordLength = 0;
3878 urlComponents.lpszUrlPath = path;
3879 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3880 urlComponents.lpszExtraInfo = NULL;
3881 urlComponents.dwExtraInfoLength = 0;
3882 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3883 return INTERNET_GetLastError();
3885 if(!strcmpiW(protocol, httpW)) {
3886 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3887 TRACE("redirect from secure page to non-secure page\n");
3888 /* FIXME: warn about from secure redirect to non-secure page */
3889 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3892 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3893 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3894 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3895 custom_port = TRUE;
3896 }else if(!strcmpiW(protocol, httpsW)) {
3897 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3898 TRACE("redirect from non-secure page to secure page\n");
3899 /* FIXME: notify about redirect to secure page */
3900 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3903 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3904 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3905 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3906 custom_port = TRUE;
3909 heap_free(session->hostName);
3911 if(custom_port) {
3912 int len;
3913 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3914 len = lstrlenW(hostName);
3915 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3916 session->hostName = heap_alloc(len*sizeof(WCHAR));
3917 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3919 else
3920 session->hostName = heap_strdupW(hostName);
3922 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3924 heap_free(session->userName);
3925 session->userName = NULL;
3926 if (userName[0])
3927 session->userName = heap_strdupW(userName);
3929 reset_data_stream(request);
3931 if(!using_proxy && (strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort)) {
3932 server_t *new_server;
3934 new_server = get_server(hostName, urlComponents.nPort, TRUE);
3935 server_release(request->server);
3936 request->server = new_server;
3939 heap_free(request->path);
3940 request->path=NULL;
3941 if (*path)
3943 DWORD needed = 0;
3944 HRESULT rc;
3946 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3947 if (rc != E_POINTER)
3948 needed = strlenW(path)+1;
3949 request->path = heap_alloc(needed*sizeof(WCHAR));
3950 rc = UrlEscapeW(path, request->path, &needed,
3951 URL_ESCAPE_SPACES_ONLY);
3952 if (rc != S_OK)
3954 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3955 strcpyW(request->path,path);
3959 /* Remove custom content-type/length headers on redirects. */
3960 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3961 if (0 <= index)
3962 HTTP_DeleteCustomHeader(request, index);
3963 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3964 if (0 <= index)
3965 HTTP_DeleteCustomHeader(request, index);
3967 return ERROR_SUCCESS;
3970 /***********************************************************************
3971 * HTTP_build_req (internal)
3973 * concatenate all the strings in the request together
3975 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3977 LPCWSTR *t;
3978 LPWSTR str;
3980 for( t = list; *t ; t++ )
3981 len += strlenW( *t );
3982 len++;
3984 str = heap_alloc(len*sizeof(WCHAR));
3985 *str = 0;
3987 for( t = list; *t ; t++ )
3988 strcatW( str, *t );
3990 return str;
3993 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3995 LPWSTR lpszPath;
3996 LPWSTR requestString;
3997 INT len;
3998 INT cnt;
3999 INT responseLen;
4000 char *ascii_req;
4001 DWORD res;
4002 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
4003 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
4004 http_session_t *session = request->session;
4006 TRACE("\n");
4008 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
4009 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
4010 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
4011 heap_free( lpszPath );
4013 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4014 NULL, 0, NULL, NULL );
4015 len--; /* the nul terminator isn't needed */
4016 ascii_req = heap_alloc(len);
4017 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4018 heap_free( requestString );
4020 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4022 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4023 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4024 heap_free( ascii_req );
4025 if (res != ERROR_SUCCESS)
4026 return res;
4028 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4029 if (!responseLen)
4030 return ERROR_HTTP_INVALID_HEADER;
4032 return ERROR_SUCCESS;
4035 static void HTTP_InsertCookies(http_request_t *request)
4037 DWORD cookie_size, size, cnt = 0;
4038 HTTPHEADERW *host;
4039 WCHAR *cookies;
4041 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4043 host = HTTP_GetHeader(request, hostW);
4044 if(!host)
4045 return;
4047 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4048 return;
4050 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4051 if(!(cookies = heap_alloc(size)))
4052 return;
4054 cnt += sprintfW(cookies, cookieW);
4055 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4056 strcatW(cookies, szCrLf);
4058 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4060 heap_free(cookies);
4063 static WORD HTTP_ParseWkday(LPCWSTR day)
4065 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4066 { 'm','o','n',0 },
4067 { 't','u','e',0 },
4068 { 'w','e','d',0 },
4069 { 't','h','u',0 },
4070 { 'f','r','i',0 },
4071 { 's','a','t',0 }};
4072 int i;
4073 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4074 if (!strcmpiW(day, days[i]))
4075 return i;
4077 /* Invalid */
4078 return 7;
4081 static WORD HTTP_ParseMonth(LPCWSTR month)
4083 static const WCHAR jan[] = { 'j','a','n',0 };
4084 static const WCHAR feb[] = { 'f','e','b',0 };
4085 static const WCHAR mar[] = { 'm','a','r',0 };
4086 static const WCHAR apr[] = { 'a','p','r',0 };
4087 static const WCHAR may[] = { 'm','a','y',0 };
4088 static const WCHAR jun[] = { 'j','u','n',0 };
4089 static const WCHAR jul[] = { 'j','u','l',0 };
4090 static const WCHAR aug[] = { 'a','u','g',0 };
4091 static const WCHAR sep[] = { 's','e','p',0 };
4092 static const WCHAR oct[] = { 'o','c','t',0 };
4093 static const WCHAR nov[] = { 'n','o','v',0 };
4094 static const WCHAR dec[] = { 'd','e','c',0 };
4096 if (!strcmpiW(month, jan)) return 1;
4097 if (!strcmpiW(month, feb)) return 2;
4098 if (!strcmpiW(month, mar)) return 3;
4099 if (!strcmpiW(month, apr)) return 4;
4100 if (!strcmpiW(month, may)) return 5;
4101 if (!strcmpiW(month, jun)) return 6;
4102 if (!strcmpiW(month, jul)) return 7;
4103 if (!strcmpiW(month, aug)) return 8;
4104 if (!strcmpiW(month, sep)) return 9;
4105 if (!strcmpiW(month, oct)) return 10;
4106 if (!strcmpiW(month, nov)) return 11;
4107 if (!strcmpiW(month, dec)) return 12;
4108 /* Invalid */
4109 return 0;
4112 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4113 * optionally preceded by whitespace.
4114 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4115 * st, and sets *str to the first character after the time format.
4117 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4119 LPCWSTR ptr = *str;
4120 WCHAR *nextPtr;
4121 unsigned long num;
4123 while (isspaceW(*ptr))
4124 ptr++;
4126 num = strtoulW(ptr, &nextPtr, 10);
4127 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4129 ERR("unexpected time format %s\n", debugstr_w(ptr));
4130 return FALSE;
4132 if (num > 23)
4134 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4135 return FALSE;
4137 ptr = nextPtr + 1;
4138 st->wHour = (WORD)num;
4139 num = strtoulW(ptr, &nextPtr, 10);
4140 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4142 ERR("unexpected time format %s\n", debugstr_w(ptr));
4143 return FALSE;
4145 if (num > 59)
4147 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4148 return FALSE;
4150 ptr = nextPtr + 1;
4151 st->wMinute = (WORD)num;
4152 num = strtoulW(ptr, &nextPtr, 10);
4153 if (!nextPtr || nextPtr <= ptr)
4155 ERR("unexpected time format %s\n", debugstr_w(ptr));
4156 return FALSE;
4158 if (num > 59)
4160 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4161 return FALSE;
4163 ptr = nextPtr + 1;
4164 *str = ptr;
4165 st->wSecond = (WORD)num;
4166 return TRUE;
4169 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4171 static const WCHAR gmt[]= { 'G','M','T',0 };
4172 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4173 LPCWSTR ptr;
4174 SYSTEMTIME st = { 0 };
4175 unsigned long num;
4177 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4178 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4179 *dayPtr = *ptr;
4180 *dayPtr = 0;
4181 st.wDayOfWeek = HTTP_ParseWkday(day);
4182 if (st.wDayOfWeek >= 7)
4184 ERR("unexpected weekday %s\n", debugstr_w(day));
4185 return FALSE;
4188 while (isspaceW(*ptr))
4189 ptr++;
4191 for (monthPtr = month; !isspace(*ptr) &&
4192 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4193 monthPtr++, ptr++)
4194 *monthPtr = *ptr;
4195 *monthPtr = 0;
4196 st.wMonth = HTTP_ParseMonth(month);
4197 if (!st.wMonth || st.wMonth > 12)
4199 ERR("unexpected month %s\n", debugstr_w(month));
4200 return FALSE;
4203 while (isspaceW(*ptr))
4204 ptr++;
4206 num = strtoulW(ptr, &nextPtr, 10);
4207 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4209 ERR("unexpected day %s\n", debugstr_w(ptr));
4210 return FALSE;
4212 ptr = nextPtr;
4213 st.wDay = (WORD)num;
4215 while (isspaceW(*ptr))
4216 ptr++;
4218 if (!HTTP_ParseTime(&st, &ptr))
4219 return FALSE;
4221 while (isspaceW(*ptr))
4222 ptr++;
4224 num = strtoulW(ptr, &nextPtr, 10);
4225 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4227 ERR("unexpected year %s\n", debugstr_w(ptr));
4228 return FALSE;
4230 ptr = nextPtr;
4231 st.wYear = (WORD)num;
4233 while (isspaceW(*ptr))
4234 ptr++;
4236 /* asctime() doesn't report a timezone, but some web servers do, so accept
4237 * with or without GMT.
4239 if (*ptr && strcmpW(ptr, gmt))
4241 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4242 return FALSE;
4244 return SystemTimeToFileTime(&st, ft);
4247 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4249 static const WCHAR gmt[]= { 'G','M','T',0 };
4250 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4251 LPCWSTR ptr;
4252 unsigned long num;
4253 SYSTEMTIME st = { 0 };
4255 ptr = strchrW(value, ',');
4256 if (!ptr)
4257 return FALSE;
4258 if (ptr - value != 3)
4260 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4261 return FALSE;
4263 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4264 day[3] = 0;
4265 st.wDayOfWeek = HTTP_ParseWkday(day);
4266 if (st.wDayOfWeek > 6)
4268 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4269 return FALSE;
4271 ptr++;
4273 while (isspaceW(*ptr))
4274 ptr++;
4276 num = strtoulW(ptr, &nextPtr, 10);
4277 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4279 WARN("unexpected day %s\n", debugstr_w(value));
4280 return FALSE;
4282 ptr = nextPtr;
4283 st.wDay = (WORD)num;
4285 while (isspaceW(*ptr))
4286 ptr++;
4288 for (monthPtr = month; !isspace(*ptr) &&
4289 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4290 monthPtr++, ptr++)
4291 *monthPtr = *ptr;
4292 *monthPtr = 0;
4293 st.wMonth = HTTP_ParseMonth(month);
4294 if (!st.wMonth || st.wMonth > 12)
4296 WARN("unexpected month %s\n", debugstr_w(month));
4297 return FALSE;
4300 while (isspaceW(*ptr))
4301 ptr++;
4303 num = strtoulW(ptr, &nextPtr, 10);
4304 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4306 ERR("unexpected year %s\n", debugstr_w(value));
4307 return FALSE;
4309 ptr = nextPtr;
4310 st.wYear = (WORD)num;
4312 if (!HTTP_ParseTime(&st, &ptr))
4313 return FALSE;
4315 while (isspaceW(*ptr))
4316 ptr++;
4318 if (strcmpW(ptr, gmt))
4320 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4321 return FALSE;
4323 return SystemTimeToFileTime(&st, ft);
4326 static WORD HTTP_ParseWeekday(LPCWSTR day)
4328 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4329 { 'm','o','n','d','a','y',0 },
4330 { 't','u','e','s','d','a','y',0 },
4331 { 'w','e','d','n','e','s','d','a','y',0 },
4332 { 't','h','u','r','s','d','a','y',0 },
4333 { 'f','r','i','d','a','y',0 },
4334 { 's','a','t','u','r','d','a','y',0 }};
4335 int i;
4336 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4337 if (!strcmpiW(day, days[i]))
4338 return i;
4340 /* Invalid */
4341 return 7;
4344 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4346 static const WCHAR gmt[]= { 'G','M','T',0 };
4347 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4348 LPCWSTR ptr;
4349 unsigned long num;
4350 SYSTEMTIME st = { 0 };
4352 ptr = strchrW(value, ',');
4353 if (!ptr)
4354 return FALSE;
4355 if (ptr - value == 3)
4357 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4358 day[3] = 0;
4359 st.wDayOfWeek = HTTP_ParseWkday(day);
4360 if (st.wDayOfWeek > 6)
4362 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4363 return FALSE;
4366 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4368 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4369 day[ptr - value + 1] = 0;
4370 st.wDayOfWeek = HTTP_ParseWeekday(day);
4371 if (st.wDayOfWeek > 6)
4373 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4374 return FALSE;
4377 else
4379 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4380 return FALSE;
4382 ptr++;
4384 while (isspaceW(*ptr))
4385 ptr++;
4387 num = strtoulW(ptr, &nextPtr, 10);
4388 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4390 ERR("unexpected day %s\n", debugstr_w(value));
4391 return FALSE;
4393 ptr = nextPtr;
4394 st.wDay = (WORD)num;
4396 if (*ptr != '-')
4398 ERR("unexpected month format %s\n", debugstr_w(ptr));
4399 return FALSE;
4401 ptr++;
4403 for (monthPtr = month; *ptr != '-' &&
4404 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4405 monthPtr++, ptr++)
4406 *monthPtr = *ptr;
4407 *monthPtr = 0;
4408 st.wMonth = HTTP_ParseMonth(month);
4409 if (!st.wMonth || st.wMonth > 12)
4411 ERR("unexpected month %s\n", debugstr_w(month));
4412 return FALSE;
4415 if (*ptr != '-')
4417 ERR("unexpected year format %s\n", debugstr_w(ptr));
4418 return FALSE;
4420 ptr++;
4422 num = strtoulW(ptr, &nextPtr, 10);
4423 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4425 ERR("unexpected year %s\n", debugstr_w(value));
4426 return FALSE;
4428 ptr = nextPtr;
4429 st.wYear = (WORD)num;
4431 if (!HTTP_ParseTime(&st, &ptr))
4432 return FALSE;
4434 while (isspaceW(*ptr))
4435 ptr++;
4437 if (strcmpW(ptr, gmt))
4439 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4440 return FALSE;
4442 return SystemTimeToFileTime(&st, ft);
4445 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4447 static const WCHAR zero[] = { '0',0 };
4448 BOOL ret;
4450 if (!strcmpW(value, zero))
4452 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4453 ret = TRUE;
4455 else if (strchrW(value, ','))
4457 ret = HTTP_ParseRfc1123Date(value, ft);
4458 if (!ret)
4460 ret = HTTP_ParseRfc850Date(value, ft);
4461 if (!ret)
4462 ERR("unexpected date format %s\n", debugstr_w(value));
4465 else
4467 ret = HTTP_ParseDateAsAsctime(value, ft);
4468 if (!ret)
4469 ERR("unexpected date format %s\n", debugstr_w(value));
4471 return ret;
4474 static void HTTP_ProcessExpires(http_request_t *request)
4476 BOOL expirationFound = FALSE;
4477 int headerIndex;
4479 /* Look for a Cache-Control header with a max-age directive, as it takes
4480 * precedence over the Expires header.
4482 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4483 if (headerIndex != -1)
4485 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4486 LPWSTR ptr;
4488 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4490 LPWSTR comma = strchrW(ptr, ','), end, equal;
4492 if (comma)
4493 end = comma;
4494 else
4495 end = ptr + strlenW(ptr);
4496 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4498 if (*equal == '=')
4500 static const WCHAR max_age[] = {
4501 'm','a','x','-','a','g','e',0 };
4503 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4505 LPWSTR nextPtr;
4506 unsigned long age;
4508 age = strtoulW(equal + 1, &nextPtr, 10);
4509 if (nextPtr > equal + 1)
4511 LARGE_INTEGER ft;
4513 NtQuerySystemTime( &ft );
4514 /* Age is in seconds, FILETIME resolution is in
4515 * 100 nanosecond intervals.
4517 ft.QuadPart += age * (ULONGLONG)1000000;
4518 request->expires.dwLowDateTime = ft.u.LowPart;
4519 request->expires.dwHighDateTime = ft.u.HighPart;
4520 expirationFound = TRUE;
4524 if (comma)
4526 ptr = comma + 1;
4527 while (isspaceW(*ptr))
4528 ptr++;
4530 else
4531 ptr = NULL;
4534 if (!expirationFound)
4536 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4537 if (headerIndex != -1)
4539 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4540 FILETIME ft;
4542 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4544 expirationFound = TRUE;
4545 request->expires = ft;
4549 if (!expirationFound)
4551 LARGE_INTEGER t;
4553 /* With no known age, default to 10 minutes until expiration. */
4554 NtQuerySystemTime( &t );
4555 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4556 request->expires.dwLowDateTime = t.u.LowPart;
4557 request->expires.dwHighDateTime = t.u.HighPart;
4561 static void HTTP_ProcessLastModified(http_request_t *request)
4563 int headerIndex;
4565 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4566 if (headerIndex != -1)
4568 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4569 FILETIME ft;
4571 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4572 request->last_modified = ft;
4576 static void http_process_keep_alive(http_request_t *req)
4578 int index;
4580 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4581 if(index != -1)
4582 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4583 else
4584 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4587 static void HTTP_CacheRequest(http_request_t *request)
4589 WCHAR url[INTERNET_MAX_URL_LENGTH];
4590 WCHAR cacheFileName[MAX_PATH+1];
4591 BOOL b;
4593 b = HTTP_GetRequestURL(request, url);
4594 if(!b) {
4595 WARN("Could not get URL\n");
4596 return;
4599 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4600 if(b) {
4601 heap_free(request->cacheFile);
4602 CloseHandle(request->hCacheFile);
4604 request->cacheFile = heap_strdupW(cacheFileName);
4605 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4606 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4607 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4608 WARN("Could not create file: %u\n", GetLastError());
4609 request->hCacheFile = NULL;
4611 }else {
4612 WARN("Could not create cache entry: %08x\n", GetLastError());
4616 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4618 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4619 netconn_t *netconn = NULL;
4620 DWORD res;
4622 assert(!request->netconn);
4623 reset_data_stream(request);
4625 res = HTTP_ResolveName(request);
4626 if(res != ERROR_SUCCESS)
4627 return res;
4629 EnterCriticalSection(&connection_pool_cs);
4631 while(!list_empty(&request->server->conn_pool)) {
4632 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4633 list_remove(&netconn->pool_entry);
4635 if(NETCON_is_alive(netconn))
4636 break;
4638 TRACE("connection %p closed during idle\n", netconn);
4639 free_netconn(netconn);
4640 netconn = NULL;
4643 LeaveCriticalSection(&connection_pool_cs);
4645 if(netconn) {
4646 TRACE("<-- reusing %p netconn\n", netconn);
4647 request->netconn = netconn;
4648 *reusing = TRUE;
4649 return ERROR_SUCCESS;
4652 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4653 INTERNET_STATUS_CONNECTING_TO_SERVER,
4654 request->server->addr_str,
4655 strlen(request->server->addr_str)+1);
4657 res = create_netconn(is_https, request->server, request->security_flags,
4658 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4659 request->connect_timeout, &netconn);
4660 if(res != ERROR_SUCCESS) {
4661 ERR("create_netconn failed: %u\n", res);
4662 return res;
4665 request->netconn = netconn;
4667 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4668 INTERNET_STATUS_CONNECTED_TO_SERVER,
4669 request->server->addr_str, strlen(request->server->addr_str)+1);
4671 if(is_https) {
4672 /* Note: we differ from Microsoft's WinINet here. they seem to have
4673 * a bug that causes no status callbacks to be sent when starting
4674 * a tunnel to a proxy server using the CONNECT verb. i believe our
4675 * behaviour to be more correct and to not cause any incompatibilities
4676 * because using a secure connection through a proxy server is a rare
4677 * case that would be hard for anyone to depend on */
4678 if(request->session->appInfo->proxy)
4679 res = HTTP_SecureProxyConnect(request);
4680 if(res == ERROR_SUCCESS)
4681 res = NETCON_secure_connect(request->netconn);
4684 if(res != ERROR_SUCCESS) {
4685 http_release_netconn(request, FALSE);
4686 return res;
4689 *reusing = FALSE;
4690 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4691 return ERROR_SUCCESS;
4694 /***********************************************************************
4695 * HTTP_HttpSendRequestW (internal)
4697 * Sends the specified request to the HTTP server
4699 * RETURNS
4700 * ERROR_SUCCESS on success
4701 * win32 error code on failure
4704 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4705 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4706 DWORD dwContentLength, BOOL bEndRequest)
4708 INT cnt;
4709 BOOL redirected = FALSE;
4710 LPWSTR requestString = NULL;
4711 INT responseLen;
4712 BOOL loop_next;
4713 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4714 static const WCHAR szContentLength[] =
4715 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4716 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4717 DWORD res;
4719 TRACE("--> %p\n", request);
4721 assert(request->hdr.htype == WH_HHTTPREQ);
4723 /* if the verb is NULL default to GET */
4724 if (!request->verb)
4725 request->verb = heap_strdupW(szGET);
4727 if (dwContentLength || strcmpW(request->verb, szGET))
4729 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4730 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4731 request->bytesToWrite = dwContentLength;
4733 if (request->session->appInfo->agent)
4735 WCHAR *agent_header;
4736 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4737 int len;
4739 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4740 agent_header = heap_alloc(len * sizeof(WCHAR));
4741 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4743 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4744 heap_free(agent_header);
4746 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4748 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4749 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4751 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4753 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4754 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4755 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4758 /* add the headers the caller supplied */
4759 if( lpszHeaders && dwHeaderLength )
4760 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4764 DWORD len;
4765 BOOL reusing_connection;
4766 char *ascii_req;
4768 loop_next = FALSE;
4770 /* like native, just in case the caller forgot to call InternetReadFile
4771 * for all the data */
4772 drain_content(request);
4773 if(redirected) {
4774 request->contentLength = ~0u;
4775 request->bytesToWrite = 0;
4778 if (TRACE_ON(wininet))
4780 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4781 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4784 HTTP_FixURL(request);
4785 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4787 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4789 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4790 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4792 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4793 HTTP_InsertCookies(request);
4795 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4797 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4798 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4799 heap_free(url);
4801 else
4802 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4805 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4807 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4808 break;
4810 /* send the request as ASCII, tack on the optional data */
4811 if (!lpOptional || redirected)
4812 dwOptionalLength = 0;
4813 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4814 NULL, 0, NULL, NULL );
4815 ascii_req = heap_alloc(len + dwOptionalLength);
4816 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4817 ascii_req, len, NULL, NULL );
4818 if( lpOptional )
4819 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4820 len = (len + dwOptionalLength - 1);
4821 ascii_req[len] = 0;
4822 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4824 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4825 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4827 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4828 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4829 heap_free( ascii_req );
4830 if(res != ERROR_SUCCESS) {
4831 TRACE("send failed: %u\n", res);
4832 if(!reusing_connection)
4833 break;
4834 http_release_netconn(request, FALSE);
4835 loop_next = TRUE;
4836 continue;
4839 request->bytesWritten = dwOptionalLength;
4841 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4842 INTERNET_STATUS_REQUEST_SENT,
4843 &len, sizeof(DWORD));
4845 if (bEndRequest)
4847 DWORD dwBufferSize;
4849 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4850 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4852 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4853 /* FIXME: We should know that connection is closed before sending
4854 * headers. Otherwise wrong callbacks are executed */
4855 if(!responseLen && reusing_connection) {
4856 TRACE("Connection closed by server, reconnecting\n");
4857 http_release_netconn(request, FALSE);
4858 loop_next = TRUE;
4859 continue;
4862 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4863 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4864 sizeof(DWORD));
4866 http_process_keep_alive(request);
4867 HTTP_ProcessCookies(request);
4868 HTTP_ProcessExpires(request);
4869 HTTP_ProcessLastModified(request);
4871 res = set_content_length(request);
4872 if(res != ERROR_SUCCESS)
4873 goto lend;
4874 if(!request->contentLength)
4875 http_release_netconn(request, TRUE);
4877 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4879 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4880 dwBufferSize=sizeof(szNewLocation);
4881 switch(request->status_code) {
4882 case HTTP_STATUS_REDIRECT:
4883 case HTTP_STATUS_MOVED:
4884 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4885 case HTTP_STATUS_REDIRECT_METHOD:
4886 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4887 break;
4889 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4890 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4892 heap_free(request->verb);
4893 request->verb = heap_strdupW(szGET);
4895 drain_content(request);
4896 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4898 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4899 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4900 res = HTTP_HandleRedirect(request, new_url);
4901 if (res == ERROR_SUCCESS)
4903 heap_free(requestString);
4904 loop_next = TRUE;
4906 heap_free( new_url );
4908 redirected = TRUE;
4911 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4913 WCHAR szAuthValue[2048];
4914 dwBufferSize=2048;
4915 if (request->status_code == HTTP_STATUS_DENIED)
4917 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4918 DWORD dwIndex = 0;
4919 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4921 if (HTTP_DoAuthorization(request, szAuthValue,
4922 &request->authInfo,
4923 request->session->userName,
4924 request->session->password,
4925 Host->lpszValue))
4927 heap_free(requestString);
4928 loop_next = TRUE;
4929 break;
4933 if(!loop_next) {
4934 TRACE("Cleaning wrong authorization data\n");
4935 destroy_authinfo(request->authInfo);
4936 request->authInfo = NULL;
4939 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4941 DWORD dwIndex = 0;
4942 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4944 if (HTTP_DoAuthorization(request, szAuthValue,
4945 &request->proxyAuthInfo,
4946 request->session->appInfo->proxyUsername,
4947 request->session->appInfo->proxyPassword,
4948 NULL))
4950 loop_next = TRUE;
4951 break;
4955 if(!loop_next) {
4956 TRACE("Cleaning wrong proxy authorization data\n");
4957 destroy_authinfo(request->proxyAuthInfo);
4958 request->proxyAuthInfo = NULL;
4963 else
4964 res = ERROR_SUCCESS;
4966 while (loop_next);
4968 if(res == ERROR_SUCCESS)
4969 HTTP_CacheRequest(request);
4971 lend:
4972 heap_free(requestString);
4974 /* TODO: send notification for P3P header */
4976 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4978 if (res == ERROR_SUCCESS) {
4979 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4980 HTTP_ReceiveRequestData(request, TRUE);
4981 else
4982 send_request_complete(request,
4983 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4984 }else {
4985 send_request_complete(request, 0, res);
4989 TRACE("<--\n");
4990 return res;
4993 /***********************************************************************
4995 * Helper functions for the HttpSendRequest(Ex) functions
4998 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
5000 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
5001 http_request_t *request = (http_request_t*) workRequest->hdr;
5003 TRACE("%p\n", request);
5005 HTTP_HttpSendRequestW(request, req->lpszHeader,
5006 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5007 req->dwContentLength, req->bEndRequest);
5009 heap_free(req->lpszHeader);
5013 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5015 DWORD dwBufferSize;
5016 INT responseLen;
5017 DWORD res = ERROR_SUCCESS;
5019 if(!request->netconn) {
5020 WARN("Not connected\n");
5021 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5022 return ERROR_INTERNET_OPERATION_CANCELLED;
5025 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5026 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5028 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5029 if (!responseLen)
5030 res = ERROR_HTTP_HEADER_NOT_FOUND;
5032 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5033 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5035 /* process cookies here. Is this right? */
5036 http_process_keep_alive(request);
5037 HTTP_ProcessCookies(request);
5038 HTTP_ProcessExpires(request);
5039 HTTP_ProcessLastModified(request);
5041 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5042 if(!request->contentLength)
5043 http_release_netconn(request, TRUE);
5046 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5048 switch(request->status_code) {
5049 case HTTP_STATUS_REDIRECT:
5050 case HTTP_STATUS_MOVED:
5051 case HTTP_STATUS_REDIRECT_METHOD:
5052 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5053 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5054 dwBufferSize=sizeof(szNewLocation);
5055 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5056 break;
5058 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5059 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5061 heap_free(request->verb);
5062 request->verb = heap_strdupW(szGET);
5064 drain_content(request);
5065 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5067 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5068 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5069 res = HTTP_HandleRedirect(request, new_url);
5070 if (res == ERROR_SUCCESS)
5071 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5072 heap_free( new_url );
5078 if (res == ERROR_SUCCESS && request->contentLength)
5079 HTTP_ReceiveRequestData(request, TRUE);
5080 else
5081 send_request_complete(request, res == ERROR_SUCCESS, res);
5083 return res;
5086 /***********************************************************************
5087 * HttpEndRequestA (WININET.@)
5089 * Ends an HTTP request that was started by HttpSendRequestEx
5091 * RETURNS
5092 * TRUE if successful
5093 * FALSE on failure
5096 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5097 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5099 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5101 if (lpBuffersOut)
5103 SetLastError(ERROR_INVALID_PARAMETER);
5104 return FALSE;
5107 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5110 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5112 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5113 http_request_t *request = (http_request_t*)work->hdr;
5115 TRACE("%p\n", request);
5117 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5120 /***********************************************************************
5121 * HttpEndRequestW (WININET.@)
5123 * Ends an HTTP request that was started by HttpSendRequestEx
5125 * RETURNS
5126 * TRUE if successful
5127 * FALSE on failure
5130 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5131 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5133 http_request_t *request;
5134 DWORD res;
5136 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5138 if (lpBuffersOut)
5140 SetLastError(ERROR_INVALID_PARAMETER);
5141 return FALSE;
5144 request = (http_request_t*) get_handle_object( hRequest );
5146 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5148 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5149 if (request)
5150 WININET_Release( &request->hdr );
5151 return FALSE;
5153 request->hdr.dwFlags |= dwFlags;
5155 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5157 WORKREQUEST work;
5158 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5160 work.asyncproc = AsyncHttpEndRequestProc;
5161 work.hdr = WININET_AddRef( &request->hdr );
5163 work_endrequest = &work.u.HttpEndRequestW;
5164 work_endrequest->dwFlags = dwFlags;
5165 work_endrequest->dwContext = dwContext;
5167 INTERNET_AsyncCall(&work);
5168 res = ERROR_IO_PENDING;
5170 else
5171 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5173 WININET_Release( &request->hdr );
5174 TRACE("%u <--\n", res);
5175 if(res != ERROR_SUCCESS)
5176 SetLastError(res);
5177 return res == ERROR_SUCCESS;
5180 /***********************************************************************
5181 * HttpSendRequestExA (WININET.@)
5183 * Sends the specified request to the HTTP server and allows chunked
5184 * transfers.
5186 * RETURNS
5187 * Success: TRUE
5188 * Failure: FALSE, call GetLastError() for more information.
5190 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5191 LPINTERNET_BUFFERSA lpBuffersIn,
5192 LPINTERNET_BUFFERSA lpBuffersOut,
5193 DWORD dwFlags, DWORD_PTR dwContext)
5195 INTERNET_BUFFERSW BuffersInW;
5196 BOOL rc = FALSE;
5197 DWORD headerlen;
5198 LPWSTR header = NULL;
5200 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5201 lpBuffersOut, dwFlags, dwContext);
5203 if (lpBuffersIn)
5205 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5206 if (lpBuffersIn->lpcszHeader)
5208 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5209 lpBuffersIn->dwHeadersLength,0,0);
5210 header = heap_alloc(headerlen*sizeof(WCHAR));
5211 if (!(BuffersInW.lpcszHeader = header))
5213 SetLastError(ERROR_OUTOFMEMORY);
5214 return FALSE;
5216 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5217 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5218 header, headerlen);
5220 else
5221 BuffersInW.lpcszHeader = NULL;
5222 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5223 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5224 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5225 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5226 BuffersInW.Next = NULL;
5229 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5231 heap_free(header);
5232 return rc;
5235 /***********************************************************************
5236 * HttpSendRequestExW (WININET.@)
5238 * Sends the specified request to the HTTP server and allows chunked
5239 * transfers
5241 * RETURNS
5242 * Success: TRUE
5243 * Failure: FALSE, call GetLastError() for more information.
5245 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5246 LPINTERNET_BUFFERSW lpBuffersIn,
5247 LPINTERNET_BUFFERSW lpBuffersOut,
5248 DWORD dwFlags, DWORD_PTR dwContext)
5250 http_request_t *request;
5251 http_session_t *session;
5252 appinfo_t *hIC;
5253 DWORD res;
5255 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5256 lpBuffersOut, dwFlags, dwContext);
5258 request = (http_request_t*) get_handle_object( hRequest );
5260 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5262 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5263 goto lend;
5266 session = request->session;
5267 assert(session->hdr.htype == WH_HHTTPSESSION);
5268 hIC = session->appInfo;
5269 assert(hIC->hdr.htype == WH_HINIT);
5271 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5273 WORKREQUEST workRequest;
5274 struct WORKREQ_HTTPSENDREQUESTW *req;
5276 workRequest.asyncproc = AsyncHttpSendRequestProc;
5277 workRequest.hdr = WININET_AddRef( &request->hdr );
5278 req = &workRequest.u.HttpSendRequestW;
5279 if (lpBuffersIn)
5281 DWORD size = 0;
5283 if (lpBuffersIn->lpcszHeader)
5285 if (lpBuffersIn->dwHeadersLength == ~0u)
5286 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5287 else
5288 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5290 req->lpszHeader = heap_alloc(size);
5291 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5293 else req->lpszHeader = NULL;
5295 req->dwHeaderLength = size / sizeof(WCHAR);
5296 req->lpOptional = lpBuffersIn->lpvBuffer;
5297 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5298 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5300 else
5302 req->lpszHeader = NULL;
5303 req->dwHeaderLength = 0;
5304 req->lpOptional = NULL;
5305 req->dwOptionalLength = 0;
5306 req->dwContentLength = 0;
5309 req->bEndRequest = FALSE;
5311 INTERNET_AsyncCall(&workRequest);
5313 * This is from windows.
5315 res = ERROR_IO_PENDING;
5317 else
5319 if (lpBuffersIn)
5320 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5321 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5322 lpBuffersIn->dwBufferTotal, FALSE);
5323 else
5324 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5327 lend:
5328 if ( request )
5329 WININET_Release( &request->hdr );
5331 TRACE("<---\n");
5332 SetLastError(res);
5333 return res == ERROR_SUCCESS;
5336 /***********************************************************************
5337 * HttpSendRequestW (WININET.@)
5339 * Sends the specified request to the HTTP server
5341 * RETURNS
5342 * TRUE on success
5343 * FALSE on failure
5346 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5347 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5349 http_request_t *request;
5350 http_session_t *session = NULL;
5351 appinfo_t *hIC = NULL;
5352 DWORD res = ERROR_SUCCESS;
5354 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5355 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5357 request = (http_request_t*) get_handle_object( hHttpRequest );
5358 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5360 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5361 goto lend;
5364 session = request->session;
5365 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5367 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5368 goto lend;
5371 hIC = session->appInfo;
5372 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5374 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5375 goto lend;
5378 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5380 WORKREQUEST workRequest;
5381 struct WORKREQ_HTTPSENDREQUESTW *req;
5383 workRequest.asyncproc = AsyncHttpSendRequestProc;
5384 workRequest.hdr = WININET_AddRef( &request->hdr );
5385 req = &workRequest.u.HttpSendRequestW;
5386 if (lpszHeaders)
5388 DWORD size;
5390 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5391 else size = dwHeaderLength * sizeof(WCHAR);
5393 req->lpszHeader = heap_alloc(size);
5394 memcpy(req->lpszHeader, lpszHeaders, size);
5396 else
5397 req->lpszHeader = 0;
5398 req->dwHeaderLength = dwHeaderLength;
5399 req->lpOptional = lpOptional;
5400 req->dwOptionalLength = dwOptionalLength;
5401 req->dwContentLength = dwOptionalLength;
5402 req->bEndRequest = TRUE;
5404 INTERNET_AsyncCall(&workRequest);
5406 * This is from windows.
5408 res = ERROR_IO_PENDING;
5410 else
5412 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5413 dwHeaderLength, lpOptional, dwOptionalLength,
5414 dwOptionalLength, TRUE);
5416 lend:
5417 if( request )
5418 WININET_Release( &request->hdr );
5420 SetLastError(res);
5421 return res == ERROR_SUCCESS;
5424 /***********************************************************************
5425 * HttpSendRequestA (WININET.@)
5427 * Sends the specified request to the HTTP server
5429 * RETURNS
5430 * TRUE on success
5431 * FALSE on failure
5434 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5435 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5437 BOOL result;
5438 LPWSTR szHeaders=NULL;
5439 DWORD nLen=dwHeaderLength;
5440 if(lpszHeaders!=NULL)
5442 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5443 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5444 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5446 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5447 heap_free(szHeaders);
5448 return result;
5451 /***********************************************************************
5452 * HTTPSESSION_Destroy (internal)
5454 * Deallocate session handle
5457 static void HTTPSESSION_Destroy(object_header_t *hdr)
5459 http_session_t *session = (http_session_t*) hdr;
5461 TRACE("%p\n", session);
5463 WININET_Release(&session->appInfo->hdr);
5465 heap_free(session->hostName);
5466 heap_free(session->password);
5467 heap_free(session->userName);
5470 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5472 http_session_t *ses = (http_session_t *)hdr;
5474 switch(option) {
5475 case INTERNET_OPTION_HANDLE_TYPE:
5476 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5478 if (*size < sizeof(ULONG))
5479 return ERROR_INSUFFICIENT_BUFFER;
5481 *size = sizeof(DWORD);
5482 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5483 return ERROR_SUCCESS;
5484 case INTERNET_OPTION_CONNECT_TIMEOUT:
5485 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5487 if (*size < sizeof(DWORD))
5488 return ERROR_INSUFFICIENT_BUFFER;
5490 *size = sizeof(DWORD);
5491 *(DWORD *)buffer = ses->connect_timeout;
5492 return ERROR_SUCCESS;
5494 case INTERNET_OPTION_SEND_TIMEOUT:
5495 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5497 if (*size < sizeof(DWORD))
5498 return ERROR_INSUFFICIENT_BUFFER;
5500 *size = sizeof(DWORD);
5501 *(DWORD *)buffer = ses->send_timeout;
5502 return ERROR_SUCCESS;
5504 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5505 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5507 if (*size < sizeof(DWORD))
5508 return ERROR_INSUFFICIENT_BUFFER;
5510 *size = sizeof(DWORD);
5511 *(DWORD *)buffer = ses->receive_timeout;
5512 return ERROR_SUCCESS;
5515 return INET_QueryOption(hdr, option, buffer, size, unicode);
5518 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5520 http_session_t *ses = (http_session_t*)hdr;
5522 switch(option) {
5523 case INTERNET_OPTION_USERNAME:
5525 heap_free(ses->userName);
5526 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5527 return ERROR_SUCCESS;
5529 case INTERNET_OPTION_PASSWORD:
5531 heap_free(ses->password);
5532 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5533 return ERROR_SUCCESS;
5535 case INTERNET_OPTION_CONNECT_TIMEOUT:
5537 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5538 ses->connect_timeout = *(DWORD *)buffer;
5539 return ERROR_SUCCESS;
5541 case INTERNET_OPTION_SEND_TIMEOUT:
5543 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5544 ses->send_timeout = *(DWORD *)buffer;
5545 return ERROR_SUCCESS;
5547 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5549 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5550 ses->receive_timeout = *(DWORD *)buffer;
5551 return ERROR_SUCCESS;
5553 default: break;
5556 return INET_SetOption(hdr, option, buffer, size);
5559 static const object_vtbl_t HTTPSESSIONVtbl = {
5560 HTTPSESSION_Destroy,
5561 NULL,
5562 HTTPSESSION_QueryOption,
5563 HTTPSESSION_SetOption,
5564 NULL,
5565 NULL,
5566 NULL,
5567 NULL,
5568 NULL
5572 /***********************************************************************
5573 * HTTP_Connect (internal)
5575 * Create http session handle
5577 * RETURNS
5578 * HINTERNET a session handle on success
5579 * NULL on failure
5582 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5583 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5584 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5585 DWORD dwInternalFlags, HINTERNET *ret)
5587 http_session_t *session = NULL;
5589 TRACE("-->\n");
5591 if (!lpszServerName || !lpszServerName[0])
5592 return ERROR_INVALID_PARAMETER;
5594 assert( hIC->hdr.htype == WH_HINIT );
5596 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5597 if (!session)
5598 return ERROR_OUTOFMEMORY;
5601 * According to my tests. The name is not resolved until a request is sent
5604 session->hdr.htype = WH_HHTTPSESSION;
5605 session->hdr.dwFlags = dwFlags;
5606 session->hdr.dwContext = dwContext;
5607 session->hdr.dwInternalFlags |= dwInternalFlags;
5609 WININET_AddRef( &hIC->hdr );
5610 session->appInfo = hIC;
5611 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5613 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5614 if(hIC->proxyBypass)
5615 FIXME("Proxy bypass is ignored.\n");
5617 session->hostName = heap_strdupW(lpszServerName);
5618 if (lpszUserName && lpszUserName[0])
5619 session->userName = heap_strdupW(lpszUserName);
5620 if (lpszPassword && lpszPassword[0])
5621 session->password = heap_strdupW(lpszPassword);
5622 session->hostPort = serverPort;
5623 session->connect_timeout = hIC->connect_timeout;
5624 session->send_timeout = INFINITE;
5625 session->receive_timeout = INFINITE;
5627 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5628 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5630 INTERNET_SendCallback(&hIC->hdr, dwContext,
5631 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5632 sizeof(HINTERNET));
5636 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5637 * windows
5640 TRACE("%p --> %p\n", hIC, session);
5642 *ret = session->hdr.hInternet;
5643 return ERROR_SUCCESS;
5646 /***********************************************************************
5647 * HTTP_clear_response_headers (internal)
5649 * clear out any old response headers
5651 static void HTTP_clear_response_headers( http_request_t *request )
5653 DWORD i;
5655 for( i=0; i<request->nCustHeaders; i++)
5657 if( !request->custHeaders[i].lpszField )
5658 continue;
5659 if( !request->custHeaders[i].lpszValue )
5660 continue;
5661 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5662 continue;
5663 HTTP_DeleteCustomHeader( request, i );
5664 i--;
5668 /***********************************************************************
5669 * HTTP_GetResponseHeaders (internal)
5671 * Read server response
5673 * RETURNS
5675 * TRUE on success
5676 * FALSE on error
5678 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5680 INT cbreaks = 0;
5681 WCHAR buffer[MAX_REPLY_LEN];
5682 DWORD buflen = MAX_REPLY_LEN;
5683 BOOL bSuccess = FALSE;
5684 INT rc = 0;
5685 char bufferA[MAX_REPLY_LEN];
5686 LPWSTR status_code = NULL, status_text = NULL;
5687 DWORD cchMaxRawHeaders = 1024;
5688 LPWSTR lpszRawHeaders = NULL;
5689 LPWSTR temp;
5690 DWORD cchRawHeaders = 0;
5691 BOOL codeHundred = FALSE;
5693 TRACE("-->\n");
5695 if(!request->netconn)
5696 goto lend;
5698 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5699 do {
5700 static const WCHAR szHundred[] = {'1','0','0',0};
5702 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5704 buflen = MAX_REPLY_LEN;
5705 if (!read_line(request, bufferA, &buflen))
5706 goto lend;
5708 /* clear old response headers (eg. from a redirect response) */
5709 if (clear) {
5710 HTTP_clear_response_headers( request );
5711 clear = FALSE;
5714 rc += buflen;
5715 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5716 /* check is this a status code line? */
5717 if (!strncmpW(buffer, g_szHttp1_0, 4))
5719 /* split the version from the status code */
5720 status_code = strchrW( buffer, ' ' );
5721 if( !status_code )
5722 goto lend;
5723 *status_code++=0;
5725 /* split the status code from the status text */
5726 status_text = strchrW( status_code, ' ' );
5727 if( !status_text )
5728 goto lend;
5729 *status_text++=0;
5731 request->status_code = atoiW(status_code);
5733 TRACE("version [%s] status code [%s] status text [%s]\n",
5734 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5736 codeHundred = (!strcmpW(status_code, szHundred));
5738 else if (!codeHundred)
5740 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5742 heap_free(request->version);
5743 heap_free(request->statusText);
5745 request->status_code = HTTP_STATUS_OK;
5746 request->version = heap_strdupW(g_szHttp1_0);
5747 request->statusText = heap_strdupW(szOK);
5749 heap_free(request->rawHeaders);
5750 request->rawHeaders = heap_strdupW(szDefaultHeader);
5752 bSuccess = TRUE;
5753 goto lend;
5755 } while (codeHundred);
5757 /* Add status code */
5758 HTTP_ProcessHeader(request, szStatus, status_code,
5759 HTTP_ADDHDR_FLAG_REPLACE);
5761 heap_free(request->version);
5762 heap_free(request->statusText);
5764 request->version = heap_strdupW(buffer);
5765 request->statusText = heap_strdupW(status_text);
5767 /* Restore the spaces */
5768 *(status_code-1) = ' ';
5769 *(status_text-1) = ' ';
5771 /* regenerate raw headers */
5772 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5773 if (!lpszRawHeaders) goto lend;
5775 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5776 cchMaxRawHeaders *= 2;
5777 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5778 if (temp == NULL) goto lend;
5779 lpszRawHeaders = temp;
5780 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5781 cchRawHeaders += (buflen-1);
5782 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5783 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5784 lpszRawHeaders[cchRawHeaders] = '\0';
5786 /* Parse each response line */
5789 buflen = MAX_REPLY_LEN;
5790 if (read_line(request, bufferA, &buflen))
5792 LPWSTR * pFieldAndValue;
5794 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5796 if (!bufferA[0]) break;
5797 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5799 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5800 if (pFieldAndValue)
5802 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5803 cchMaxRawHeaders *= 2;
5804 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5805 if (temp == NULL) goto lend;
5806 lpszRawHeaders = temp;
5807 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5808 cchRawHeaders += (buflen-1);
5809 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5810 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5811 lpszRawHeaders[cchRawHeaders] = '\0';
5813 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5814 HTTP_ADDREQ_FLAG_ADD );
5816 HTTP_FreeTokens(pFieldAndValue);
5819 else
5821 cbreaks++;
5822 if (cbreaks >= 2)
5823 break;
5825 }while(1);
5827 /* make sure the response header is terminated with an empty line. Some apps really
5828 truly care about that empty line being there for some reason. Just add it to the
5829 header. */
5830 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5832 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5833 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5834 if (temp == NULL) goto lend;
5835 lpszRawHeaders = temp;
5838 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5840 heap_free(request->rawHeaders);
5841 request->rawHeaders = lpszRawHeaders;
5842 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5843 bSuccess = TRUE;
5845 lend:
5847 TRACE("<--\n");
5848 if (bSuccess)
5849 return rc;
5850 else
5852 heap_free(lpszRawHeaders);
5853 return 0;
5857 /***********************************************************************
5858 * HTTP_InterpretHttpHeader (internal)
5860 * Parse server response
5862 * RETURNS
5864 * Pointer to array of field, value, NULL on success.
5865 * NULL on error.
5867 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5869 LPWSTR * pTokenPair;
5870 LPWSTR pszColon;
5871 INT len;
5873 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5875 pszColon = strchrW(buffer, ':');
5876 /* must have two tokens */
5877 if (!pszColon)
5879 HTTP_FreeTokens(pTokenPair);
5880 if (buffer[0])
5881 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5882 return NULL;
5885 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5886 if (!pTokenPair[0])
5888 HTTP_FreeTokens(pTokenPair);
5889 return NULL;
5891 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5892 pTokenPair[0][pszColon - buffer] = '\0';
5894 /* skip colon */
5895 pszColon++;
5896 len = strlenW(pszColon);
5897 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5898 if (!pTokenPair[1])
5900 HTTP_FreeTokens(pTokenPair);
5901 return NULL;
5903 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5905 strip_spaces(pTokenPair[0]);
5906 strip_spaces(pTokenPair[1]);
5908 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5909 return pTokenPair;
5912 /***********************************************************************
5913 * HTTP_ProcessHeader (internal)
5915 * Stuff header into header tables according to <dwModifier>
5919 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5921 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5923 LPHTTPHEADERW lphttpHdr = NULL;
5924 INT index = -1;
5925 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5926 DWORD res = ERROR_HTTP_INVALID_HEADER;
5928 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5930 /* REPLACE wins out over ADD */
5931 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5932 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5934 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5935 index = -1;
5936 else
5937 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5939 if (index >= 0)
5941 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5942 return ERROR_HTTP_INVALID_HEADER;
5943 lphttpHdr = &request->custHeaders[index];
5945 else if (value)
5947 HTTPHEADERW hdr;
5949 hdr.lpszField = (LPWSTR)field;
5950 hdr.lpszValue = (LPWSTR)value;
5951 hdr.wFlags = hdr.wCount = 0;
5953 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5954 hdr.wFlags |= HDR_ISREQUEST;
5956 return HTTP_InsertCustomHeader(request, &hdr);
5958 /* no value to delete */
5959 else return ERROR_SUCCESS;
5961 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5962 lphttpHdr->wFlags |= HDR_ISREQUEST;
5963 else
5964 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5966 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5968 HTTP_DeleteCustomHeader( request, index );
5970 if (value)
5972 HTTPHEADERW hdr;
5974 hdr.lpszField = (LPWSTR)field;
5975 hdr.lpszValue = (LPWSTR)value;
5976 hdr.wFlags = hdr.wCount = 0;
5978 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5979 hdr.wFlags |= HDR_ISREQUEST;
5981 return HTTP_InsertCustomHeader(request, &hdr);
5984 return ERROR_SUCCESS;
5986 else if (dwModifier & COALESCEFLAGS)
5988 LPWSTR lpsztmp;
5989 WCHAR ch = 0;
5990 INT len = 0;
5991 INT origlen = strlenW(lphttpHdr->lpszValue);
5992 INT valuelen = strlenW(value);
5994 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5996 ch = ',';
5997 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5999 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6001 ch = ';';
6002 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6005 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6007 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6008 if (lpsztmp)
6010 lphttpHdr->lpszValue = lpsztmp;
6011 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6012 if (ch > 0)
6014 lphttpHdr->lpszValue[origlen] = ch;
6015 origlen++;
6016 lphttpHdr->lpszValue[origlen] = ' ';
6017 origlen++;
6020 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6021 lphttpHdr->lpszValue[len] = '\0';
6022 res = ERROR_SUCCESS;
6024 else
6026 WARN("heap_realloc (%d bytes) failed\n",len+1);
6027 res = ERROR_OUTOFMEMORY;
6030 TRACE("<-- %d\n", res);
6031 return res;
6034 /***********************************************************************
6035 * HTTP_GetCustomHeaderIndex (internal)
6037 * Return index of custom header from header array
6040 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6041 int requested_index, BOOL request_only)
6043 DWORD index;
6045 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6047 for (index = 0; index < request->nCustHeaders; index++)
6049 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6050 continue;
6052 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6053 continue;
6055 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6056 continue;
6058 if (requested_index == 0)
6059 break;
6060 requested_index --;
6063 if (index >= request->nCustHeaders)
6064 index = -1;
6066 TRACE("Return: %d\n", index);
6067 return index;
6071 /***********************************************************************
6072 * HTTP_InsertCustomHeader (internal)
6074 * Insert header into array
6077 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6079 INT count;
6080 LPHTTPHEADERW lph = NULL;
6082 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6083 count = request->nCustHeaders + 1;
6084 if (count > 1)
6085 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6086 else
6087 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6089 if (!lph)
6090 return ERROR_OUTOFMEMORY;
6092 request->custHeaders = lph;
6093 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6094 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6095 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6096 request->custHeaders[count-1].wCount= lpHdr->wCount;
6097 request->nCustHeaders++;
6099 return ERROR_SUCCESS;
6103 /***********************************************************************
6104 * HTTP_DeleteCustomHeader (internal)
6106 * Delete header from array
6107 * If this function is called, the indexs may change.
6109 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6111 if( request->nCustHeaders <= 0 )
6112 return FALSE;
6113 if( index >= request->nCustHeaders )
6114 return FALSE;
6115 request->nCustHeaders--;
6117 heap_free(request->custHeaders[index].lpszField);
6118 heap_free(request->custHeaders[index].lpszValue);
6120 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6121 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6122 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6124 return TRUE;
6128 /***********************************************************************
6129 * HTTP_VerifyValidHeader (internal)
6131 * Verify the given header is not invalid for the given http request
6134 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6136 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6137 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6138 return ERROR_HTTP_INVALID_HEADER;
6140 return ERROR_SUCCESS;
6143 /***********************************************************************
6144 * IsHostInProxyBypassList (@)
6146 * Undocumented
6149 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6151 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6152 return FALSE;
6155 /***********************************************************************
6156 * InternetShowSecurityInfoByURLA (@)
6158 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6160 FIXME("stub: %s %p\n", url, window);
6161 return FALSE;
6164 /***********************************************************************
6165 * InternetShowSecurityInfoByURLW (@)
6167 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6169 FIXME("stub: %s %p\n", debugstr_w(url), window);
6170 return FALSE;
6173 /***********************************************************************
6174 * ShowX509EncodedCertificate (@)
6176 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6178 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6179 cert, len);
6180 DWORD ret;
6182 if (certContext)
6184 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6186 memset(&view, 0, sizeof(view));
6187 view.hwndParent = parent;
6188 view.pCertContext = certContext;
6189 if (CryptUIDlgViewCertificateW(&view, NULL))
6190 ret = ERROR_SUCCESS;
6191 else
6192 ret = GetLastError();
6193 CertFreeCertificateContext(certContext);
6195 else
6196 ret = GetLastError();
6197 return ret;