wininet: Properly set flags in InternetSetOption(INTERNET_OPTION_SECURITY_FLAGS).
[wine/multimedia.git] / dlls / wininet / http.c
blobe263d8a213bc8a48e94ba7e0aea87fc9c1ca1a8d
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 heap_free(server->name);
248 heap_free(server);
251 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
253 server_t *iter, *server = NULL;
255 EnterCriticalSection(&connection_pool_cs);
257 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
258 if(iter->port == port && !strcmpW(iter->name, name)) {
259 server = iter;
260 server_addref(server);
261 break;
265 if(!server) {
266 server = heap_alloc(sizeof(*server));
267 if(server) {
268 server->addr_len = 0;
269 server->ref = 2; /* list reference and return */
270 server->port = port;
271 server->security_flags = 0;
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);
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 INTERNET_OPTION_SECURITY_FLAGS:
2195 DWORD flags;
2197 if (!buffer || size != sizeof(DWORD))
2198 return ERROR_INVALID_PARAMETER;
2199 flags = *(DWORD *)buffer;
2200 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2201 flags &= SECURITY_SET_MASK;
2202 req->security_flags |= flags;
2203 if(req->netconn)
2204 req->netconn->security_flags |= flags;
2205 return ERROR_SUCCESS;
2207 case INTERNET_OPTION_CONNECT_TIMEOUT:
2208 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2209 req->connect_timeout = *(DWORD *)buffer;
2210 return ERROR_SUCCESS;
2212 case INTERNET_OPTION_SEND_TIMEOUT:
2213 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2214 req->send_timeout = *(DWORD *)buffer;
2215 return ERROR_SUCCESS;
2217 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2218 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2219 req->receive_timeout = *(DWORD *)buffer;
2220 return ERROR_SUCCESS;
2222 case INTERNET_OPTION_USERNAME:
2223 heap_free(req->session->userName);
2224 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2225 return ERROR_SUCCESS;
2227 case INTERNET_OPTION_PASSWORD:
2228 heap_free(req->session->password);
2229 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2230 return ERROR_SUCCESS;
2231 case INTERNET_OPTION_HTTP_DECODING:
2232 if(size != sizeof(BOOL))
2233 return ERROR_INVALID_PARAMETER;
2234 req->decoding = *(BOOL*)buffer;
2235 return ERROR_SUCCESS;
2238 return INET_SetOption(hdr, option, buffer, size);
2241 /* read some more data into the read buffer (the read section must be held) */
2242 static DWORD read_more_data( http_request_t *req, int maxlen )
2244 DWORD res;
2245 int len;
2247 if (req->read_pos)
2249 /* move existing data to the start of the buffer */
2250 if(req->read_size)
2251 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2252 req->read_pos = 0;
2255 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2257 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2258 maxlen - req->read_size, 0, &len );
2259 if(res == ERROR_SUCCESS)
2260 req->read_size += len;
2262 return res;
2265 /* remove some amount of data from the read buffer (the read section must be held) */
2266 static void remove_data( http_request_t *req, int count )
2268 if (!(req->read_size -= count)) req->read_pos = 0;
2269 else req->read_pos += count;
2272 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2274 int count, bytes_read, pos = 0;
2275 DWORD res;
2277 EnterCriticalSection( &req->read_section );
2278 for (;;)
2280 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2282 if (eol)
2284 count = eol - (req->read_buf + req->read_pos);
2285 bytes_read = count + 1;
2287 else count = bytes_read = req->read_size;
2289 count = min( count, *len - pos );
2290 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2291 pos += count;
2292 remove_data( req, bytes_read );
2293 if (eol) break;
2295 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2297 *len = 0;
2298 TRACE( "returning empty string %u\n", res);
2299 LeaveCriticalSection( &req->read_section );
2300 INTERNET_SetLastError(res);
2301 return FALSE;
2304 LeaveCriticalSection( &req->read_section );
2306 if (pos < *len)
2308 if (pos && buffer[pos - 1] == '\r') pos--;
2309 *len = pos + 1;
2311 buffer[*len - 1] = 0;
2312 TRACE( "returning %s\n", debugstr_a(buffer));
2313 return TRUE;
2316 /* check if we have reached the end of the data to read (the read section must be held) */
2317 static BOOL end_of_read_data( http_request_t *req )
2319 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2322 /* fetch some more data into the read buffer (the read section must be held) */
2323 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2325 DWORD res, read=0;
2327 if(req->read_size == sizeof(req->read_buf))
2328 return ERROR_SUCCESS;
2330 if(req->read_pos) {
2331 if(req->read_size)
2332 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2333 req->read_pos = 0;
2336 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2337 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2338 req->read_size += read;
2340 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2341 if(read_bytes)
2342 *read_bytes = read;
2343 return res;
2346 /* return the size of data available to be read immediately (the read section must be held) */
2347 static DWORD get_avail_data( http_request_t *req )
2349 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2352 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2354 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2355 DWORD avail = 0;
2357 if(req->netconn)
2358 NETCON_query_data_available(req->netconn, &avail);
2359 return netconn_stream->content_length == ~0u
2360 ? avail
2361 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2364 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2366 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2367 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2370 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2371 DWORD *read, read_mode_t read_mode)
2373 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2374 int len = 0;
2376 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2378 if(read_mode == READMODE_NOBLOCK)
2379 size = min(size, netconn_get_avail_data(stream, req));
2381 if(size && req->netconn) {
2382 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2383 len = 0;
2384 if(!len)
2385 netconn_stream->content_length = netconn_stream->content_read;
2388 netconn_stream->content_read += *read = len;
2389 TRACE("read %u bytes\n", len);
2390 return ERROR_SUCCESS;
2393 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2395 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2396 BYTE buf[1024];
2397 DWORD avail;
2398 int len;
2400 if(netconn_end_of_data(stream, req))
2401 return TRUE;
2403 do {
2404 avail = netconn_get_avail_data(stream, req);
2405 if(!avail)
2406 return FALSE;
2408 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2409 return FALSE;
2411 netconn_stream->content_read += len;
2412 }while(netconn_stream->content_read < netconn_stream->content_length);
2414 return TRUE;
2417 static void netconn_destroy(data_stream_t *stream)
2421 static const data_stream_vtbl_t netconn_stream_vtbl = {
2422 netconn_get_avail_data,
2423 netconn_end_of_data,
2424 netconn_read,
2425 netconn_drain_content,
2426 netconn_destroy
2429 /* read some more data into the read buffer (the read section must be held) */
2430 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2432 DWORD res;
2433 int len;
2435 if (stream->buf_pos)
2437 /* move existing data to the start of the buffer */
2438 if(stream->buf_size)
2439 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2440 stream->buf_pos = 0;
2443 if (maxlen == -1) maxlen = sizeof(stream->buf);
2445 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2446 maxlen - stream->buf_size, 0, &len );
2447 if(res == ERROR_SUCCESS)
2448 stream->buf_size += len;
2450 return res;
2453 /* remove some amount of data from the read buffer (the read section must be held) */
2454 static void remove_chunked_data(chunked_stream_t *stream, int count)
2456 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2457 else stream->buf_pos += count;
2460 /* discard data contents until we reach end of line (the read section must be held) */
2461 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2463 DWORD res;
2467 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2468 if (eol)
2470 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2471 break;
2473 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2474 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2475 } while (stream->buf_size);
2476 return ERROR_SUCCESS;
2479 /* read the size of the next chunk (the read section must be held) */
2480 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2482 /* TODOO */
2483 DWORD chunk_size = 0, res;
2485 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2486 return res;
2488 for (;;)
2490 while (stream->buf_size)
2492 char ch = stream->buf[stream->buf_pos];
2493 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2494 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2495 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2496 else if (ch == ';' || ch == '\r' || ch == '\n')
2498 TRACE( "reading %u byte chunk\n", chunk_size );
2499 stream->chunk_size = chunk_size;
2500 req->contentLength += chunk_size;
2501 return discard_chunked_eol(stream, req);
2503 remove_chunked_data(stream, 1);
2505 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2506 if (!stream->buf_size)
2508 stream->chunk_size = 0;
2509 return ERROR_SUCCESS;
2514 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2516 /* Allow reading only from read buffer */
2517 return 0;
2520 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2522 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2523 return !chunked_stream->chunk_size;
2526 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2527 DWORD *read, read_mode_t read_mode)
2529 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2530 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2532 if(chunked_stream->chunk_size == ~0u) {
2533 res = start_next_chunk(chunked_stream, req);
2534 if(res != ERROR_SUCCESS)
2535 return res;
2538 while(size && chunked_stream->chunk_size) {
2539 if(chunked_stream->buf_size) {
2540 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2542 /* this could block */
2543 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2544 break;
2546 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2547 remove_chunked_data(chunked_stream, read_bytes);
2548 }else {
2549 read_bytes = min(size, chunked_stream->chunk_size);
2551 if(read_mode == READMODE_NOBLOCK) {
2552 DWORD avail;
2554 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2555 break;
2556 if(read_bytes > avail)
2557 read_bytes = avail;
2559 /* this could block */
2560 if(read_bytes == chunked_stream->chunk_size)
2561 break;
2564 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2565 if(res != ERROR_SUCCESS)
2566 break;
2569 chunked_stream->chunk_size -= read_bytes;
2570 size -= read_bytes;
2571 ret_read += read_bytes;
2572 if(!chunked_stream->chunk_size) {
2573 assert(read_mode != READMODE_NOBLOCK);
2574 res = start_next_chunk(chunked_stream, req);
2575 if(res != ERROR_SUCCESS)
2576 break;
2579 if(read_mode == READMODE_ASYNC)
2580 read_mode = READMODE_NOBLOCK;
2583 TRACE("read %u bytes\n", ret_read);
2584 *read = ret_read;
2585 return res;
2588 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2590 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2592 /* FIXME: we can do better */
2593 return !chunked_stream->chunk_size;
2596 static void chunked_destroy(data_stream_t *stream)
2598 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2599 heap_free(chunked_stream);
2602 static const data_stream_vtbl_t chunked_stream_vtbl = {
2603 chunked_get_avail_data,
2604 chunked_end_of_data,
2605 chunked_read,
2606 chunked_drain_content,
2607 chunked_destroy
2610 /* set the request content length based on the headers */
2611 static DWORD set_content_length(http_request_t *request)
2613 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2614 WCHAR encoding[20];
2615 DWORD size;
2617 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2618 request->contentLength = request->netconn_stream.content_length = 0;
2619 return ERROR_SUCCESS;
2622 size = sizeof(request->contentLength);
2623 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2624 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2625 request->contentLength = ~0u;
2626 request->netconn_stream.content_length = request->contentLength;
2627 request->netconn_stream.content_read = request->read_size;
2629 size = sizeof(encoding);
2630 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2631 !strcmpiW(encoding, szChunked))
2633 chunked_stream_t *chunked_stream;
2635 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2636 if(!chunked_stream)
2637 return ERROR_OUTOFMEMORY;
2639 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2640 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2641 chunked_stream->chunk_size = ~0u;
2643 if(request->read_size) {
2644 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2645 chunked_stream->buf_size = request->read_size;
2646 request->read_size = request->read_pos = 0;
2649 request->data_stream = &chunked_stream->data_stream;
2650 request->contentLength = ~0u;
2651 request->read_chunked = TRUE;
2654 if(request->decoding) {
2655 int encoding_idx;
2657 static const WCHAR gzipW[] = {'g','z','i','p',0};
2659 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2660 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2661 return init_gzip_stream(request);
2664 return ERROR_SUCCESS;
2667 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2669 INTERNET_ASYNC_RESULT iar;
2671 iar.dwResult = result;
2672 iar.dwError = error;
2674 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2675 sizeof(INTERNET_ASYNC_RESULT));
2678 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2680 DWORD res, read = 0, avail = 0;
2681 read_mode_t mode;
2683 TRACE("%p\n", req);
2685 EnterCriticalSection( &req->read_section );
2687 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2688 res = refill_read_buffer(req, mode, &read);
2689 if(res == ERROR_SUCCESS && !first_notif)
2690 avail = get_avail_data(req);
2692 LeaveCriticalSection( &req->read_section );
2694 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2695 WARN("res %u read %u, closing connection\n", res, read);
2696 http_release_netconn(req, FALSE);
2699 if(res == ERROR_SUCCESS)
2700 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2701 else
2702 send_request_complete(req, 0, res);
2705 /* read data from the http connection (the read section must be held) */
2706 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2708 DWORD current_read = 0, ret_read = 0;
2709 read_mode_t read_mode;
2710 DWORD res = ERROR_SUCCESS;
2712 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2714 EnterCriticalSection( &req->read_section );
2716 if(req->read_size) {
2717 ret_read = min(size, req->read_size);
2718 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2719 req->read_size -= ret_read;
2720 req->read_pos += ret_read;
2721 if(read_mode == READMODE_ASYNC)
2722 read_mode = READMODE_NOBLOCK;
2725 if(ret_read < size) {
2726 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2727 ret_read += current_read;
2730 LeaveCriticalSection( &req->read_section );
2732 *read = ret_read;
2733 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2735 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2736 BOOL res;
2737 DWORD written;
2739 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2740 if(!res)
2741 WARN("WriteFile failed: %u\n", GetLastError());
2744 if(size && !ret_read)
2745 http_release_netconn(req, res == ERROR_SUCCESS);
2747 return res;
2751 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2753 http_request_t *req = (http_request_t*)hdr;
2754 DWORD res;
2756 EnterCriticalSection( &req->read_section );
2757 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2758 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2760 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2761 if(res == ERROR_SUCCESS)
2762 res = hdr->dwError;
2763 LeaveCriticalSection( &req->read_section );
2765 return res;
2768 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2770 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2771 http_request_t *req = (http_request_t*)workRequest->hdr;
2772 DWORD res;
2774 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2776 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2777 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2779 send_request_complete(req, res == ERROR_SUCCESS, res);
2782 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2783 DWORD flags, DWORD_PTR context)
2785 http_request_t *req = (http_request_t*)hdr;
2786 DWORD res, size, read, error = ERROR_SUCCESS;
2788 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2789 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2791 if (buffers->dwStructSize != sizeof(*buffers))
2792 return ERROR_INVALID_PARAMETER;
2794 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2796 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2798 WORKREQUEST workRequest;
2800 if (TryEnterCriticalSection( &req->read_section ))
2802 if (get_avail_data(req))
2804 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2805 &buffers->dwBufferLength, FALSE);
2806 size = buffers->dwBufferLength;
2807 LeaveCriticalSection( &req->read_section );
2808 goto done;
2810 LeaveCriticalSection( &req->read_section );
2813 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2814 workRequest.hdr = WININET_AddRef(&req->hdr);
2815 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2817 INTERNET_AsyncCall(&workRequest);
2819 return ERROR_IO_PENDING;
2822 read = 0;
2823 size = buffers->dwBufferLength;
2825 EnterCriticalSection( &req->read_section );
2826 if(hdr->dwError == ERROR_SUCCESS)
2827 hdr->dwError = INTERNET_HANDLE_IN_USE;
2828 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2829 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2831 while(1) {
2832 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2833 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2834 if(res != ERROR_SUCCESS)
2835 break;
2837 read += buffers->dwBufferLength;
2838 if(read == size || end_of_read_data(req))
2839 break;
2841 LeaveCriticalSection( &req->read_section );
2843 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2844 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2845 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2846 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2848 EnterCriticalSection( &req->read_section );
2851 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2852 hdr->dwError = ERROR_SUCCESS;
2853 else
2854 error = hdr->dwError;
2856 LeaveCriticalSection( &req->read_section );
2857 size = buffers->dwBufferLength;
2858 buffers->dwBufferLength = read;
2860 done:
2861 if (res == ERROR_SUCCESS) {
2862 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2863 &size, sizeof(size));
2866 return res==ERROR_SUCCESS ? error : res;
2869 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2871 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2872 http_request_t *req = (http_request_t*)workRequest->hdr;
2873 DWORD res;
2875 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2877 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2878 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2880 send_request_complete(req, res == ERROR_SUCCESS, res);
2883 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2884 DWORD flags, DWORD_PTR context)
2887 http_request_t *req = (http_request_t*)hdr;
2888 DWORD res, size, read, error = ERROR_SUCCESS;
2890 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2891 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2893 if (buffers->dwStructSize != sizeof(*buffers))
2894 return ERROR_INVALID_PARAMETER;
2896 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2898 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2900 WORKREQUEST workRequest;
2902 if (TryEnterCriticalSection( &req->read_section ))
2904 if (get_avail_data(req))
2906 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2907 &buffers->dwBufferLength, FALSE);
2908 size = buffers->dwBufferLength;
2909 LeaveCriticalSection( &req->read_section );
2910 goto done;
2912 LeaveCriticalSection( &req->read_section );
2915 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2916 workRequest.hdr = WININET_AddRef(&req->hdr);
2917 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2919 INTERNET_AsyncCall(&workRequest);
2921 return ERROR_IO_PENDING;
2924 read = 0;
2925 size = buffers->dwBufferLength;
2927 EnterCriticalSection( &req->read_section );
2928 if(hdr->dwError == ERROR_SUCCESS)
2929 hdr->dwError = INTERNET_HANDLE_IN_USE;
2930 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2931 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2933 while(1) {
2934 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2935 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2936 if(res != ERROR_SUCCESS)
2937 break;
2939 read += buffers->dwBufferLength;
2940 if(read == size || end_of_read_data(req))
2941 break;
2943 LeaveCriticalSection( &req->read_section );
2945 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2946 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2947 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2948 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2950 EnterCriticalSection( &req->read_section );
2953 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2954 hdr->dwError = ERROR_SUCCESS;
2955 else
2956 error = hdr->dwError;
2958 LeaveCriticalSection( &req->read_section );
2959 size = buffers->dwBufferLength;
2960 buffers->dwBufferLength = read;
2962 done:
2963 if (res == ERROR_SUCCESS) {
2964 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2965 &size, sizeof(size));
2968 return res==ERROR_SUCCESS ? error : res;
2971 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2973 DWORD res;
2974 http_request_t *request = (http_request_t*)hdr;
2976 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2978 *written = 0;
2979 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2980 if (res == ERROR_SUCCESS)
2981 request->bytesWritten += *written;
2983 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2984 return res;
2987 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2989 http_request_t *req = (http_request_t*)workRequest->hdr;
2991 HTTP_ReceiveRequestData(req, FALSE);
2994 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2996 http_request_t *req = (http_request_t*)hdr;
2998 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3000 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3002 WORKREQUEST workRequest;
3004 /* never wait, if we can't enter the section we queue an async request right away */
3005 if (TryEnterCriticalSection( &req->read_section ))
3007 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3008 if ((*available = get_avail_data( req ))) goto done;
3009 if (end_of_read_data( req )) goto done;
3010 LeaveCriticalSection( &req->read_section );
3013 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3014 workRequest.hdr = WININET_AddRef( &req->hdr );
3016 INTERNET_AsyncCall(&workRequest);
3018 return ERROR_IO_PENDING;
3021 EnterCriticalSection( &req->read_section );
3023 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3025 refill_read_buffer( req, READMODE_ASYNC, NULL );
3026 *available = get_avail_data( req );
3029 done:
3030 LeaveCriticalSection( &req->read_section );
3032 TRACE( "returning %u\n", *available );
3033 return ERROR_SUCCESS;
3036 static const object_vtbl_t HTTPREQVtbl = {
3037 HTTPREQ_Destroy,
3038 HTTPREQ_CloseConnection,
3039 HTTPREQ_QueryOption,
3040 HTTPREQ_SetOption,
3041 HTTPREQ_ReadFile,
3042 HTTPREQ_ReadFileExA,
3043 HTTPREQ_ReadFileExW,
3044 HTTPREQ_WriteFile,
3045 HTTPREQ_QueryDataAvailable,
3046 NULL
3049 /***********************************************************************
3050 * HTTP_HttpOpenRequestW (internal)
3052 * Open a HTTP request handle
3054 * RETURNS
3055 * HINTERNET a HTTP request handle on success
3056 * NULL on failure
3059 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3060 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3061 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3062 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3064 appinfo_t *hIC = session->appInfo;
3065 http_request_t *request;
3066 INTERNET_PORT port;
3067 DWORD len, res = ERROR_SUCCESS;
3069 TRACE("-->\n");
3071 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3072 if(!request)
3073 return ERROR_OUTOFMEMORY;
3075 request->hdr.htype = WH_HHTTPREQ;
3076 request->hdr.dwFlags = dwFlags;
3077 request->hdr.dwContext = dwContext;
3078 request->contentLength = ~0u;
3080 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3081 request->data_stream = &request->netconn_stream.data_stream;
3082 request->connect_timeout = session->connect_timeout;
3083 request->send_timeout = session->send_timeout;
3084 request->receive_timeout = session->receive_timeout;
3086 InitializeCriticalSection( &request->read_section );
3087 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3089 WININET_AddRef( &session->hdr );
3090 request->session = session;
3091 list_add_head( &session->hdr.children, &request->hdr.entry );
3093 port = session->hostPort;
3094 if(port == INTERNET_INVALID_PORT_NUMBER)
3095 port = dwFlags & INTERNET_FLAG_SECURE ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3097 request->server = get_server(session->hostName, port);
3098 if(!request->server) {
3099 WININET_Release(&request->hdr);
3100 return ERROR_OUTOFMEMORY;
3103 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3104 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3105 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3106 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3108 if (lpszObjectName && *lpszObjectName) {
3109 HRESULT rc;
3111 len = 0;
3112 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3113 if (rc != E_POINTER)
3114 len = strlenW(lpszObjectName)+1;
3115 request->path = heap_alloc(len*sizeof(WCHAR));
3116 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3117 URL_ESCAPE_SPACES_ONLY);
3118 if (rc != S_OK)
3120 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3121 strcpyW(request->path,lpszObjectName);
3123 }else {
3124 static const WCHAR slashW[] = {'/',0};
3126 request->path = heap_strdupW(slashW);
3129 if (lpszReferrer && *lpszReferrer)
3130 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3132 if (lpszAcceptTypes)
3134 int i;
3135 for (i = 0; lpszAcceptTypes[i]; i++)
3137 if (!*lpszAcceptTypes[i]) continue;
3138 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3139 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3140 HTTP_ADDHDR_FLAG_REQ |
3141 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3145 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3146 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3148 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3149 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3150 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3152 WCHAR *host_name;
3154 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3156 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3157 if (!host_name) {
3158 res = ERROR_OUTOFMEMORY;
3159 goto lend;
3162 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3163 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3164 heap_free(host_name);
3166 else
3167 HTTP_ProcessHeader(request, hostW, session->hostName,
3168 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3170 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3171 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3172 INTERNET_DEFAULT_HTTPS_PORT :
3173 INTERNET_DEFAULT_HTTP_PORT);
3175 if (hIC->proxy && hIC->proxy[0])
3176 HTTP_DealWithProxy( hIC, session, request );
3178 INTERNET_SendCallback(&session->hdr, dwContext,
3179 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3180 sizeof(HINTERNET));
3182 lend:
3183 TRACE("<-- %u (%p)\n", res, request);
3185 if(res != ERROR_SUCCESS) {
3186 WININET_Release( &request->hdr );
3187 *ret = NULL;
3188 return res;
3191 *ret = request->hdr.hInternet;
3192 return ERROR_SUCCESS;
3195 /***********************************************************************
3196 * HttpOpenRequestW (WININET.@)
3198 * Open a HTTP request handle
3200 * RETURNS
3201 * HINTERNET a HTTP request handle on success
3202 * NULL on failure
3205 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3206 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3207 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3208 DWORD dwFlags, DWORD_PTR dwContext)
3210 http_session_t *session;
3211 HINTERNET handle = NULL;
3212 DWORD res;
3214 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3215 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3216 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3217 dwFlags, dwContext);
3218 if(lpszAcceptTypes!=NULL)
3220 int i;
3221 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3222 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3225 session = (http_session_t*) get_handle_object( hHttpSession );
3226 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3228 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3229 goto lend;
3233 * My tests seem to show that the windows version does not
3234 * become asynchronous until after this point. And anyhow
3235 * if this call was asynchronous then how would you get the
3236 * necessary HINTERNET pointer returned by this function.
3239 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3240 lpszVersion, lpszReferrer, lpszAcceptTypes,
3241 dwFlags, dwContext, &handle);
3242 lend:
3243 if( session )
3244 WININET_Release( &session->hdr );
3245 TRACE("returning %p\n", handle);
3246 if(res != ERROR_SUCCESS)
3247 SetLastError(res);
3248 return handle;
3251 static const LPCWSTR header_lookup[] = {
3252 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3253 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3254 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3255 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3256 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3257 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3258 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3259 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3260 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3261 szDate, /* HTTP_QUERY_DATE = 9 */
3262 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3263 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3264 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3265 szURI, /* HTTP_QUERY_URI = 13 */
3266 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3267 NULL, /* HTTP_QUERY_COST = 15 */
3268 NULL, /* HTTP_QUERY_LINK = 16 */
3269 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3270 NULL, /* HTTP_QUERY_VERSION = 18 */
3271 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3272 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3273 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3274 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3275 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3276 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3277 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3278 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3279 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3280 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3281 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3282 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3283 NULL, /* HTTP_QUERY_FROM = 31 */
3284 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3285 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3286 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3287 szReferer, /* HTTP_QUERY_REFERER = 35 */
3288 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3289 szServer, /* HTTP_QUERY_SERVER = 37 */
3290 NULL, /* HTTP_TITLE = 38 */
3291 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3292 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3293 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3294 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3295 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3296 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3297 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3298 NULL, /* HTTP_QUERY_REFRESH = 46 */
3299 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3300 szAge, /* HTTP_QUERY_AGE = 48 */
3301 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3302 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3303 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3304 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3305 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3306 szETag, /* HTTP_QUERY_ETAG = 54 */
3307 hostW, /* HTTP_QUERY_HOST = 55 */
3308 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3309 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3310 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3311 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3312 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3313 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3314 szRange, /* HTTP_QUERY_RANGE = 62 */
3315 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3316 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3317 szVary, /* HTTP_QUERY_VARY = 65 */
3318 szVia, /* HTTP_QUERY_VIA = 66 */
3319 szWarning, /* HTTP_QUERY_WARNING = 67 */
3320 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3321 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3322 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3325 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3327 /***********************************************************************
3328 * HTTP_HttpQueryInfoW (internal)
3330 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3331 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3333 LPHTTPHEADERW lphttpHdr = NULL;
3334 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3335 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3336 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3337 INT index = -1;
3339 /* Find requested header structure */
3340 switch (level)
3342 case HTTP_QUERY_CUSTOM:
3343 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3344 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3345 break;
3346 case HTTP_QUERY_RAW_HEADERS_CRLF:
3348 LPWSTR headers;
3349 DWORD len = 0;
3350 DWORD res = ERROR_INVALID_PARAMETER;
3352 if (request_only)
3353 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3354 else
3355 headers = request->rawHeaders;
3357 if (headers)
3358 len = strlenW(headers) * sizeof(WCHAR);
3360 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3362 len += sizeof(WCHAR);
3363 res = ERROR_INSUFFICIENT_BUFFER;
3365 else if (lpBuffer)
3367 if (headers)
3368 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3369 else
3371 len = strlenW(szCrLf) * sizeof(WCHAR);
3372 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3374 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3375 res = ERROR_SUCCESS;
3377 *lpdwBufferLength = len;
3379 if (request_only) heap_free(headers);
3380 return res;
3382 case HTTP_QUERY_RAW_HEADERS:
3384 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3385 DWORD i, size = 0;
3386 LPWSTR pszString = lpBuffer;
3388 for (i = 0; ppszRawHeaderLines[i]; i++)
3389 size += strlenW(ppszRawHeaderLines[i]) + 1;
3391 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3393 HTTP_FreeTokens(ppszRawHeaderLines);
3394 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3395 return ERROR_INSUFFICIENT_BUFFER;
3397 if (pszString)
3399 for (i = 0; ppszRawHeaderLines[i]; i++)
3401 DWORD len = strlenW(ppszRawHeaderLines[i]);
3402 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3403 pszString += len+1;
3405 *pszString = '\0';
3406 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3408 *lpdwBufferLength = size * sizeof(WCHAR);
3409 HTTP_FreeTokens(ppszRawHeaderLines);
3411 return ERROR_SUCCESS;
3413 case HTTP_QUERY_STATUS_TEXT:
3414 if (request->statusText)
3416 DWORD len = strlenW(request->statusText);
3417 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3419 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3420 return ERROR_INSUFFICIENT_BUFFER;
3422 if (lpBuffer)
3424 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3425 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3427 *lpdwBufferLength = len * sizeof(WCHAR);
3428 return ERROR_SUCCESS;
3430 break;
3431 case HTTP_QUERY_VERSION:
3432 if (request->version)
3434 DWORD len = strlenW(request->version);
3435 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3437 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3438 return ERROR_INSUFFICIENT_BUFFER;
3440 if (lpBuffer)
3442 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3443 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3445 *lpdwBufferLength = len * sizeof(WCHAR);
3446 return ERROR_SUCCESS;
3448 break;
3449 case HTTP_QUERY_CONTENT_ENCODING:
3450 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3451 requested_index,request_only);
3452 break;
3453 case HTTP_QUERY_STATUS_CODE: {
3454 DWORD res = ERROR_SUCCESS;
3456 if(request_only)
3457 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3459 if(requested_index)
3460 break;
3462 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3463 if(*lpdwBufferLength >= sizeof(DWORD))
3464 *(DWORD*)lpBuffer = request->status_code;
3465 else
3466 res = ERROR_INSUFFICIENT_BUFFER;
3467 *lpdwBufferLength = sizeof(DWORD);
3468 }else {
3469 WCHAR buf[12];
3470 DWORD size;
3471 static const WCHAR formatW[] = {'%','u',0};
3473 size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3475 if(size <= *lpdwBufferLength)
3476 memcpy(lpBuffer, buf, size);
3477 else
3478 res = ERROR_INSUFFICIENT_BUFFER;
3480 *lpdwBufferLength = size;
3482 return res;
3484 default:
3485 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3487 if (level < LAST_TABLE_HEADER && header_lookup[level])
3488 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3489 requested_index,request_only);
3492 if (index >= 0)
3493 lphttpHdr = &request->custHeaders[index];
3495 /* Ensure header satisfies requested attributes */
3496 if (!lphttpHdr ||
3497 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3498 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3500 return ERROR_HTTP_HEADER_NOT_FOUND;
3503 if (lpdwIndex) (*lpdwIndex)++;
3505 /* coalesce value to requested type */
3506 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3508 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3509 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3511 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3513 time_t tmpTime;
3514 struct tm tmpTM;
3515 SYSTEMTIME *STHook;
3517 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3519 tmpTM = *gmtime(&tmpTime);
3520 STHook = (SYSTEMTIME *)lpBuffer;
3521 STHook->wDay = tmpTM.tm_mday;
3522 STHook->wHour = tmpTM.tm_hour;
3523 STHook->wMilliseconds = 0;
3524 STHook->wMinute = tmpTM.tm_min;
3525 STHook->wDayOfWeek = tmpTM.tm_wday;
3526 STHook->wMonth = tmpTM.tm_mon + 1;
3527 STHook->wSecond = tmpTM.tm_sec;
3528 STHook->wYear = tmpTM.tm_year;
3530 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3531 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3532 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3534 else if (lphttpHdr->lpszValue)
3536 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3538 if (len > *lpdwBufferLength)
3540 *lpdwBufferLength = len;
3541 return ERROR_INSUFFICIENT_BUFFER;
3543 if (lpBuffer)
3545 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3546 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3548 *lpdwBufferLength = len - sizeof(WCHAR);
3550 return ERROR_SUCCESS;
3553 /***********************************************************************
3554 * HttpQueryInfoW (WININET.@)
3556 * Queries for information about an HTTP request
3558 * RETURNS
3559 * TRUE on success
3560 * FALSE on failure
3563 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3564 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3566 http_request_t *request;
3567 DWORD res;
3569 if (TRACE_ON(wininet)) {
3570 #define FE(x) { x, #x }
3571 static const wininet_flag_info query_flags[] = {
3572 FE(HTTP_QUERY_MIME_VERSION),
3573 FE(HTTP_QUERY_CONTENT_TYPE),
3574 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3575 FE(HTTP_QUERY_CONTENT_ID),
3576 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3577 FE(HTTP_QUERY_CONTENT_LENGTH),
3578 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3579 FE(HTTP_QUERY_ALLOW),
3580 FE(HTTP_QUERY_PUBLIC),
3581 FE(HTTP_QUERY_DATE),
3582 FE(HTTP_QUERY_EXPIRES),
3583 FE(HTTP_QUERY_LAST_MODIFIED),
3584 FE(HTTP_QUERY_MESSAGE_ID),
3585 FE(HTTP_QUERY_URI),
3586 FE(HTTP_QUERY_DERIVED_FROM),
3587 FE(HTTP_QUERY_COST),
3588 FE(HTTP_QUERY_LINK),
3589 FE(HTTP_QUERY_PRAGMA),
3590 FE(HTTP_QUERY_VERSION),
3591 FE(HTTP_QUERY_STATUS_CODE),
3592 FE(HTTP_QUERY_STATUS_TEXT),
3593 FE(HTTP_QUERY_RAW_HEADERS),
3594 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3595 FE(HTTP_QUERY_CONNECTION),
3596 FE(HTTP_QUERY_ACCEPT),
3597 FE(HTTP_QUERY_ACCEPT_CHARSET),
3598 FE(HTTP_QUERY_ACCEPT_ENCODING),
3599 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3600 FE(HTTP_QUERY_AUTHORIZATION),
3601 FE(HTTP_QUERY_CONTENT_ENCODING),
3602 FE(HTTP_QUERY_FORWARDED),
3603 FE(HTTP_QUERY_FROM),
3604 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3605 FE(HTTP_QUERY_LOCATION),
3606 FE(HTTP_QUERY_ORIG_URI),
3607 FE(HTTP_QUERY_REFERER),
3608 FE(HTTP_QUERY_RETRY_AFTER),
3609 FE(HTTP_QUERY_SERVER),
3610 FE(HTTP_QUERY_TITLE),
3611 FE(HTTP_QUERY_USER_AGENT),
3612 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3613 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3614 FE(HTTP_QUERY_ACCEPT_RANGES),
3615 FE(HTTP_QUERY_SET_COOKIE),
3616 FE(HTTP_QUERY_COOKIE),
3617 FE(HTTP_QUERY_REQUEST_METHOD),
3618 FE(HTTP_QUERY_REFRESH),
3619 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3620 FE(HTTP_QUERY_AGE),
3621 FE(HTTP_QUERY_CACHE_CONTROL),
3622 FE(HTTP_QUERY_CONTENT_BASE),
3623 FE(HTTP_QUERY_CONTENT_LOCATION),
3624 FE(HTTP_QUERY_CONTENT_MD5),
3625 FE(HTTP_QUERY_CONTENT_RANGE),
3626 FE(HTTP_QUERY_ETAG),
3627 FE(HTTP_QUERY_HOST),
3628 FE(HTTP_QUERY_IF_MATCH),
3629 FE(HTTP_QUERY_IF_NONE_MATCH),
3630 FE(HTTP_QUERY_IF_RANGE),
3631 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3632 FE(HTTP_QUERY_MAX_FORWARDS),
3633 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3634 FE(HTTP_QUERY_RANGE),
3635 FE(HTTP_QUERY_TRANSFER_ENCODING),
3636 FE(HTTP_QUERY_UPGRADE),
3637 FE(HTTP_QUERY_VARY),
3638 FE(HTTP_QUERY_VIA),
3639 FE(HTTP_QUERY_WARNING),
3640 FE(HTTP_QUERY_CUSTOM)
3642 static const wininet_flag_info modifier_flags[] = {
3643 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3644 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3645 FE(HTTP_QUERY_FLAG_NUMBER),
3646 FE(HTTP_QUERY_FLAG_COALESCE)
3648 #undef FE
3649 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3650 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3651 DWORD i;
3653 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3654 TRACE(" Attribute:");
3655 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3656 if (query_flags[i].val == info) {
3657 TRACE(" %s", query_flags[i].name);
3658 break;
3661 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3662 TRACE(" Unknown (%08x)", info);
3665 TRACE(" Modifier:");
3666 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3667 if (modifier_flags[i].val & info_mod) {
3668 TRACE(" %s", modifier_flags[i].name);
3669 info_mod &= ~ modifier_flags[i].val;
3673 if (info_mod) {
3674 TRACE(" Unknown (%08x)", info_mod);
3676 TRACE("\n");
3679 request = (http_request_t*) get_handle_object( hHttpRequest );
3680 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3682 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3683 goto lend;
3686 if (lpBuffer == NULL)
3687 *lpdwBufferLength = 0;
3688 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3689 lpBuffer, lpdwBufferLength, lpdwIndex);
3691 lend:
3692 if( request )
3693 WININET_Release( &request->hdr );
3695 TRACE("%u <--\n", res);
3696 if(res != ERROR_SUCCESS)
3697 SetLastError(res);
3698 return res == ERROR_SUCCESS;
3701 /***********************************************************************
3702 * HttpQueryInfoA (WININET.@)
3704 * Queries for information about an HTTP request
3706 * RETURNS
3707 * TRUE on success
3708 * FALSE on failure
3711 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3712 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3714 BOOL result;
3715 DWORD len;
3716 WCHAR* bufferW;
3718 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3719 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3721 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3722 lpdwBufferLength, lpdwIndex );
3725 if (lpBuffer)
3727 DWORD alloclen;
3728 len = (*lpdwBufferLength)*sizeof(WCHAR);
3729 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3731 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3732 if (alloclen < len)
3733 alloclen = len;
3735 else
3736 alloclen = len;
3737 bufferW = heap_alloc(alloclen);
3738 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3739 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3740 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3741 } else
3743 bufferW = NULL;
3744 len = 0;
3747 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3748 &len, lpdwIndex );
3749 if( result )
3751 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3752 lpBuffer, *lpdwBufferLength, NULL, NULL );
3753 *lpdwBufferLength = len - 1;
3755 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3757 else
3758 /* since the strings being returned from HttpQueryInfoW should be
3759 * only ASCII characters, it is reasonable to assume that all of
3760 * the Unicode characters can be reduced to a single byte */
3761 *lpdwBufferLength = len / sizeof(WCHAR);
3763 heap_free( bufferW );
3764 return result;
3767 /***********************************************************************
3768 * HTTP_GetRedirectURL (internal)
3770 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3772 static WCHAR szHttp[] = {'h','t','t','p',0};
3773 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3774 http_session_t *session = request->session;
3775 URL_COMPONENTSW urlComponents;
3776 DWORD url_length = 0;
3777 LPWSTR orig_url;
3778 LPWSTR combined_url;
3780 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3781 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3782 urlComponents.dwSchemeLength = 0;
3783 urlComponents.lpszHostName = session->hostName;
3784 urlComponents.dwHostNameLength = 0;
3785 urlComponents.nPort = session->hostPort;
3786 urlComponents.lpszUserName = session->userName;
3787 urlComponents.dwUserNameLength = 0;
3788 urlComponents.lpszPassword = NULL;
3789 urlComponents.dwPasswordLength = 0;
3790 urlComponents.lpszUrlPath = request->path;
3791 urlComponents.dwUrlPathLength = 0;
3792 urlComponents.lpszExtraInfo = NULL;
3793 urlComponents.dwExtraInfoLength = 0;
3795 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3796 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3797 return NULL;
3799 orig_url = heap_alloc(url_length);
3801 /* convert from bytes to characters */
3802 url_length = url_length / sizeof(WCHAR) - 1;
3803 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3805 heap_free(orig_url);
3806 return NULL;
3809 url_length = 0;
3810 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3811 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3813 heap_free(orig_url);
3814 return NULL;
3816 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3818 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3820 heap_free(orig_url);
3821 heap_free(combined_url);
3822 return NULL;
3824 heap_free(orig_url);
3825 return combined_url;
3829 /***********************************************************************
3830 * HTTP_HandleRedirect (internal)
3832 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3834 http_session_t *session = request->session;
3835 appinfo_t *hIC = session->appInfo;
3836 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3837 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3838 int index;
3840 if(lpszUrl[0]=='/')
3842 /* if it's an absolute path, keep the same session info */
3843 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3845 else
3847 URL_COMPONENTSW urlComponents;
3848 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3849 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3850 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3851 BOOL custom_port = FALSE;
3853 static WCHAR httpW[] = {'h','t','t','p',0};
3854 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3856 userName[0] = 0;
3857 hostName[0] = 0;
3858 protocol[0] = 0;
3860 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3861 urlComponents.lpszScheme = protocol;
3862 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3863 urlComponents.lpszHostName = hostName;
3864 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3865 urlComponents.lpszUserName = userName;
3866 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3867 urlComponents.lpszPassword = NULL;
3868 urlComponents.dwPasswordLength = 0;
3869 urlComponents.lpszUrlPath = path;
3870 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3871 urlComponents.lpszExtraInfo = NULL;
3872 urlComponents.dwExtraInfoLength = 0;
3873 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3874 return INTERNET_GetLastError();
3876 if(!strcmpiW(protocol, httpW)) {
3877 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3878 TRACE("redirect from secure page to non-secure page\n");
3879 /* FIXME: warn about from secure redirect to non-secure page */
3880 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3883 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3884 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3885 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3886 custom_port = TRUE;
3887 }else if(!strcmpiW(protocol, httpsW)) {
3888 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3889 TRACE("redirect from non-secure page to secure page\n");
3890 /* FIXME: notify about redirect to secure page */
3891 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3894 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3895 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3896 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3897 custom_port = TRUE;
3900 heap_free(session->hostName);
3902 if(custom_port) {
3903 int len;
3904 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3905 len = lstrlenW(hostName);
3906 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3907 session->hostName = heap_alloc(len*sizeof(WCHAR));
3908 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3910 else
3911 session->hostName = heap_strdupW(hostName);
3913 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3915 heap_free(session->userName);
3916 session->userName = NULL;
3917 if (userName[0])
3918 session->userName = heap_strdupW(userName);
3920 reset_data_stream(request);
3922 if(!using_proxy && (strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort)) {
3923 server_t *new_server;
3925 new_server = get_server(hostName, urlComponents.nPort);
3926 server_release(request->server);
3927 request->server = new_server;
3930 heap_free(request->path);
3931 request->path=NULL;
3932 if (*path)
3934 DWORD needed = 0;
3935 HRESULT rc;
3937 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3938 if (rc != E_POINTER)
3939 needed = strlenW(path)+1;
3940 request->path = heap_alloc(needed*sizeof(WCHAR));
3941 rc = UrlEscapeW(path, request->path, &needed,
3942 URL_ESCAPE_SPACES_ONLY);
3943 if (rc != S_OK)
3945 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3946 strcpyW(request->path,path);
3950 /* Remove custom content-type/length headers on redirects. */
3951 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3952 if (0 <= index)
3953 HTTP_DeleteCustomHeader(request, index);
3954 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3955 if (0 <= index)
3956 HTTP_DeleteCustomHeader(request, index);
3958 return ERROR_SUCCESS;
3961 /***********************************************************************
3962 * HTTP_build_req (internal)
3964 * concatenate all the strings in the request together
3966 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3968 LPCWSTR *t;
3969 LPWSTR str;
3971 for( t = list; *t ; t++ )
3972 len += strlenW( *t );
3973 len++;
3975 str = heap_alloc(len*sizeof(WCHAR));
3976 *str = 0;
3978 for( t = list; *t ; t++ )
3979 strcatW( str, *t );
3981 return str;
3984 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3986 LPWSTR lpszPath;
3987 LPWSTR requestString;
3988 INT len;
3989 INT cnt;
3990 INT responseLen;
3991 char *ascii_req;
3992 DWORD res;
3993 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3994 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3995 http_session_t *session = request->session;
3997 TRACE("\n");
3999 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
4000 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
4001 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
4002 heap_free( lpszPath );
4004 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4005 NULL, 0, NULL, NULL );
4006 len--; /* the nul terminator isn't needed */
4007 ascii_req = heap_alloc(len);
4008 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4009 heap_free( requestString );
4011 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4013 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4014 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4015 heap_free( ascii_req );
4016 if (res != ERROR_SUCCESS)
4017 return res;
4019 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4020 if (!responseLen)
4021 return ERROR_HTTP_INVALID_HEADER;
4023 return ERROR_SUCCESS;
4026 static void HTTP_InsertCookies(http_request_t *request)
4028 DWORD cookie_size, size, cnt = 0;
4029 HTTPHEADERW *host;
4030 WCHAR *cookies;
4032 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4034 host = HTTP_GetHeader(request, hostW);
4035 if(!host)
4036 return;
4038 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4039 return;
4041 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4042 if(!(cookies = heap_alloc(size)))
4043 return;
4045 cnt += sprintfW(cookies, cookieW);
4046 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4047 strcatW(cookies, szCrLf);
4049 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4051 heap_free(cookies);
4054 static WORD HTTP_ParseWkday(LPCWSTR day)
4056 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4057 { 'm','o','n',0 },
4058 { 't','u','e',0 },
4059 { 'w','e','d',0 },
4060 { 't','h','u',0 },
4061 { 'f','r','i',0 },
4062 { 's','a','t',0 }};
4063 int i;
4064 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4065 if (!strcmpiW(day, days[i]))
4066 return i;
4068 /* Invalid */
4069 return 7;
4072 static WORD HTTP_ParseMonth(LPCWSTR month)
4074 static const WCHAR jan[] = { 'j','a','n',0 };
4075 static const WCHAR feb[] = { 'f','e','b',0 };
4076 static const WCHAR mar[] = { 'm','a','r',0 };
4077 static const WCHAR apr[] = { 'a','p','r',0 };
4078 static const WCHAR may[] = { 'm','a','y',0 };
4079 static const WCHAR jun[] = { 'j','u','n',0 };
4080 static const WCHAR jul[] = { 'j','u','l',0 };
4081 static const WCHAR aug[] = { 'a','u','g',0 };
4082 static const WCHAR sep[] = { 's','e','p',0 };
4083 static const WCHAR oct[] = { 'o','c','t',0 };
4084 static const WCHAR nov[] = { 'n','o','v',0 };
4085 static const WCHAR dec[] = { 'd','e','c',0 };
4087 if (!strcmpiW(month, jan)) return 1;
4088 if (!strcmpiW(month, feb)) return 2;
4089 if (!strcmpiW(month, mar)) return 3;
4090 if (!strcmpiW(month, apr)) return 4;
4091 if (!strcmpiW(month, may)) return 5;
4092 if (!strcmpiW(month, jun)) return 6;
4093 if (!strcmpiW(month, jul)) return 7;
4094 if (!strcmpiW(month, aug)) return 8;
4095 if (!strcmpiW(month, sep)) return 9;
4096 if (!strcmpiW(month, oct)) return 10;
4097 if (!strcmpiW(month, nov)) return 11;
4098 if (!strcmpiW(month, dec)) return 12;
4099 /* Invalid */
4100 return 0;
4103 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4104 * optionally preceded by whitespace.
4105 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4106 * st, and sets *str to the first character after the time format.
4108 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4110 LPCWSTR ptr = *str;
4111 WCHAR *nextPtr;
4112 unsigned long num;
4114 while (isspaceW(*ptr))
4115 ptr++;
4117 num = strtoulW(ptr, &nextPtr, 10);
4118 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4120 ERR("unexpected time format %s\n", debugstr_w(ptr));
4121 return FALSE;
4123 if (num > 23)
4125 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4126 return FALSE;
4128 ptr = nextPtr + 1;
4129 st->wHour = (WORD)num;
4130 num = strtoulW(ptr, &nextPtr, 10);
4131 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4133 ERR("unexpected time format %s\n", debugstr_w(ptr));
4134 return FALSE;
4136 if (num > 59)
4138 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4139 return FALSE;
4141 ptr = nextPtr + 1;
4142 st->wMinute = (WORD)num;
4143 num = strtoulW(ptr, &nextPtr, 10);
4144 if (!nextPtr || nextPtr <= ptr)
4146 ERR("unexpected time format %s\n", debugstr_w(ptr));
4147 return FALSE;
4149 if (num > 59)
4151 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4152 return FALSE;
4154 ptr = nextPtr + 1;
4155 *str = ptr;
4156 st->wSecond = (WORD)num;
4157 return TRUE;
4160 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4162 static const WCHAR gmt[]= { 'G','M','T',0 };
4163 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4164 LPCWSTR ptr;
4165 SYSTEMTIME st = { 0 };
4166 unsigned long num;
4168 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4169 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4170 *dayPtr = *ptr;
4171 *dayPtr = 0;
4172 st.wDayOfWeek = HTTP_ParseWkday(day);
4173 if (st.wDayOfWeek >= 7)
4175 ERR("unexpected weekday %s\n", debugstr_w(day));
4176 return FALSE;
4179 while (isspaceW(*ptr))
4180 ptr++;
4182 for (monthPtr = month; !isspace(*ptr) &&
4183 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4184 monthPtr++, ptr++)
4185 *monthPtr = *ptr;
4186 *monthPtr = 0;
4187 st.wMonth = HTTP_ParseMonth(month);
4188 if (!st.wMonth || st.wMonth > 12)
4190 ERR("unexpected month %s\n", debugstr_w(month));
4191 return FALSE;
4194 while (isspaceW(*ptr))
4195 ptr++;
4197 num = strtoulW(ptr, &nextPtr, 10);
4198 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4200 ERR("unexpected day %s\n", debugstr_w(ptr));
4201 return FALSE;
4203 ptr = nextPtr;
4204 st.wDay = (WORD)num;
4206 while (isspaceW(*ptr))
4207 ptr++;
4209 if (!HTTP_ParseTime(&st, &ptr))
4210 return FALSE;
4212 while (isspaceW(*ptr))
4213 ptr++;
4215 num = strtoulW(ptr, &nextPtr, 10);
4216 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4218 ERR("unexpected year %s\n", debugstr_w(ptr));
4219 return FALSE;
4221 ptr = nextPtr;
4222 st.wYear = (WORD)num;
4224 while (isspaceW(*ptr))
4225 ptr++;
4227 /* asctime() doesn't report a timezone, but some web servers do, so accept
4228 * with or without GMT.
4230 if (*ptr && strcmpW(ptr, gmt))
4232 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4233 return FALSE;
4235 return SystemTimeToFileTime(&st, ft);
4238 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4240 static const WCHAR gmt[]= { 'G','M','T',0 };
4241 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4242 LPCWSTR ptr;
4243 unsigned long num;
4244 SYSTEMTIME st = { 0 };
4246 ptr = strchrW(value, ',');
4247 if (!ptr)
4248 return FALSE;
4249 if (ptr - value != 3)
4251 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4252 return FALSE;
4254 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4255 day[3] = 0;
4256 st.wDayOfWeek = HTTP_ParseWkday(day);
4257 if (st.wDayOfWeek > 6)
4259 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4260 return FALSE;
4262 ptr++;
4264 while (isspaceW(*ptr))
4265 ptr++;
4267 num = strtoulW(ptr, &nextPtr, 10);
4268 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4270 WARN("unexpected day %s\n", debugstr_w(value));
4271 return FALSE;
4273 ptr = nextPtr;
4274 st.wDay = (WORD)num;
4276 while (isspaceW(*ptr))
4277 ptr++;
4279 for (monthPtr = month; !isspace(*ptr) &&
4280 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4281 monthPtr++, ptr++)
4282 *monthPtr = *ptr;
4283 *monthPtr = 0;
4284 st.wMonth = HTTP_ParseMonth(month);
4285 if (!st.wMonth || st.wMonth > 12)
4287 WARN("unexpected month %s\n", debugstr_w(month));
4288 return FALSE;
4291 while (isspaceW(*ptr))
4292 ptr++;
4294 num = strtoulW(ptr, &nextPtr, 10);
4295 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4297 ERR("unexpected year %s\n", debugstr_w(value));
4298 return FALSE;
4300 ptr = nextPtr;
4301 st.wYear = (WORD)num;
4303 if (!HTTP_ParseTime(&st, &ptr))
4304 return FALSE;
4306 while (isspaceW(*ptr))
4307 ptr++;
4309 if (strcmpW(ptr, gmt))
4311 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4312 return FALSE;
4314 return SystemTimeToFileTime(&st, ft);
4317 static WORD HTTP_ParseWeekday(LPCWSTR day)
4319 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4320 { 'm','o','n','d','a','y',0 },
4321 { 't','u','e','s','d','a','y',0 },
4322 { 'w','e','d','n','e','s','d','a','y',0 },
4323 { 't','h','u','r','s','d','a','y',0 },
4324 { 'f','r','i','d','a','y',0 },
4325 { 's','a','t','u','r','d','a','y',0 }};
4326 int i;
4327 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4328 if (!strcmpiW(day, days[i]))
4329 return i;
4331 /* Invalid */
4332 return 7;
4335 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4337 static const WCHAR gmt[]= { 'G','M','T',0 };
4338 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4339 LPCWSTR ptr;
4340 unsigned long num;
4341 SYSTEMTIME st = { 0 };
4343 ptr = strchrW(value, ',');
4344 if (!ptr)
4345 return FALSE;
4346 if (ptr - value == 3)
4348 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4349 day[3] = 0;
4350 st.wDayOfWeek = HTTP_ParseWkday(day);
4351 if (st.wDayOfWeek > 6)
4353 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4354 return FALSE;
4357 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4359 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4360 day[ptr - value + 1] = 0;
4361 st.wDayOfWeek = HTTP_ParseWeekday(day);
4362 if (st.wDayOfWeek > 6)
4364 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4365 return FALSE;
4368 else
4370 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4371 return FALSE;
4373 ptr++;
4375 while (isspaceW(*ptr))
4376 ptr++;
4378 num = strtoulW(ptr, &nextPtr, 10);
4379 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4381 ERR("unexpected day %s\n", debugstr_w(value));
4382 return FALSE;
4384 ptr = nextPtr;
4385 st.wDay = (WORD)num;
4387 if (*ptr != '-')
4389 ERR("unexpected month format %s\n", debugstr_w(ptr));
4390 return FALSE;
4392 ptr++;
4394 for (monthPtr = month; *ptr != '-' &&
4395 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4396 monthPtr++, ptr++)
4397 *monthPtr = *ptr;
4398 *monthPtr = 0;
4399 st.wMonth = HTTP_ParseMonth(month);
4400 if (!st.wMonth || st.wMonth > 12)
4402 ERR("unexpected month %s\n", debugstr_w(month));
4403 return FALSE;
4406 if (*ptr != '-')
4408 ERR("unexpected year format %s\n", debugstr_w(ptr));
4409 return FALSE;
4411 ptr++;
4413 num = strtoulW(ptr, &nextPtr, 10);
4414 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4416 ERR("unexpected year %s\n", debugstr_w(value));
4417 return FALSE;
4419 ptr = nextPtr;
4420 st.wYear = (WORD)num;
4422 if (!HTTP_ParseTime(&st, &ptr))
4423 return FALSE;
4425 while (isspaceW(*ptr))
4426 ptr++;
4428 if (strcmpW(ptr, gmt))
4430 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4431 return FALSE;
4433 return SystemTimeToFileTime(&st, ft);
4436 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4438 static const WCHAR zero[] = { '0',0 };
4439 BOOL ret;
4441 if (!strcmpW(value, zero))
4443 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4444 ret = TRUE;
4446 else if (strchrW(value, ','))
4448 ret = HTTP_ParseRfc1123Date(value, ft);
4449 if (!ret)
4451 ret = HTTP_ParseRfc850Date(value, ft);
4452 if (!ret)
4453 ERR("unexpected date format %s\n", debugstr_w(value));
4456 else
4458 ret = HTTP_ParseDateAsAsctime(value, ft);
4459 if (!ret)
4460 ERR("unexpected date format %s\n", debugstr_w(value));
4462 return ret;
4465 static void HTTP_ProcessExpires(http_request_t *request)
4467 BOOL expirationFound = FALSE;
4468 int headerIndex;
4470 /* Look for a Cache-Control header with a max-age directive, as it takes
4471 * precedence over the Expires header.
4473 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4474 if (headerIndex != -1)
4476 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4477 LPWSTR ptr;
4479 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4481 LPWSTR comma = strchrW(ptr, ','), end, equal;
4483 if (comma)
4484 end = comma;
4485 else
4486 end = ptr + strlenW(ptr);
4487 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4489 if (*equal == '=')
4491 static const WCHAR max_age[] = {
4492 'm','a','x','-','a','g','e',0 };
4494 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4496 LPWSTR nextPtr;
4497 unsigned long age;
4499 age = strtoulW(equal + 1, &nextPtr, 10);
4500 if (nextPtr > equal + 1)
4502 LARGE_INTEGER ft;
4504 NtQuerySystemTime( &ft );
4505 /* Age is in seconds, FILETIME resolution is in
4506 * 100 nanosecond intervals.
4508 ft.QuadPart += age * (ULONGLONG)1000000;
4509 request->expires.dwLowDateTime = ft.u.LowPart;
4510 request->expires.dwHighDateTime = ft.u.HighPart;
4511 expirationFound = TRUE;
4515 if (comma)
4517 ptr = comma + 1;
4518 while (isspaceW(*ptr))
4519 ptr++;
4521 else
4522 ptr = NULL;
4525 if (!expirationFound)
4527 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4528 if (headerIndex != -1)
4530 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4531 FILETIME ft;
4533 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4535 expirationFound = TRUE;
4536 request->expires = ft;
4540 if (!expirationFound)
4542 LARGE_INTEGER t;
4544 /* With no known age, default to 10 minutes until expiration. */
4545 NtQuerySystemTime( &t );
4546 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4547 request->expires.dwLowDateTime = t.u.LowPart;
4548 request->expires.dwHighDateTime = t.u.HighPart;
4552 static void HTTP_ProcessLastModified(http_request_t *request)
4554 int headerIndex;
4556 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4557 if (headerIndex != -1)
4559 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4560 FILETIME ft;
4562 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4563 request->last_modified = ft;
4567 static void http_process_keep_alive(http_request_t *req)
4569 int index;
4571 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4572 if(index != -1)
4573 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4574 else
4575 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4578 static void HTTP_CacheRequest(http_request_t *request)
4580 WCHAR url[INTERNET_MAX_URL_LENGTH];
4581 WCHAR cacheFileName[MAX_PATH+1];
4582 BOOL b;
4584 b = HTTP_GetRequestURL(request, url);
4585 if(!b) {
4586 WARN("Could not get URL\n");
4587 return;
4590 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4591 if(b) {
4592 heap_free(request->cacheFile);
4593 CloseHandle(request->hCacheFile);
4595 request->cacheFile = heap_strdupW(cacheFileName);
4596 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4597 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4598 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4599 WARN("Could not create file: %u\n", GetLastError());
4600 request->hCacheFile = NULL;
4602 }else {
4603 WARN("Could not create cache entry: %08x\n", GetLastError());
4607 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4609 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4610 netconn_t *netconn = NULL;
4611 DWORD res;
4613 assert(!request->netconn);
4614 reset_data_stream(request);
4616 res = HTTP_ResolveName(request);
4617 if(res != ERROR_SUCCESS)
4618 return res;
4620 EnterCriticalSection(&connection_pool_cs);
4622 while(!list_empty(&request->server->conn_pool)) {
4623 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4624 list_remove(&netconn->pool_entry);
4626 if(NETCON_is_alive(netconn))
4627 break;
4629 TRACE("connection %p closed during idle\n", netconn);
4630 free_netconn(netconn);
4631 netconn = NULL;
4634 LeaveCriticalSection(&connection_pool_cs);
4636 if(netconn) {
4637 TRACE("<-- reusing %p netconn\n", netconn);
4638 request->netconn = netconn;
4639 *reusing = TRUE;
4640 return ERROR_SUCCESS;
4643 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4644 INTERNET_STATUS_CONNECTING_TO_SERVER,
4645 request->server->addr_str,
4646 strlen(request->server->addr_str)+1);
4648 res = create_netconn(is_https, request->server, request->security_flags,
4649 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4650 request->connect_timeout, &netconn);
4651 if(res != ERROR_SUCCESS) {
4652 ERR("create_netconn failed: %u\n", res);
4653 return res;
4656 request->netconn = netconn;
4658 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4659 INTERNET_STATUS_CONNECTED_TO_SERVER,
4660 request->server->addr_str, strlen(request->server->addr_str)+1);
4662 if(is_https) {
4663 /* Note: we differ from Microsoft's WinINet here. they seem to have
4664 * a bug that causes no status callbacks to be sent when starting
4665 * a tunnel to a proxy server using the CONNECT verb. i believe our
4666 * behaviour to be more correct and to not cause any incompatibilities
4667 * because using a secure connection through a proxy server is a rare
4668 * case that would be hard for anyone to depend on */
4669 if(request->session->appInfo->proxy)
4670 res = HTTP_SecureProxyConnect(request);
4671 if(res == ERROR_SUCCESS)
4672 res = NETCON_secure_connect(request->netconn);
4675 if(res != ERROR_SUCCESS) {
4676 http_release_netconn(request, FALSE);
4677 return res;
4680 *reusing = FALSE;
4681 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4682 return ERROR_SUCCESS;
4685 /***********************************************************************
4686 * HTTP_HttpSendRequestW (internal)
4688 * Sends the specified request to the HTTP server
4690 * RETURNS
4691 * ERROR_SUCCESS on success
4692 * win32 error code on failure
4695 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4696 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4697 DWORD dwContentLength, BOOL bEndRequest)
4699 INT cnt;
4700 BOOL redirected = FALSE;
4701 LPWSTR requestString = NULL;
4702 INT responseLen;
4703 BOOL loop_next;
4704 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4705 static const WCHAR szContentLength[] =
4706 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4707 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4708 DWORD res;
4710 TRACE("--> %p\n", request);
4712 assert(request->hdr.htype == WH_HHTTPREQ);
4714 /* if the verb is NULL default to GET */
4715 if (!request->verb)
4716 request->verb = heap_strdupW(szGET);
4718 if (dwContentLength || strcmpW(request->verb, szGET))
4720 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4721 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4722 request->bytesToWrite = dwContentLength;
4724 if (request->session->appInfo->agent)
4726 WCHAR *agent_header;
4727 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4728 int len;
4730 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4731 agent_header = heap_alloc(len * sizeof(WCHAR));
4732 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4734 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4735 heap_free(agent_header);
4737 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4739 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4740 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4742 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4744 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4745 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4746 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4749 /* add the headers the caller supplied */
4750 if( lpszHeaders && dwHeaderLength )
4751 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4755 DWORD len;
4756 BOOL reusing_connection;
4757 char *ascii_req;
4759 loop_next = FALSE;
4761 /* like native, just in case the caller forgot to call InternetReadFile
4762 * for all the data */
4763 drain_content(request);
4764 if(redirected) {
4765 request->contentLength = ~0u;
4766 request->bytesToWrite = 0;
4769 if (TRACE_ON(wininet))
4771 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4772 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4775 HTTP_FixURL(request);
4776 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4778 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4780 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4781 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4783 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4784 HTTP_InsertCookies(request);
4786 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4788 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4789 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4790 heap_free(url);
4792 else
4793 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4796 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4798 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4799 break;
4801 /* send the request as ASCII, tack on the optional data */
4802 if (!lpOptional || redirected)
4803 dwOptionalLength = 0;
4804 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4805 NULL, 0, NULL, NULL );
4806 ascii_req = heap_alloc(len + dwOptionalLength);
4807 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4808 ascii_req, len, NULL, NULL );
4809 if( lpOptional )
4810 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4811 len = (len + dwOptionalLength - 1);
4812 ascii_req[len] = 0;
4813 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4815 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4816 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4818 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4819 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4820 heap_free( ascii_req );
4821 if(res != ERROR_SUCCESS) {
4822 TRACE("send failed: %u\n", res);
4823 if(!reusing_connection)
4824 break;
4825 http_release_netconn(request, FALSE);
4826 loop_next = TRUE;
4827 continue;
4830 request->bytesWritten = dwOptionalLength;
4832 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4833 INTERNET_STATUS_REQUEST_SENT,
4834 &len, sizeof(DWORD));
4836 if (bEndRequest)
4838 DWORD dwBufferSize;
4840 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4841 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4843 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4844 /* FIXME: We should know that connection is closed before sending
4845 * headers. Otherwise wrong callbacks are executed */
4846 if(!responseLen && reusing_connection) {
4847 TRACE("Connection closed by server, reconnecting\n");
4848 http_release_netconn(request, FALSE);
4849 loop_next = TRUE;
4850 continue;
4853 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4854 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4855 sizeof(DWORD));
4857 http_process_keep_alive(request);
4858 HTTP_ProcessCookies(request);
4859 HTTP_ProcessExpires(request);
4860 HTTP_ProcessLastModified(request);
4862 res = set_content_length(request);
4863 if(res != ERROR_SUCCESS)
4864 goto lend;
4865 if(!request->contentLength)
4866 http_release_netconn(request, TRUE);
4868 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4870 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4871 dwBufferSize=sizeof(szNewLocation);
4872 switch(request->status_code) {
4873 case HTTP_STATUS_REDIRECT:
4874 case HTTP_STATUS_MOVED:
4875 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4876 case HTTP_STATUS_REDIRECT_METHOD:
4877 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4878 break;
4880 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4881 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4883 heap_free(request->verb);
4884 request->verb = heap_strdupW(szGET);
4886 drain_content(request);
4887 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4889 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4890 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4891 res = HTTP_HandleRedirect(request, new_url);
4892 if (res == ERROR_SUCCESS)
4894 heap_free(requestString);
4895 loop_next = TRUE;
4897 heap_free( new_url );
4899 redirected = TRUE;
4902 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4904 WCHAR szAuthValue[2048];
4905 dwBufferSize=2048;
4906 if (request->status_code == HTTP_STATUS_DENIED)
4908 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4909 DWORD dwIndex = 0;
4910 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4912 if (HTTP_DoAuthorization(request, szAuthValue,
4913 &request->authInfo,
4914 request->session->userName,
4915 request->session->password,
4916 Host->lpszValue))
4918 heap_free(requestString);
4919 loop_next = TRUE;
4920 break;
4924 if(!loop_next) {
4925 TRACE("Cleaning wrong authorization data\n");
4926 destroy_authinfo(request->authInfo);
4927 request->authInfo = NULL;
4930 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4932 DWORD dwIndex = 0;
4933 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4935 if (HTTP_DoAuthorization(request, szAuthValue,
4936 &request->proxyAuthInfo,
4937 request->session->appInfo->proxyUsername,
4938 request->session->appInfo->proxyPassword,
4939 NULL))
4941 loop_next = TRUE;
4942 break;
4946 if(!loop_next) {
4947 TRACE("Cleaning wrong proxy authorization data\n");
4948 destroy_authinfo(request->proxyAuthInfo);
4949 request->proxyAuthInfo = NULL;
4954 else
4955 res = ERROR_SUCCESS;
4957 while (loop_next);
4959 if(res == ERROR_SUCCESS)
4960 HTTP_CacheRequest(request);
4962 lend:
4963 heap_free(requestString);
4965 /* TODO: send notification for P3P header */
4967 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4969 if (res == ERROR_SUCCESS) {
4970 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4971 HTTP_ReceiveRequestData(request, TRUE);
4972 else
4973 send_request_complete(request,
4974 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4975 }else {
4976 send_request_complete(request, 0, res);
4980 TRACE("<--\n");
4981 return res;
4984 /***********************************************************************
4986 * Helper functions for the HttpSendRequest(Ex) functions
4989 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4991 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4992 http_request_t *request = (http_request_t*) workRequest->hdr;
4994 TRACE("%p\n", request);
4996 HTTP_HttpSendRequestW(request, req->lpszHeader,
4997 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4998 req->dwContentLength, req->bEndRequest);
5000 heap_free(req->lpszHeader);
5004 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5006 DWORD dwBufferSize;
5007 INT responseLen;
5008 DWORD res = ERROR_SUCCESS;
5010 if(!request->netconn) {
5011 WARN("Not connected\n");
5012 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5013 return ERROR_INTERNET_OPERATION_CANCELLED;
5016 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5017 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5019 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5020 if (!responseLen)
5021 res = ERROR_HTTP_HEADER_NOT_FOUND;
5023 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5024 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5026 /* process cookies here. Is this right? */
5027 http_process_keep_alive(request);
5028 HTTP_ProcessCookies(request);
5029 HTTP_ProcessExpires(request);
5030 HTTP_ProcessLastModified(request);
5032 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5033 if(!request->contentLength)
5034 http_release_netconn(request, TRUE);
5037 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5039 switch(request->status_code) {
5040 case HTTP_STATUS_REDIRECT:
5041 case HTTP_STATUS_MOVED:
5042 case HTTP_STATUS_REDIRECT_METHOD:
5043 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5044 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5045 dwBufferSize=sizeof(szNewLocation);
5046 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5047 break;
5049 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5050 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5052 heap_free(request->verb);
5053 request->verb = heap_strdupW(szGET);
5055 drain_content(request);
5056 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5058 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5059 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5060 res = HTTP_HandleRedirect(request, new_url);
5061 if (res == ERROR_SUCCESS)
5062 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5063 heap_free( new_url );
5069 if (res == ERROR_SUCCESS && request->contentLength)
5070 HTTP_ReceiveRequestData(request, TRUE);
5071 else
5072 send_request_complete(request, res == ERROR_SUCCESS, res);
5074 return res;
5077 /***********************************************************************
5078 * HttpEndRequestA (WININET.@)
5080 * Ends an HTTP request that was started by HttpSendRequestEx
5082 * RETURNS
5083 * TRUE if successful
5084 * FALSE on failure
5087 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5088 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5090 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5092 if (lpBuffersOut)
5094 SetLastError(ERROR_INVALID_PARAMETER);
5095 return FALSE;
5098 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5101 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5103 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5104 http_request_t *request = (http_request_t*)work->hdr;
5106 TRACE("%p\n", request);
5108 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5111 /***********************************************************************
5112 * HttpEndRequestW (WININET.@)
5114 * Ends an HTTP request that was started by HttpSendRequestEx
5116 * RETURNS
5117 * TRUE if successful
5118 * FALSE on failure
5121 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5122 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5124 http_request_t *request;
5125 DWORD res;
5127 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5129 if (lpBuffersOut)
5131 SetLastError(ERROR_INVALID_PARAMETER);
5132 return FALSE;
5135 request = (http_request_t*) get_handle_object( hRequest );
5137 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5139 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5140 if (request)
5141 WININET_Release( &request->hdr );
5142 return FALSE;
5144 request->hdr.dwFlags |= dwFlags;
5146 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5148 WORKREQUEST work;
5149 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5151 work.asyncproc = AsyncHttpEndRequestProc;
5152 work.hdr = WININET_AddRef( &request->hdr );
5154 work_endrequest = &work.u.HttpEndRequestW;
5155 work_endrequest->dwFlags = dwFlags;
5156 work_endrequest->dwContext = dwContext;
5158 INTERNET_AsyncCall(&work);
5159 res = ERROR_IO_PENDING;
5161 else
5162 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5164 WININET_Release( &request->hdr );
5165 TRACE("%u <--\n", res);
5166 if(res != ERROR_SUCCESS)
5167 SetLastError(res);
5168 return res == ERROR_SUCCESS;
5171 /***********************************************************************
5172 * HttpSendRequestExA (WININET.@)
5174 * Sends the specified request to the HTTP server and allows chunked
5175 * transfers.
5177 * RETURNS
5178 * Success: TRUE
5179 * Failure: FALSE, call GetLastError() for more information.
5181 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5182 LPINTERNET_BUFFERSA lpBuffersIn,
5183 LPINTERNET_BUFFERSA lpBuffersOut,
5184 DWORD dwFlags, DWORD_PTR dwContext)
5186 INTERNET_BUFFERSW BuffersInW;
5187 BOOL rc = FALSE;
5188 DWORD headerlen;
5189 LPWSTR header = NULL;
5191 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5192 lpBuffersOut, dwFlags, dwContext);
5194 if (lpBuffersIn)
5196 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5197 if (lpBuffersIn->lpcszHeader)
5199 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5200 lpBuffersIn->dwHeadersLength,0,0);
5201 header = heap_alloc(headerlen*sizeof(WCHAR));
5202 if (!(BuffersInW.lpcszHeader = header))
5204 SetLastError(ERROR_OUTOFMEMORY);
5205 return FALSE;
5207 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5208 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5209 header, headerlen);
5211 else
5212 BuffersInW.lpcszHeader = NULL;
5213 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5214 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5215 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5216 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5217 BuffersInW.Next = NULL;
5220 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5222 heap_free(header);
5223 return rc;
5226 /***********************************************************************
5227 * HttpSendRequestExW (WININET.@)
5229 * Sends the specified request to the HTTP server and allows chunked
5230 * transfers
5232 * RETURNS
5233 * Success: TRUE
5234 * Failure: FALSE, call GetLastError() for more information.
5236 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5237 LPINTERNET_BUFFERSW lpBuffersIn,
5238 LPINTERNET_BUFFERSW lpBuffersOut,
5239 DWORD dwFlags, DWORD_PTR dwContext)
5241 http_request_t *request;
5242 http_session_t *session;
5243 appinfo_t *hIC;
5244 DWORD res;
5246 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5247 lpBuffersOut, dwFlags, dwContext);
5249 request = (http_request_t*) get_handle_object( hRequest );
5251 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5253 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5254 goto lend;
5257 session = request->session;
5258 assert(session->hdr.htype == WH_HHTTPSESSION);
5259 hIC = session->appInfo;
5260 assert(hIC->hdr.htype == WH_HINIT);
5262 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5264 WORKREQUEST workRequest;
5265 struct WORKREQ_HTTPSENDREQUESTW *req;
5267 workRequest.asyncproc = AsyncHttpSendRequestProc;
5268 workRequest.hdr = WININET_AddRef( &request->hdr );
5269 req = &workRequest.u.HttpSendRequestW;
5270 if (lpBuffersIn)
5272 DWORD size = 0;
5274 if (lpBuffersIn->lpcszHeader)
5276 if (lpBuffersIn->dwHeadersLength == ~0u)
5277 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5278 else
5279 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5281 req->lpszHeader = heap_alloc(size);
5282 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5284 else req->lpszHeader = NULL;
5286 req->dwHeaderLength = size / sizeof(WCHAR);
5287 req->lpOptional = lpBuffersIn->lpvBuffer;
5288 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5289 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5291 else
5293 req->lpszHeader = NULL;
5294 req->dwHeaderLength = 0;
5295 req->lpOptional = NULL;
5296 req->dwOptionalLength = 0;
5297 req->dwContentLength = 0;
5300 req->bEndRequest = FALSE;
5302 INTERNET_AsyncCall(&workRequest);
5304 * This is from windows.
5306 res = ERROR_IO_PENDING;
5308 else
5310 if (lpBuffersIn)
5311 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5312 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5313 lpBuffersIn->dwBufferTotal, FALSE);
5314 else
5315 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5318 lend:
5319 if ( request )
5320 WININET_Release( &request->hdr );
5322 TRACE("<---\n");
5323 SetLastError(res);
5324 return res == ERROR_SUCCESS;
5327 /***********************************************************************
5328 * HttpSendRequestW (WININET.@)
5330 * Sends the specified request to the HTTP server
5332 * RETURNS
5333 * TRUE on success
5334 * FALSE on failure
5337 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5338 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5340 http_request_t *request;
5341 http_session_t *session = NULL;
5342 appinfo_t *hIC = NULL;
5343 DWORD res = ERROR_SUCCESS;
5345 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5346 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5348 request = (http_request_t*) get_handle_object( hHttpRequest );
5349 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5351 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5352 goto lend;
5355 session = request->session;
5356 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5358 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5359 goto lend;
5362 hIC = session->appInfo;
5363 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5365 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5366 goto lend;
5369 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5371 WORKREQUEST workRequest;
5372 struct WORKREQ_HTTPSENDREQUESTW *req;
5374 workRequest.asyncproc = AsyncHttpSendRequestProc;
5375 workRequest.hdr = WININET_AddRef( &request->hdr );
5376 req = &workRequest.u.HttpSendRequestW;
5377 if (lpszHeaders)
5379 DWORD size;
5381 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5382 else size = dwHeaderLength * sizeof(WCHAR);
5384 req->lpszHeader = heap_alloc(size);
5385 memcpy(req->lpszHeader, lpszHeaders, size);
5387 else
5388 req->lpszHeader = 0;
5389 req->dwHeaderLength = dwHeaderLength;
5390 req->lpOptional = lpOptional;
5391 req->dwOptionalLength = dwOptionalLength;
5392 req->dwContentLength = dwOptionalLength;
5393 req->bEndRequest = TRUE;
5395 INTERNET_AsyncCall(&workRequest);
5397 * This is from windows.
5399 res = ERROR_IO_PENDING;
5401 else
5403 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5404 dwHeaderLength, lpOptional, dwOptionalLength,
5405 dwOptionalLength, TRUE);
5407 lend:
5408 if( request )
5409 WININET_Release( &request->hdr );
5411 SetLastError(res);
5412 return res == ERROR_SUCCESS;
5415 /***********************************************************************
5416 * HttpSendRequestA (WININET.@)
5418 * Sends the specified request to the HTTP server
5420 * RETURNS
5421 * TRUE on success
5422 * FALSE on failure
5425 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5426 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5428 BOOL result;
5429 LPWSTR szHeaders=NULL;
5430 DWORD nLen=dwHeaderLength;
5431 if(lpszHeaders!=NULL)
5433 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5434 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5435 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5437 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5438 heap_free(szHeaders);
5439 return result;
5442 /***********************************************************************
5443 * HTTPSESSION_Destroy (internal)
5445 * Deallocate session handle
5448 static void HTTPSESSION_Destroy(object_header_t *hdr)
5450 http_session_t *session = (http_session_t*) hdr;
5452 TRACE("%p\n", session);
5454 WININET_Release(&session->appInfo->hdr);
5456 heap_free(session->hostName);
5457 heap_free(session->password);
5458 heap_free(session->userName);
5461 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5463 http_session_t *ses = (http_session_t *)hdr;
5465 switch(option) {
5466 case INTERNET_OPTION_HANDLE_TYPE:
5467 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5469 if (*size < sizeof(ULONG))
5470 return ERROR_INSUFFICIENT_BUFFER;
5472 *size = sizeof(DWORD);
5473 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5474 return ERROR_SUCCESS;
5475 case INTERNET_OPTION_CONNECT_TIMEOUT:
5476 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5478 if (*size < sizeof(DWORD))
5479 return ERROR_INSUFFICIENT_BUFFER;
5481 *size = sizeof(DWORD);
5482 *(DWORD *)buffer = ses->connect_timeout;
5483 return ERROR_SUCCESS;
5485 case INTERNET_OPTION_SEND_TIMEOUT:
5486 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5488 if (*size < sizeof(DWORD))
5489 return ERROR_INSUFFICIENT_BUFFER;
5491 *size = sizeof(DWORD);
5492 *(DWORD *)buffer = ses->send_timeout;
5493 return ERROR_SUCCESS;
5495 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5496 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5498 if (*size < sizeof(DWORD))
5499 return ERROR_INSUFFICIENT_BUFFER;
5501 *size = sizeof(DWORD);
5502 *(DWORD *)buffer = ses->receive_timeout;
5503 return ERROR_SUCCESS;
5506 return INET_QueryOption(hdr, option, buffer, size, unicode);
5509 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5511 http_session_t *ses = (http_session_t*)hdr;
5513 switch(option) {
5514 case INTERNET_OPTION_USERNAME:
5516 heap_free(ses->userName);
5517 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5518 return ERROR_SUCCESS;
5520 case INTERNET_OPTION_PASSWORD:
5522 heap_free(ses->password);
5523 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5524 return ERROR_SUCCESS;
5526 case INTERNET_OPTION_CONNECT_TIMEOUT:
5528 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5529 ses->connect_timeout = *(DWORD *)buffer;
5530 return ERROR_SUCCESS;
5532 case INTERNET_OPTION_SEND_TIMEOUT:
5534 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5535 ses->send_timeout = *(DWORD *)buffer;
5536 return ERROR_SUCCESS;
5538 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5540 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5541 ses->receive_timeout = *(DWORD *)buffer;
5542 return ERROR_SUCCESS;
5544 default: break;
5547 return INET_SetOption(hdr, option, buffer, size);
5550 static const object_vtbl_t HTTPSESSIONVtbl = {
5551 HTTPSESSION_Destroy,
5552 NULL,
5553 HTTPSESSION_QueryOption,
5554 HTTPSESSION_SetOption,
5555 NULL,
5556 NULL,
5557 NULL,
5558 NULL,
5559 NULL
5563 /***********************************************************************
5564 * HTTP_Connect (internal)
5566 * Create http session handle
5568 * RETURNS
5569 * HINTERNET a session handle on success
5570 * NULL on failure
5573 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5574 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5575 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5576 DWORD dwInternalFlags, HINTERNET *ret)
5578 http_session_t *session = NULL;
5580 TRACE("-->\n");
5582 if (!lpszServerName || !lpszServerName[0])
5583 return ERROR_INVALID_PARAMETER;
5585 assert( hIC->hdr.htype == WH_HINIT );
5587 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5588 if (!session)
5589 return ERROR_OUTOFMEMORY;
5592 * According to my tests. The name is not resolved until a request is sent
5595 session->hdr.htype = WH_HHTTPSESSION;
5596 session->hdr.dwFlags = dwFlags;
5597 session->hdr.dwContext = dwContext;
5598 session->hdr.dwInternalFlags |= dwInternalFlags;
5600 WININET_AddRef( &hIC->hdr );
5601 session->appInfo = hIC;
5602 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5604 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5605 if(hIC->proxyBypass)
5606 FIXME("Proxy bypass is ignored.\n");
5608 session->hostName = heap_strdupW(lpszServerName);
5609 if (lpszUserName && lpszUserName[0])
5610 session->userName = heap_strdupW(lpszUserName);
5611 if (lpszPassword && lpszPassword[0])
5612 session->password = heap_strdupW(lpszPassword);
5613 session->hostPort = serverPort;
5614 session->connect_timeout = hIC->connect_timeout;
5615 session->send_timeout = INFINITE;
5616 session->receive_timeout = INFINITE;
5618 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5619 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5621 INTERNET_SendCallback(&hIC->hdr, dwContext,
5622 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5623 sizeof(HINTERNET));
5627 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5628 * windows
5631 TRACE("%p --> %p\n", hIC, session);
5633 *ret = session->hdr.hInternet;
5634 return ERROR_SUCCESS;
5637 /***********************************************************************
5638 * HTTP_clear_response_headers (internal)
5640 * clear out any old response headers
5642 static void HTTP_clear_response_headers( http_request_t *request )
5644 DWORD i;
5646 for( i=0; i<request->nCustHeaders; i++)
5648 if( !request->custHeaders[i].lpszField )
5649 continue;
5650 if( !request->custHeaders[i].lpszValue )
5651 continue;
5652 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5653 continue;
5654 HTTP_DeleteCustomHeader( request, i );
5655 i--;
5659 /***********************************************************************
5660 * HTTP_GetResponseHeaders (internal)
5662 * Read server response
5664 * RETURNS
5666 * TRUE on success
5667 * FALSE on error
5669 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5671 INT cbreaks = 0;
5672 WCHAR buffer[MAX_REPLY_LEN];
5673 DWORD buflen = MAX_REPLY_LEN;
5674 BOOL bSuccess = FALSE;
5675 INT rc = 0;
5676 char bufferA[MAX_REPLY_LEN];
5677 LPWSTR status_code = NULL, status_text = NULL;
5678 DWORD cchMaxRawHeaders = 1024;
5679 LPWSTR lpszRawHeaders = NULL;
5680 LPWSTR temp;
5681 DWORD cchRawHeaders = 0;
5682 BOOL codeHundred = FALSE;
5684 TRACE("-->\n");
5686 if(!request->netconn)
5687 goto lend;
5689 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5690 do {
5691 static const WCHAR szHundred[] = {'1','0','0',0};
5693 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5695 buflen = MAX_REPLY_LEN;
5696 if (!read_line(request, bufferA, &buflen))
5697 goto lend;
5699 /* clear old response headers (eg. from a redirect response) */
5700 if (clear) {
5701 HTTP_clear_response_headers( request );
5702 clear = FALSE;
5705 rc += buflen;
5706 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5707 /* check is this a status code line? */
5708 if (!strncmpW(buffer, g_szHttp1_0, 4))
5710 /* split the version from the status code */
5711 status_code = strchrW( buffer, ' ' );
5712 if( !status_code )
5713 goto lend;
5714 *status_code++=0;
5716 /* split the status code from the status text */
5717 status_text = strchrW( status_code, ' ' );
5718 if( !status_text )
5719 goto lend;
5720 *status_text++=0;
5722 request->status_code = atoiW(status_code);
5724 TRACE("version [%s] status code [%s] status text [%s]\n",
5725 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5727 codeHundred = (!strcmpW(status_code, szHundred));
5729 else if (!codeHundred)
5731 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5733 heap_free(request->version);
5734 heap_free(request->statusText);
5736 request->status_code = HTTP_STATUS_OK;
5737 request->version = heap_strdupW(g_szHttp1_0);
5738 request->statusText = heap_strdupW(szOK);
5740 heap_free(request->rawHeaders);
5741 request->rawHeaders = heap_strdupW(szDefaultHeader);
5743 bSuccess = TRUE;
5744 goto lend;
5746 } while (codeHundred);
5748 /* Add status code */
5749 HTTP_ProcessHeader(request, szStatus, status_code,
5750 HTTP_ADDHDR_FLAG_REPLACE);
5752 heap_free(request->version);
5753 heap_free(request->statusText);
5755 request->version = heap_strdupW(buffer);
5756 request->statusText = heap_strdupW(status_text);
5758 /* Restore the spaces */
5759 *(status_code-1) = ' ';
5760 *(status_text-1) = ' ';
5762 /* regenerate raw headers */
5763 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5764 if (!lpszRawHeaders) goto lend;
5766 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5767 cchMaxRawHeaders *= 2;
5768 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5769 if (temp == NULL) goto lend;
5770 lpszRawHeaders = temp;
5771 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5772 cchRawHeaders += (buflen-1);
5773 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5774 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5775 lpszRawHeaders[cchRawHeaders] = '\0';
5777 /* Parse each response line */
5780 buflen = MAX_REPLY_LEN;
5781 if (read_line(request, bufferA, &buflen))
5783 LPWSTR * pFieldAndValue;
5785 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5787 if (!bufferA[0]) break;
5788 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5790 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5791 if (pFieldAndValue)
5793 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5794 cchMaxRawHeaders *= 2;
5795 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5796 if (temp == NULL) goto lend;
5797 lpszRawHeaders = temp;
5798 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5799 cchRawHeaders += (buflen-1);
5800 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5801 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5802 lpszRawHeaders[cchRawHeaders] = '\0';
5804 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5805 HTTP_ADDREQ_FLAG_ADD );
5807 HTTP_FreeTokens(pFieldAndValue);
5810 else
5812 cbreaks++;
5813 if (cbreaks >= 2)
5814 break;
5816 }while(1);
5818 /* make sure the response header is terminated with an empty line. Some apps really
5819 truly care about that empty line being there for some reason. Just add it to the
5820 header. */
5821 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5823 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5824 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5825 if (temp == NULL) goto lend;
5826 lpszRawHeaders = temp;
5829 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5831 heap_free(request->rawHeaders);
5832 request->rawHeaders = lpszRawHeaders;
5833 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5834 bSuccess = TRUE;
5836 lend:
5838 TRACE("<--\n");
5839 if (bSuccess)
5840 return rc;
5841 else
5843 heap_free(lpszRawHeaders);
5844 return 0;
5848 /***********************************************************************
5849 * HTTP_InterpretHttpHeader (internal)
5851 * Parse server response
5853 * RETURNS
5855 * Pointer to array of field, value, NULL on success.
5856 * NULL on error.
5858 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5860 LPWSTR * pTokenPair;
5861 LPWSTR pszColon;
5862 INT len;
5864 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5866 pszColon = strchrW(buffer, ':');
5867 /* must have two tokens */
5868 if (!pszColon)
5870 HTTP_FreeTokens(pTokenPair);
5871 if (buffer[0])
5872 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5873 return NULL;
5876 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5877 if (!pTokenPair[0])
5879 HTTP_FreeTokens(pTokenPair);
5880 return NULL;
5882 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5883 pTokenPair[0][pszColon - buffer] = '\0';
5885 /* skip colon */
5886 pszColon++;
5887 len = strlenW(pszColon);
5888 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5889 if (!pTokenPair[1])
5891 HTTP_FreeTokens(pTokenPair);
5892 return NULL;
5894 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5896 strip_spaces(pTokenPair[0]);
5897 strip_spaces(pTokenPair[1]);
5899 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5900 return pTokenPair;
5903 /***********************************************************************
5904 * HTTP_ProcessHeader (internal)
5906 * Stuff header into header tables according to <dwModifier>
5910 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5912 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5914 LPHTTPHEADERW lphttpHdr = NULL;
5915 INT index = -1;
5916 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5917 DWORD res = ERROR_HTTP_INVALID_HEADER;
5919 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5921 /* REPLACE wins out over ADD */
5922 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5923 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5925 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5926 index = -1;
5927 else
5928 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5930 if (index >= 0)
5932 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5933 return ERROR_HTTP_INVALID_HEADER;
5934 lphttpHdr = &request->custHeaders[index];
5936 else if (value)
5938 HTTPHEADERW hdr;
5940 hdr.lpszField = (LPWSTR)field;
5941 hdr.lpszValue = (LPWSTR)value;
5942 hdr.wFlags = hdr.wCount = 0;
5944 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5945 hdr.wFlags |= HDR_ISREQUEST;
5947 return HTTP_InsertCustomHeader(request, &hdr);
5949 /* no value to delete */
5950 else return ERROR_SUCCESS;
5952 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5953 lphttpHdr->wFlags |= HDR_ISREQUEST;
5954 else
5955 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5957 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5959 HTTP_DeleteCustomHeader( request, index );
5961 if (value)
5963 HTTPHEADERW hdr;
5965 hdr.lpszField = (LPWSTR)field;
5966 hdr.lpszValue = (LPWSTR)value;
5967 hdr.wFlags = hdr.wCount = 0;
5969 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5970 hdr.wFlags |= HDR_ISREQUEST;
5972 return HTTP_InsertCustomHeader(request, &hdr);
5975 return ERROR_SUCCESS;
5977 else if (dwModifier & COALESCEFLAGS)
5979 LPWSTR lpsztmp;
5980 WCHAR ch = 0;
5981 INT len = 0;
5982 INT origlen = strlenW(lphttpHdr->lpszValue);
5983 INT valuelen = strlenW(value);
5985 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5987 ch = ',';
5988 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5990 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5992 ch = ';';
5993 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5996 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5998 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5999 if (lpsztmp)
6001 lphttpHdr->lpszValue = lpsztmp;
6002 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6003 if (ch > 0)
6005 lphttpHdr->lpszValue[origlen] = ch;
6006 origlen++;
6007 lphttpHdr->lpszValue[origlen] = ' ';
6008 origlen++;
6011 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6012 lphttpHdr->lpszValue[len] = '\0';
6013 res = ERROR_SUCCESS;
6015 else
6017 WARN("heap_realloc (%d bytes) failed\n",len+1);
6018 res = ERROR_OUTOFMEMORY;
6021 TRACE("<-- %d\n", res);
6022 return res;
6025 /***********************************************************************
6026 * HTTP_GetCustomHeaderIndex (internal)
6028 * Return index of custom header from header array
6031 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6032 int requested_index, BOOL request_only)
6034 DWORD index;
6036 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6038 for (index = 0; index < request->nCustHeaders; index++)
6040 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6041 continue;
6043 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6044 continue;
6046 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6047 continue;
6049 if (requested_index == 0)
6050 break;
6051 requested_index --;
6054 if (index >= request->nCustHeaders)
6055 index = -1;
6057 TRACE("Return: %d\n", index);
6058 return index;
6062 /***********************************************************************
6063 * HTTP_InsertCustomHeader (internal)
6065 * Insert header into array
6068 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6070 INT count;
6071 LPHTTPHEADERW lph = NULL;
6073 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6074 count = request->nCustHeaders + 1;
6075 if (count > 1)
6076 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6077 else
6078 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6080 if (!lph)
6081 return ERROR_OUTOFMEMORY;
6083 request->custHeaders = lph;
6084 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6085 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6086 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6087 request->custHeaders[count-1].wCount= lpHdr->wCount;
6088 request->nCustHeaders++;
6090 return ERROR_SUCCESS;
6094 /***********************************************************************
6095 * HTTP_DeleteCustomHeader (internal)
6097 * Delete header from array
6098 * If this function is called, the indexs may change.
6100 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6102 if( request->nCustHeaders <= 0 )
6103 return FALSE;
6104 if( index >= request->nCustHeaders )
6105 return FALSE;
6106 request->nCustHeaders--;
6108 heap_free(request->custHeaders[index].lpszField);
6109 heap_free(request->custHeaders[index].lpszValue);
6111 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6112 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6113 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6115 return TRUE;
6119 /***********************************************************************
6120 * HTTP_VerifyValidHeader (internal)
6122 * Verify the given header is not invalid for the given http request
6125 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6127 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6128 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6129 return ERROR_HTTP_INVALID_HEADER;
6131 return ERROR_SUCCESS;
6134 /***********************************************************************
6135 * IsHostInProxyBypassList (@)
6137 * Undocumented
6140 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6142 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6143 return FALSE;
6146 /***********************************************************************
6147 * InternetShowSecurityInfoByURLA (@)
6149 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6151 FIXME("stub: %s %p\n", url, window);
6152 return FALSE;
6155 /***********************************************************************
6156 * InternetShowSecurityInfoByURLW (@)
6158 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6160 FIXME("stub: %s %p\n", debugstr_w(url), window);
6161 return FALSE;
6164 /***********************************************************************
6165 * ShowX509EncodedCertificate (@)
6167 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6169 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6170 cert, len);
6171 DWORD ret;
6173 if (certContext)
6175 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6177 memset(&view, 0, sizeof(view));
6178 view.hwndParent = parent;
6179 view.pCertContext = certContext;
6180 if (CryptUIDlgViewCertificateW(&view, NULL))
6181 ret = ERROR_SUCCESS;
6182 else
6183 ret = GetLastError();
6184 CertFreeCertificateContext(certContext);
6186 else
6187 ret = GetLastError();
6188 return ret;