wininet: Don't return a relative path when building the proxy url request.
[wine/multimedia.git] / dlls / wininet / http.c
blob772aba6126c79868768049a5f1ae15b20283816e
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"
69 #include "internet.h"
70 #include "wine/debug.h"
71 #include "wine/exception.h"
72 #include "wine/unicode.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
76 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR szOK[] = {'O','K',0};
79 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
80 static const WCHAR hostW[] = { 'H','o','s','t',0 };
81 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
84 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
85 static const WCHAR szGET[] = { 'G','E','T', 0 };
86 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
87 static const WCHAR szCrLf[] = {'\r','\n', 0};
89 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
90 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
91 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
92 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
94 static const WCHAR szAge[] = { 'A','g','e',0 };
95 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
96 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
97 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
98 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
99 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
101 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
102 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
103 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
104 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
105 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
106 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 };
107 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
108 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
109 static const WCHAR szDate[] = { 'D','a','t','e',0 };
110 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
111 static const WCHAR szETag[] = { 'E','T','a','g',0 };
112 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
113 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
114 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
116 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
118 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
120 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
121 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
122 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
123 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
124 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
125 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
126 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
127 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
128 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
129 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
130 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
131 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
132 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
133 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 };
134 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
135 static const WCHAR szURI[] = { 'U','R','I',0 };
136 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
137 static const WCHAR szVary[] = { 'V','a','r','y',0 };
138 static const WCHAR szVia[] = { 'V','i','a',0 };
139 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
140 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define COLLECT_TIME 60000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
158 struct HttpAuthInfo
160 LPWSTR scheme;
161 CredHandle cred;
162 CtxtHandle ctx;
163 TimeStamp exp;
164 ULONG attr;
165 ULONG max_token;
166 void *auth_data;
167 unsigned int auth_data_len;
168 BOOL finished; /* finished authenticating */
172 typedef struct _basicAuthorizationData
174 struct list entry;
176 LPWSTR host;
177 LPWSTR realm;
178 LPSTR authorization;
179 UINT authorizationLen;
180 } basicAuthorizationData;
182 typedef struct _authorizationData
184 struct list entry;
186 LPWSTR host;
187 LPWSTR scheme;
188 LPWSTR domain;
189 UINT domain_len;
190 LPWSTR user;
191 UINT user_len;
192 LPWSTR password;
193 UINT password_len;
194 } authorizationData;
196 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
197 static struct list authorizationCache = LIST_INIT(authorizationCache);
199 static CRITICAL_SECTION authcache_cs;
200 static CRITICAL_SECTION_DEBUG critsect_debug =
202 0, 0, &authcache_cs,
203 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
204 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
206 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
208 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
209 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
210 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
211 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
212 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
213 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
214 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
215 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
216 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
217 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
218 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
220 static CRITICAL_SECTION connection_pool_cs;
221 static CRITICAL_SECTION_DEBUG connection_pool_debug =
223 0, 0, &connection_pool_cs,
224 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
225 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
227 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
229 static struct list connection_pool = LIST_INIT(connection_pool);
230 static BOOL collector_running;
232 void server_addref(server_t *server)
234 InterlockedIncrement(&server->ref);
237 void server_release(server_t *server)
239 if(InterlockedDecrement(&server->ref))
240 return;
242 if(!server->ref)
243 server->keep_until = GetTickCount64() + COLLECT_TIME;
246 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
248 server_t *iter, *server = NULL;
250 EnterCriticalSection(&connection_pool_cs);
252 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
253 if(iter->port == port && !strcmpW(iter->name, name)) {
254 server = iter;
255 server_addref(server);
256 break;
260 if(!server) {
261 server = heap_alloc(sizeof(*server));
262 if(server) {
263 server->addr_len = 0;
264 server->ref = 1;
265 server->port = port;
266 list_init(&server->conn_pool);
267 server->name = heap_strdupW(name);
268 if(server->name) {
269 list_add_head(&connection_pool, &server->entry);
270 }else {
271 heap_free(server);
272 server = NULL;
277 LeaveCriticalSection(&connection_pool_cs);
279 return server;
282 BOOL collect_connections(BOOL collect_all)
284 netconn_t *netconn, *netconn_safe;
285 server_t *server, *server_safe;
286 BOOL remaining = FALSE;
287 DWORD64 now;
289 now = GetTickCount64();
291 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
292 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
293 if(collect_all || netconn->keep_until < now) {
294 TRACE("freeing %p\n", netconn);
295 list_remove(&netconn->pool_entry);
296 free_netconn(netconn);
297 }else {
298 remaining = TRUE;
302 if(!server->ref) {
303 if(collect_all || server->keep_until < now) {
304 list_remove(&server->entry);
306 heap_free(server->name);
307 heap_free(server);
308 }else {
309 remaining = TRUE;
314 return remaining;
317 static DWORD WINAPI collect_connections_proc(void *arg)
319 BOOL remaining_conns;
321 do {
322 /* FIXME: Use more sophisticated method */
323 Sleep(5000);
325 EnterCriticalSection(&connection_pool_cs);
327 remaining_conns = collect_connections(FALSE);
328 if(!remaining_conns)
329 collector_running = FALSE;
331 LeaveCriticalSection(&connection_pool_cs);
332 }while(remaining_conns);
334 FreeLibraryAndExitThread(WININET_hModule, 0);
337 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
339 int HeaderIndex = 0;
340 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
341 if (HeaderIndex == -1)
342 return NULL;
343 else
344 return &req->custHeaders[HeaderIndex];
347 typedef enum {
348 READMODE_SYNC,
349 READMODE_ASYNC,
350 READMODE_NOBLOCK
351 } read_mode_t;
353 struct data_stream_vtbl_t {
354 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
355 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
356 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
357 BOOL (*drain_content)(data_stream_t*,http_request_t*);
358 void (*destroy)(data_stream_t*);
361 typedef struct {
362 data_stream_t data_stream;
364 BYTE buf[READ_BUFFER_SIZE];
365 DWORD buf_size;
366 DWORD buf_pos;
367 DWORD chunk_size;
368 } chunked_stream_t;
370 static inline void destroy_data_stream(data_stream_t *stream)
372 stream->vtbl->destroy(stream);
375 static void reset_data_stream(http_request_t *req)
377 destroy_data_stream(req->data_stream);
378 req->data_stream = &req->netconn_stream.data_stream;
379 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
380 req->read_chunked = req->read_gzip = FALSE;
383 #ifdef HAVE_ZLIB
385 typedef struct {
386 data_stream_t stream;
387 data_stream_t *parent_stream;
388 z_stream zstream;
389 BYTE buf[READ_BUFFER_SIZE];
390 DWORD buf_size;
391 DWORD buf_pos;
392 BOOL end_of_data;
393 } gzip_stream_t;
395 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
397 /* Allow reading only from read buffer */
398 return 0;
401 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
403 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
404 return gzip_stream->end_of_data;
407 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
408 DWORD *read, read_mode_t read_mode)
410 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
411 z_stream *zstream = &gzip_stream->zstream;
412 DWORD current_read, ret_read = 0;
413 BOOL end;
414 int zres;
415 DWORD res = ERROR_SUCCESS;
417 while(size && !gzip_stream->end_of_data) {
418 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
420 if(gzip_stream->buf_size <= 64 && !end) {
421 if(gzip_stream->buf_pos) {
422 if(gzip_stream->buf_size)
423 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
424 gzip_stream->buf_pos = 0;
426 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
427 sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
428 gzip_stream->buf_size += current_read;
429 if(res != ERROR_SUCCESS)
430 break;
431 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
432 if(!current_read && !end) {
433 if(read_mode != READMODE_NOBLOCK) {
434 WARN("unexpected end of data\n");
435 gzip_stream->end_of_data = TRUE;
437 break;
439 if(gzip_stream->buf_size <= 64 && !end)
440 continue;
443 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
444 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
445 zstream->next_out = buf+ret_read;
446 zstream->avail_out = size;
447 zres = inflate(&gzip_stream->zstream, 0);
448 current_read = size - zstream->avail_out;
449 size -= current_read;
450 ret_read += current_read;
451 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
452 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
453 if(zres == Z_STREAM_END) {
454 TRACE("end of data\n");
455 gzip_stream->end_of_data = TRUE;
456 inflateEnd(zstream);
457 }else if(zres != Z_OK) {
458 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
459 if(!ret_read)
460 res = ERROR_INTERNET_DECODING_FAILED;
461 break;
464 if(ret_read && read_mode == READMODE_ASYNC)
465 read_mode = READMODE_NOBLOCK;
468 TRACE("read %u bytes\n", ret_read);
469 *read = ret_read;
470 return res;
473 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
475 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
476 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
479 static void gzip_destroy(data_stream_t *stream)
481 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
483 destroy_data_stream(gzip_stream->parent_stream);
485 if(!gzip_stream->end_of_data)
486 inflateEnd(&gzip_stream->zstream);
487 heap_free(gzip_stream);
490 static const data_stream_vtbl_t gzip_stream_vtbl = {
491 gzip_get_avail_data,
492 gzip_end_of_data,
493 gzip_read,
494 gzip_drain_content,
495 gzip_destroy
498 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
500 return heap_alloc(items*size);
503 static void wininet_zfree(voidpf opaque, voidpf address)
505 heap_free(address);
508 static DWORD init_gzip_stream(http_request_t *req)
510 gzip_stream_t *gzip_stream;
511 int index, zres;
513 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
514 if(!gzip_stream)
515 return ERROR_OUTOFMEMORY;
517 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
518 gzip_stream->zstream.zalloc = wininet_zalloc;
519 gzip_stream->zstream.zfree = wininet_zfree;
521 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
522 if(zres != Z_OK) {
523 ERR("inflateInit failed: %d\n", zres);
524 heap_free(gzip_stream);
525 return ERROR_OUTOFMEMORY;
528 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
529 if(index != -1)
530 HTTP_DeleteCustomHeader(req, index);
532 if(req->read_size) {
533 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
534 gzip_stream->buf_size = req->read_size;
535 req->read_pos = req->read_size = 0;
538 req->read_gzip = TRUE;
539 gzip_stream->parent_stream = req->data_stream;
540 req->data_stream = &gzip_stream->stream;
541 return ERROR_SUCCESS;
544 #else
546 static DWORD init_gzip_stream(http_request_t *req)
548 ERR("gzip stream not supported, missing zlib.\n");
549 return ERROR_SUCCESS;
552 #endif
554 /***********************************************************************
555 * HTTP_Tokenize (internal)
557 * Tokenize a string, allocating memory for the tokens.
559 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
561 LPWSTR * token_array;
562 int tokens = 0;
563 int i;
564 LPCWSTR next_token;
566 if (string)
568 /* empty string has no tokens */
569 if (*string)
570 tokens++;
571 /* count tokens */
572 for (i = 0; string[i]; i++)
574 if (!strncmpW(string+i, token_string, strlenW(token_string)))
576 DWORD j;
577 tokens++;
578 /* we want to skip over separators, but not the null terminator */
579 for (j = 0; j < strlenW(token_string) - 1; j++)
580 if (!string[i+j])
581 break;
582 i += j;
587 /* add 1 for terminating NULL */
588 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
589 token_array[tokens] = NULL;
590 if (!tokens)
591 return token_array;
592 for (i = 0; i < tokens; i++)
594 int len;
595 next_token = strstrW(string, token_string);
596 if (!next_token) next_token = string+strlenW(string);
597 len = next_token - string;
598 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
599 memcpy(token_array[i], string, len*sizeof(WCHAR));
600 token_array[i][len] = '\0';
601 string = next_token+strlenW(token_string);
603 return token_array;
606 /***********************************************************************
607 * HTTP_FreeTokens (internal)
609 * Frees memory returned from HTTP_Tokenize.
611 static void HTTP_FreeTokens(LPWSTR * token_array)
613 int i;
614 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
615 heap_free(token_array);
618 static void HTTP_FixURL(http_request_t *request)
620 static const WCHAR szSlash[] = { '/',0 };
621 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
623 /* If we don't have a path we set it to root */
624 if (NULL == request->path)
625 request->path = heap_strdupW(szSlash);
626 else /* remove \r and \n*/
628 int nLen = strlenW(request->path);
629 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
631 nLen--;
632 request->path[nLen]='\0';
634 /* Replace '\' with '/' */
635 while (nLen>0) {
636 nLen--;
637 if (request->path[nLen] == '\\') request->path[nLen]='/';
641 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
642 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
643 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
645 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
646 *fixurl = '/';
647 strcpyW(fixurl + 1, request->path);
648 heap_free( request->path );
649 request->path = fixurl;
653 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
655 LPWSTR requestString;
656 DWORD len, n;
657 LPCWSTR *req;
658 UINT i;
659 LPWSTR p;
661 static const WCHAR szSpace[] = { ' ',0 };
662 static const WCHAR szColon[] = { ':',' ',0 };
663 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
665 /* allocate space for an array of all the string pointers to be added */
666 len = (request->nCustHeaders)*4 + 10;
667 req = heap_alloc(len*sizeof(LPCWSTR));
669 /* add the verb, path and HTTP version string */
670 n = 0;
671 req[n++] = verb;
672 req[n++] = szSpace;
673 req[n++] = path;
674 req[n++] = szSpace;
675 req[n++] = version;
677 /* Append custom request headers */
678 for (i = 0; i < request->nCustHeaders; i++)
680 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
682 req[n++] = szCrLf;
683 req[n++] = request->custHeaders[i].lpszField;
684 req[n++] = szColon;
685 req[n++] = request->custHeaders[i].lpszValue;
687 TRACE("Adding custom header %s (%s)\n",
688 debugstr_w(request->custHeaders[i].lpszField),
689 debugstr_w(request->custHeaders[i].lpszValue));
693 if( n >= len )
694 ERR("oops. buffer overrun\n");
696 req[n] = NULL;
697 requestString = HTTP_build_req( req, 4 );
698 heap_free( req );
701 * Set (header) termination string for request
702 * Make sure there's exactly two new lines at the end of the request
704 p = &requestString[strlenW(requestString)-1];
705 while ( (*p == '\n') || (*p == '\r') )
706 p--;
707 strcpyW( p+1, sztwocrlf );
709 return requestString;
712 static void HTTP_ProcessCookies( http_request_t *request )
714 int HeaderIndex;
715 int numCookies = 0;
716 LPHTTPHEADERW setCookieHeader;
718 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
719 return;
721 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
723 HTTPHEADERW *host;
724 const WCHAR *data;
725 WCHAR *name;
727 setCookieHeader = &request->custHeaders[HeaderIndex];
729 if (!setCookieHeader->lpszValue)
730 continue;
732 host = HTTP_GetHeader(request, hostW);
733 if(!host)
734 continue;
736 data = strchrW(setCookieHeader->lpszValue, '=');
737 if(!data)
738 continue;
740 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
741 if(!name)
742 continue;
744 data++;
745 set_cookie(host->lpszValue, request->path, name, data);
746 heap_free(name);
750 static void strip_spaces(LPWSTR start)
752 LPWSTR str = start;
753 LPWSTR end;
755 while (*str == ' ' && *str != '\0')
756 str++;
758 if (str != start)
759 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
761 end = start + strlenW(start) - 1;
762 while (end >= start && *end == ' ')
764 *end = '\0';
765 end--;
769 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
771 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
772 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
773 BOOL is_basic;
774 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
775 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
776 if (is_basic && pszRealm)
778 LPCWSTR token;
779 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
780 LPCWSTR realm;
781 ptr++;
782 *pszRealm=NULL;
783 token = strchrW(ptr,'=');
784 if (!token)
785 return TRUE;
786 realm = ptr;
787 while (*realm == ' ' && *realm != '\0')
788 realm++;
789 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
790 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
792 token++;
793 while (*token == ' ' && *token != '\0')
794 token++;
795 if (*token == '\0')
796 return TRUE;
797 *pszRealm = heap_strdupW(token);
798 strip_spaces(*pszRealm);
802 return is_basic;
805 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
807 if (!authinfo) return;
809 if (SecIsValidHandle(&authinfo->ctx))
810 DeleteSecurityContext(&authinfo->ctx);
811 if (SecIsValidHandle(&authinfo->cred))
812 FreeCredentialsHandle(&authinfo->cred);
814 heap_free(authinfo->auth_data);
815 heap_free(authinfo->scheme);
816 heap_free(authinfo);
819 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
821 basicAuthorizationData *ad;
822 UINT rc = 0;
824 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
826 EnterCriticalSection(&authcache_cs);
827 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
829 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
831 TRACE("Authorization found in cache\n");
832 *auth_data = heap_alloc(ad->authorizationLen);
833 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
834 rc = ad->authorizationLen;
835 break;
838 LeaveCriticalSection(&authcache_cs);
839 return rc;
842 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
844 struct list *cursor;
845 basicAuthorizationData* ad = NULL;
847 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
849 EnterCriticalSection(&authcache_cs);
850 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
852 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
853 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
855 ad = check;
856 break;
860 if (ad)
862 TRACE("Found match in cache, replacing\n");
863 heap_free(ad->authorization);
864 ad->authorization = heap_alloc(auth_data_len);
865 memcpy(ad->authorization, auth_data, auth_data_len);
866 ad->authorizationLen = auth_data_len;
868 else
870 ad = heap_alloc(sizeof(basicAuthorizationData));
871 ad->host = heap_strdupW(host);
872 ad->realm = heap_strdupW(realm);
873 ad->authorization = heap_alloc(auth_data_len);
874 memcpy(ad->authorization, auth_data, auth_data_len);
875 ad->authorizationLen = auth_data_len;
876 list_add_head(&basicAuthorizationCache,&ad->entry);
877 TRACE("authorization cached\n");
879 LeaveCriticalSection(&authcache_cs);
882 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
883 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
885 authorizationData *ad;
887 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
889 EnterCriticalSection(&authcache_cs);
890 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
891 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
892 TRACE("Authorization found in cache\n");
894 nt_auth_identity->User = heap_strdupW(ad->user);
895 nt_auth_identity->Password = heap_strdupW(ad->password);
896 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
897 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
898 (!nt_auth_identity->Domain && ad->domain_len)) {
899 heap_free(nt_auth_identity->User);
900 heap_free(nt_auth_identity->Password);
901 heap_free(nt_auth_identity->Domain);
902 break;
905 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
906 nt_auth_identity->UserLength = ad->user_len;
907 nt_auth_identity->PasswordLength = ad->password_len;
908 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
909 nt_auth_identity->DomainLength = ad->domain_len;
910 LeaveCriticalSection(&authcache_cs);
911 return TRUE;
914 LeaveCriticalSection(&authcache_cs);
916 return FALSE;
919 static void cache_authorization(LPWSTR host, LPWSTR scheme,
920 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
922 authorizationData *ad;
923 BOOL found = FALSE;
925 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
927 EnterCriticalSection(&authcache_cs);
928 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
929 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
930 found = TRUE;
931 break;
934 if(found) {
935 heap_free(ad->user);
936 heap_free(ad->password);
937 heap_free(ad->domain);
938 } else {
939 ad = heap_alloc(sizeof(authorizationData));
940 if(!ad) {
941 LeaveCriticalSection(&authcache_cs);
942 return;
945 ad->host = heap_strdupW(host);
946 ad->scheme = heap_strdupW(scheme);
947 list_add_head(&authorizationCache, &ad->entry);
950 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
951 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
952 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
953 ad->user_len = nt_auth_identity->UserLength;
954 ad->password_len = nt_auth_identity->PasswordLength;
955 ad->domain_len = nt_auth_identity->DomainLength;
957 if(!ad->host || !ad->scheme || !ad->user || !ad->password
958 || (nt_auth_identity->Domain && !ad->domain)) {
959 heap_free(ad->host);
960 heap_free(ad->scheme);
961 heap_free(ad->user);
962 heap_free(ad->password);
963 heap_free(ad->domain);
964 list_remove(&ad->entry);
965 heap_free(ad);
968 LeaveCriticalSection(&authcache_cs);
971 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
972 struct HttpAuthInfo **ppAuthInfo,
973 LPWSTR domain_and_username, LPWSTR password,
974 LPWSTR host )
976 SECURITY_STATUS sec_status;
977 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
978 BOOL first = FALSE;
979 LPWSTR szRealm = NULL;
981 TRACE("%s\n", debugstr_w(pszAuthValue));
983 if (!pAuthInfo)
985 TimeStamp exp;
987 first = TRUE;
988 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
989 if (!pAuthInfo)
990 return FALSE;
992 SecInvalidateHandle(&pAuthInfo->cred);
993 SecInvalidateHandle(&pAuthInfo->ctx);
994 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
995 pAuthInfo->attr = 0;
996 pAuthInfo->auth_data = NULL;
997 pAuthInfo->auth_data_len = 0;
998 pAuthInfo->finished = FALSE;
1000 if (is_basic_auth_value(pszAuthValue,NULL))
1002 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1003 pAuthInfo->scheme = heap_strdupW(szBasic);
1004 if (!pAuthInfo->scheme)
1006 heap_free(pAuthInfo);
1007 return FALSE;
1010 else
1012 PVOID pAuthData;
1013 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1015 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1016 if (!pAuthInfo->scheme)
1018 heap_free(pAuthInfo);
1019 return FALSE;
1022 if (domain_and_username)
1024 WCHAR *user = strchrW(domain_and_username, '\\');
1025 WCHAR *domain = domain_and_username;
1027 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1029 pAuthData = &nt_auth_identity;
1031 if (user) user++;
1032 else
1034 user = domain_and_username;
1035 domain = NULL;
1038 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1039 nt_auth_identity.User = user;
1040 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1041 nt_auth_identity.Domain = domain;
1042 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1043 nt_auth_identity.Password = password;
1044 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1046 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1048 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1049 pAuthData = &nt_auth_identity;
1050 else
1051 /* use default credentials */
1052 pAuthData = NULL;
1054 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1055 SECPKG_CRED_OUTBOUND, NULL,
1056 pAuthData, NULL,
1057 NULL, &pAuthInfo->cred,
1058 &exp);
1060 if(pAuthData && !domain_and_username) {
1061 heap_free(nt_auth_identity.User);
1062 heap_free(nt_auth_identity.Domain);
1063 heap_free(nt_auth_identity.Password);
1066 if (sec_status == SEC_E_OK)
1068 PSecPkgInfoW sec_pkg_info;
1069 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1070 if (sec_status == SEC_E_OK)
1072 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1073 FreeContextBuffer(sec_pkg_info);
1076 if (sec_status != SEC_E_OK)
1078 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1079 debugstr_w(pAuthInfo->scheme), sec_status);
1080 heap_free(pAuthInfo->scheme);
1081 heap_free(pAuthInfo);
1082 return FALSE;
1085 *ppAuthInfo = pAuthInfo;
1087 else if (pAuthInfo->finished)
1088 return FALSE;
1090 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1091 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1093 ERR("authentication scheme changed from %s to %s\n",
1094 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1095 return FALSE;
1098 if (is_basic_auth_value(pszAuthValue,&szRealm))
1100 int userlen;
1101 int passlen;
1102 char *auth_data = NULL;
1103 UINT auth_data_len = 0;
1105 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1107 if (!domain_and_username)
1109 if (host && szRealm)
1110 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1111 if (auth_data_len == 0)
1113 heap_free(szRealm);
1114 return FALSE;
1117 else
1119 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1120 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1122 /* length includes a nul terminator, which will be re-used for the ':' */
1123 auth_data = heap_alloc(userlen + 1 + passlen);
1124 if (!auth_data)
1126 heap_free(szRealm);
1127 return FALSE;
1130 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1131 auth_data[userlen] = ':';
1132 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1133 auth_data_len = userlen + 1 + passlen;
1134 if (host && szRealm)
1135 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1138 pAuthInfo->auth_data = auth_data;
1139 pAuthInfo->auth_data_len = auth_data_len;
1140 pAuthInfo->finished = TRUE;
1141 heap_free(szRealm);
1142 return TRUE;
1144 else
1146 LPCWSTR pszAuthData;
1147 SecBufferDesc out_desc, in_desc;
1148 SecBuffer out, in;
1149 unsigned char *buffer;
1150 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1151 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1153 in.BufferType = SECBUFFER_TOKEN;
1154 in.cbBuffer = 0;
1155 in.pvBuffer = NULL;
1157 in_desc.ulVersion = 0;
1158 in_desc.cBuffers = 1;
1159 in_desc.pBuffers = &in;
1161 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1162 if (*pszAuthData == ' ')
1164 pszAuthData++;
1165 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1166 in.pvBuffer = heap_alloc(in.cbBuffer);
1167 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1170 buffer = heap_alloc(pAuthInfo->max_token);
1172 out.BufferType = SECBUFFER_TOKEN;
1173 out.cbBuffer = pAuthInfo->max_token;
1174 out.pvBuffer = buffer;
1176 out_desc.ulVersion = 0;
1177 out_desc.cBuffers = 1;
1178 out_desc.pBuffers = &out;
1180 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1181 first ? NULL : &pAuthInfo->ctx,
1182 first ? request->session->serverName : NULL,
1183 context_req, 0, SECURITY_NETWORK_DREP,
1184 in.pvBuffer ? &in_desc : NULL,
1185 0, &pAuthInfo->ctx, &out_desc,
1186 &pAuthInfo->attr, &pAuthInfo->exp);
1187 if (sec_status == SEC_E_OK)
1189 pAuthInfo->finished = TRUE;
1190 pAuthInfo->auth_data = out.pvBuffer;
1191 pAuthInfo->auth_data_len = out.cbBuffer;
1192 TRACE("sending last auth packet\n");
1194 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1196 pAuthInfo->auth_data = out.pvBuffer;
1197 pAuthInfo->auth_data_len = out.cbBuffer;
1198 TRACE("sending next auth packet\n");
1200 else
1202 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1203 heap_free(out.pvBuffer);
1204 destroy_authinfo(pAuthInfo);
1205 *ppAuthInfo = NULL;
1206 return FALSE;
1210 return TRUE;
1213 /***********************************************************************
1214 * HTTP_HttpAddRequestHeadersW (internal)
1216 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1217 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1219 LPWSTR lpszStart;
1220 LPWSTR lpszEnd;
1221 LPWSTR buffer;
1222 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1224 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1226 if( dwHeaderLength == ~0U )
1227 len = strlenW(lpszHeader);
1228 else
1229 len = dwHeaderLength;
1230 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1231 lstrcpynW( buffer, lpszHeader, len + 1);
1233 lpszStart = buffer;
1237 LPWSTR * pFieldAndValue;
1239 lpszEnd = lpszStart;
1241 while (*lpszEnd != '\0')
1243 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1244 break;
1245 lpszEnd++;
1248 if (*lpszStart == '\0')
1249 break;
1251 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1253 *lpszEnd = '\0';
1254 lpszEnd++; /* Jump over newline */
1256 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1257 if (*lpszStart == '\0')
1259 /* Skip 0-length headers */
1260 lpszStart = lpszEnd;
1261 res = ERROR_SUCCESS;
1262 continue;
1264 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1265 if (pFieldAndValue)
1267 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1268 if (res == ERROR_SUCCESS)
1269 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1270 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1271 HTTP_FreeTokens(pFieldAndValue);
1274 lpszStart = lpszEnd;
1275 } while (res == ERROR_SUCCESS);
1277 heap_free(buffer);
1278 return res;
1281 /***********************************************************************
1282 * HttpAddRequestHeadersW (WININET.@)
1284 * Adds one or more HTTP header to the request handler
1286 * NOTE
1287 * On Windows if dwHeaderLength includes the trailing '\0', then
1288 * HttpAddRequestHeadersW() adds it too. However this results in an
1289 * invalid Http header which is rejected by some servers so we probably
1290 * don't need to match Windows on that point.
1292 * RETURNS
1293 * TRUE on success
1294 * FALSE on failure
1297 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1298 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1300 http_request_t *request;
1301 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1303 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1305 if (!lpszHeader)
1306 return TRUE;
1308 request = (http_request_t*) get_handle_object( hHttpRequest );
1309 if (request && request->hdr.htype == WH_HHTTPREQ)
1310 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1311 if( request )
1312 WININET_Release( &request->hdr );
1314 if(res != ERROR_SUCCESS)
1315 SetLastError(res);
1316 return res == ERROR_SUCCESS;
1319 /***********************************************************************
1320 * HttpAddRequestHeadersA (WININET.@)
1322 * Adds one or more HTTP header to the request handler
1324 * RETURNS
1325 * TRUE on success
1326 * FALSE on failure
1329 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1330 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1332 DWORD len;
1333 LPWSTR hdr;
1334 BOOL r;
1336 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1338 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1339 hdr = heap_alloc(len*sizeof(WCHAR));
1340 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1341 if( dwHeaderLength != ~0U )
1342 dwHeaderLength = len;
1344 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1346 heap_free( hdr );
1347 return r;
1350 /***********************************************************************
1351 * HttpOpenRequestA (WININET.@)
1353 * Open a HTTP request handle
1355 * RETURNS
1356 * HINTERNET a HTTP request handle on success
1357 * NULL on failure
1360 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1361 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1362 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1363 DWORD dwFlags, DWORD_PTR dwContext)
1365 LPWSTR szVerb = NULL, szObjectName = NULL;
1366 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1367 INT acceptTypesCount;
1368 HINTERNET rc = FALSE;
1369 LPCSTR *types;
1371 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1372 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1373 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1374 dwFlags, dwContext);
1376 if (lpszVerb)
1378 szVerb = heap_strdupAtoW(lpszVerb);
1379 if ( !szVerb )
1380 goto end;
1383 if (lpszObjectName)
1385 szObjectName = heap_strdupAtoW(lpszObjectName);
1386 if ( !szObjectName )
1387 goto end;
1390 if (lpszVersion)
1392 szVersion = heap_strdupAtoW(lpszVersion);
1393 if ( !szVersion )
1394 goto end;
1397 if (lpszReferrer)
1399 szReferrer = heap_strdupAtoW(lpszReferrer);
1400 if ( !szReferrer )
1401 goto end;
1404 if (lpszAcceptTypes)
1406 acceptTypesCount = 0;
1407 types = lpszAcceptTypes;
1408 while (*types)
1410 __TRY
1412 /* find out how many there are */
1413 if (*types && **types)
1415 TRACE("accept type: %s\n", debugstr_a(*types));
1416 acceptTypesCount++;
1419 __EXCEPT_PAGE_FAULT
1421 WARN("invalid accept type pointer\n");
1423 __ENDTRY;
1424 types++;
1426 szAcceptTypes = heap_alloc(sizeof(WCHAR *) * (acceptTypesCount+1));
1427 if (!szAcceptTypes) goto end;
1429 acceptTypesCount = 0;
1430 types = lpszAcceptTypes;
1431 while (*types)
1433 __TRY
1435 if (*types && **types)
1436 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1438 __EXCEPT_PAGE_FAULT
1440 /* ignore invalid pointer */
1442 __ENDTRY;
1443 types++;
1445 szAcceptTypes[acceptTypesCount] = NULL;
1448 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1449 szVersion, szReferrer,
1450 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1452 end:
1453 if (szAcceptTypes)
1455 acceptTypesCount = 0;
1456 while (szAcceptTypes[acceptTypesCount])
1458 heap_free(szAcceptTypes[acceptTypesCount]);
1459 acceptTypesCount++;
1461 heap_free(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 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1700 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1701 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1703 memset( &UrlComponents, 0, sizeof UrlComponents );
1704 UrlComponents.dwStructSize = sizeof UrlComponents;
1705 UrlComponents.lpszHostName = buf;
1706 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1708 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1709 return FALSE;
1710 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1711 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1712 sprintfW(proxy, szFormat, protoProxy);
1713 else
1714 strcpyW(proxy, protoProxy);
1715 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1716 return FALSE;
1717 if( UrlComponents.dwHostNameLength == 0 )
1718 return FALSE;
1720 if( !request->path )
1721 request->path = szNul;
1723 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1724 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1726 heap_free(session->serverName);
1727 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1728 session->serverPort = UrlComponents.nPort;
1730 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1731 return TRUE;
1734 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
1736 socklen_t addr_len;
1737 const void *addr;
1739 if(server->addr_len)
1740 return ERROR_SUCCESS;
1742 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1743 INTERNET_STATUS_RESOLVING_NAME,
1744 server->name,
1745 (strlenW(server->name)+1) * sizeof(WCHAR));
1747 addr_len = sizeof(server->addr);
1748 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1749 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1751 switch(server->addr.ss_family) {
1752 case AF_INET:
1753 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1754 break;
1755 case AF_INET6:
1756 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1757 break;
1758 default:
1759 WARN("unsupported family %d\n", server->addr.ss_family);
1760 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1763 server->addr_len = addr_len;
1764 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1765 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1766 INTERNET_STATUS_NAME_RESOLVED,
1767 server->addr_str, strlen(server->addr_str)+1);
1769 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1770 return ERROR_SUCCESS;
1773 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1775 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1776 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1777 static const WCHAR slash[] = { '/',0 };
1778 LPHTTPHEADERW host_header;
1779 LPCWSTR scheme;
1781 host_header = HTTP_GetHeader(req, hostW);
1782 if(!host_header)
1783 return FALSE;
1785 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1786 scheme = https;
1787 else
1788 scheme = http;
1789 strcpyW(buf, scheme);
1790 strcatW(buf, host_header->lpszValue);
1791 if (req->path[0] != '/')
1792 strcatW(buf, slash);
1793 strcatW(buf, req->path);
1794 return TRUE;
1798 /***********************************************************************
1799 * HTTPREQ_Destroy (internal)
1801 * Deallocate request handle
1804 static void HTTPREQ_Destroy(object_header_t *hdr)
1806 http_request_t *request = (http_request_t*) hdr;
1807 DWORD i;
1809 TRACE("\n");
1811 if(request->hCacheFile) {
1812 WCHAR url[INTERNET_MAX_URL_LENGTH];
1814 CloseHandle(request->hCacheFile);
1816 if(HTTP_GetRequestURL(request, url)) {
1817 DWORD headersLen;
1819 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1820 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1821 request->last_modified, NORMAL_CACHE_ENTRY,
1822 request->rawHeaders, headersLen, NULL, 0);
1825 heap_free(request->cacheFile);
1827 DeleteCriticalSection( &request->read_section );
1828 WININET_Release(&request->session->hdr);
1830 destroy_authinfo(request->authInfo);
1831 destroy_authinfo(request->proxyAuthInfo);
1833 heap_free(request->path);
1834 heap_free(request->verb);
1835 heap_free(request->rawHeaders);
1836 heap_free(request->version);
1837 heap_free(request->statusText);
1839 for (i = 0; i < request->nCustHeaders; i++)
1841 heap_free(request->custHeaders[i].lpszField);
1842 heap_free(request->custHeaders[i].lpszValue);
1844 destroy_data_stream(request->data_stream);
1845 heap_free(request->custHeaders);
1848 static void http_release_netconn(http_request_t *req, BOOL reuse)
1850 TRACE("%p %p\n",req, req->netconn);
1852 if(!req->netconn)
1853 return;
1855 if(reuse && req->netconn->keep_alive) {
1856 BOOL run_collector;
1858 EnterCriticalSection(&connection_pool_cs);
1860 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1861 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1862 req->netconn = NULL;
1864 run_collector = !collector_running;
1865 collector_running = TRUE;
1867 LeaveCriticalSection(&connection_pool_cs);
1869 if(run_collector) {
1870 HANDLE thread = NULL;
1871 HMODULE module;
1873 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1874 if(module)
1875 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1876 if(!thread) {
1877 EnterCriticalSection(&connection_pool_cs);
1878 collector_running = FALSE;
1879 LeaveCriticalSection(&connection_pool_cs);
1881 if(module)
1882 FreeLibrary(module);
1885 return;
1888 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1889 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1891 free_netconn(req->netconn);
1892 req->netconn = NULL;
1894 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1895 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1898 static void drain_content(http_request_t *req)
1900 BOOL try_reuse;
1902 if (!req->netconn) return;
1904 if (req->contentLength == -1)
1905 try_reuse = FALSE;
1906 else if(!strcmpW(req->verb, szHEAD))
1907 try_reuse = TRUE;
1908 else
1909 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1911 http_release_netconn(req, try_reuse);
1914 static BOOL HTTP_KeepAlive(http_request_t *request)
1916 WCHAR szVersion[10];
1917 WCHAR szConnectionResponse[20];
1918 DWORD dwBufferSize = sizeof(szVersion);
1919 BOOL keepalive = FALSE;
1921 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1922 * the connection is keep-alive by default */
1923 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1924 && !strcmpiW(szVersion, g_szHttp1_1))
1926 keepalive = TRUE;
1929 dwBufferSize = sizeof(szConnectionResponse);
1930 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1931 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1933 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1936 return keepalive;
1939 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1941 http_request_t *req = (http_request_t*)hdr;
1943 drain_content(req);
1946 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1948 http_request_t *req = (http_request_t*)hdr;
1950 switch(option) {
1951 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1953 http_session_t *session = req->session;
1954 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1956 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1958 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1959 return ERROR_INSUFFICIENT_BUFFER;
1960 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1961 /* FIXME: can't get a SOCKET from our connection since we don't use
1962 * winsock
1964 info->Socket = 0;
1965 /* FIXME: get source port from req->netConnection */
1966 info->SourcePort = 0;
1967 info->DestPort = session->hostPort;
1968 info->Flags = 0;
1969 if (HTTP_KeepAlive(req))
1970 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1971 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1972 info->Flags |= IDSI_FLAG_PROXY;
1973 if (req->netconn->useSSL)
1974 info->Flags |= IDSI_FLAG_SECURE;
1976 return ERROR_SUCCESS;
1979 case INTERNET_OPTION_SECURITY_FLAGS:
1981 DWORD flags;
1983 if (*size < sizeof(ULONG))
1984 return ERROR_INSUFFICIENT_BUFFER;
1986 *size = sizeof(DWORD);
1987 flags = 0;
1988 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1989 flags |= SECURITY_FLAG_SECURE;
1990 flags |= req->security_flags;
1991 if(req->netconn) {
1992 int bits = NETCON_GetCipherStrength(req->netconn);
1993 if (bits >= 128)
1994 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1995 else if (bits >= 56)
1996 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1997 else
1998 flags |= SECURITY_FLAG_STRENGTH_WEAK;
2000 *(DWORD *)buffer = flags;
2001 return ERROR_SUCCESS;
2004 case INTERNET_OPTION_HANDLE_TYPE:
2005 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2007 if (*size < sizeof(ULONG))
2008 return ERROR_INSUFFICIENT_BUFFER;
2010 *size = sizeof(DWORD);
2011 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2012 return ERROR_SUCCESS;
2014 case INTERNET_OPTION_URL: {
2015 WCHAR url[INTERNET_MAX_URL_LENGTH];
2016 HTTPHEADERW *host;
2017 DWORD len;
2018 WCHAR *pch;
2020 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2022 TRACE("INTERNET_OPTION_URL\n");
2024 host = HTTP_GetHeader(req, hostW);
2025 strcpyW(url, httpW);
2026 strcatW(url, host->lpszValue);
2027 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2028 *pch = 0;
2029 strcatW(url, req->path);
2031 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2033 if(unicode) {
2034 len = (strlenW(url)+1) * sizeof(WCHAR);
2035 if(*size < len)
2036 return ERROR_INSUFFICIENT_BUFFER;
2038 *size = len;
2039 strcpyW(buffer, url);
2040 return ERROR_SUCCESS;
2041 }else {
2042 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2043 if(len > *size)
2044 return ERROR_INSUFFICIENT_BUFFER;
2046 *size = len;
2047 return ERROR_SUCCESS;
2051 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2052 INTERNET_CACHE_ENTRY_INFOW *info;
2053 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2054 WCHAR url[INTERNET_MAX_URL_LENGTH];
2055 DWORD nbytes, error;
2056 BOOL ret;
2058 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2060 if (*size < sizeof(*ts))
2062 *size = sizeof(*ts);
2063 return ERROR_INSUFFICIENT_BUFFER;
2065 nbytes = 0;
2066 HTTP_GetRequestURL(req, url);
2067 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2068 error = GetLastError();
2069 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2071 if (!(info = heap_alloc(nbytes)))
2072 return ERROR_OUTOFMEMORY;
2074 GetUrlCacheEntryInfoW(url, info, &nbytes);
2076 ts->ftExpires = info->ExpireTime;
2077 ts->ftLastModified = info->LastModifiedTime;
2079 heap_free(info);
2080 *size = sizeof(*ts);
2081 return ERROR_SUCCESS;
2083 return error;
2086 case INTERNET_OPTION_DATAFILE_NAME: {
2087 DWORD req_size;
2089 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2091 if(!req->cacheFile) {
2092 *size = 0;
2093 return ERROR_INTERNET_ITEM_NOT_FOUND;
2096 if(unicode) {
2097 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2098 if(*size < req_size)
2099 return ERROR_INSUFFICIENT_BUFFER;
2101 *size = req_size;
2102 memcpy(buffer, req->cacheFile, *size);
2103 return ERROR_SUCCESS;
2104 }else {
2105 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2106 if (req_size > *size)
2107 return ERROR_INSUFFICIENT_BUFFER;
2109 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2110 -1, buffer, *size, NULL, NULL);
2111 return ERROR_SUCCESS;
2115 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2116 PCCERT_CONTEXT context;
2118 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2119 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2120 return ERROR_INSUFFICIENT_BUFFER;
2123 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2124 if(context) {
2125 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2126 DWORD len;
2128 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2129 info->ftExpiry = context->pCertInfo->NotAfter;
2130 info->ftStart = context->pCertInfo->NotBefore;
2131 len = CertNameToStrA(context->dwCertEncodingType,
2132 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2133 info->lpszSubjectInfo = LocalAlloc(0, len);
2134 if(info->lpszSubjectInfo)
2135 CertNameToStrA(context->dwCertEncodingType,
2136 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2137 info->lpszSubjectInfo, len);
2138 len = CertNameToStrA(context->dwCertEncodingType,
2139 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2140 info->lpszIssuerInfo = LocalAlloc(0, len);
2141 if(info->lpszIssuerInfo)
2142 CertNameToStrA(context->dwCertEncodingType,
2143 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2144 info->lpszIssuerInfo, len);
2145 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2146 CertFreeCertificateContext(context);
2147 return ERROR_SUCCESS;
2152 return INET_QueryOption(hdr, option, buffer, size, unicode);
2155 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2157 http_request_t *req = (http_request_t*)hdr;
2159 switch(option) {
2160 case INTERNET_OPTION_SECURITY_FLAGS:
2162 DWORD flags;
2164 if (!buffer || size != sizeof(DWORD))
2165 return ERROR_INVALID_PARAMETER;
2166 flags = *(DWORD *)buffer;
2167 TRACE("%08x\n", flags);
2168 req->security_flags = flags;
2169 if(req->netconn)
2170 req->netconn->security_flags = flags;
2171 return ERROR_SUCCESS;
2173 case INTERNET_OPTION_SEND_TIMEOUT:
2174 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2175 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2177 if (size != sizeof(DWORD))
2178 return ERROR_INVALID_PARAMETER;
2180 if(!req->netconn) {
2181 FIXME("unsupported without active connection\n");
2182 return ERROR_SUCCESS;
2185 return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
2186 *(DWORD*)buffer);
2188 case INTERNET_OPTION_USERNAME:
2189 heap_free(req->session->userName);
2190 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2191 return ERROR_SUCCESS;
2193 case INTERNET_OPTION_PASSWORD:
2194 heap_free(req->session->password);
2195 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2196 return ERROR_SUCCESS;
2197 case INTERNET_OPTION_HTTP_DECODING:
2198 if(size != sizeof(BOOL))
2199 return ERROR_INVALID_PARAMETER;
2200 req->decoding = *(BOOL*)buffer;
2201 return ERROR_SUCCESS;
2204 return ERROR_INTERNET_INVALID_OPTION;
2207 /* read some more data into the read buffer (the read section must be held) */
2208 static DWORD read_more_data( http_request_t *req, int maxlen )
2210 DWORD res;
2211 int len;
2213 if (req->read_pos)
2215 /* move existing data to the start of the buffer */
2216 if(req->read_size)
2217 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2218 req->read_pos = 0;
2221 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2223 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2224 maxlen - req->read_size, 0, &len );
2225 if(res == ERROR_SUCCESS)
2226 req->read_size += len;
2228 return res;
2231 /* remove some amount of data from the read buffer (the read section must be held) */
2232 static void remove_data( http_request_t *req, int count )
2234 if (!(req->read_size -= count)) req->read_pos = 0;
2235 else req->read_pos += count;
2238 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2240 int count, bytes_read, pos = 0;
2241 DWORD res;
2243 EnterCriticalSection( &req->read_section );
2244 for (;;)
2246 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2248 if (eol)
2250 count = eol - (req->read_buf + req->read_pos);
2251 bytes_read = count + 1;
2253 else count = bytes_read = req->read_size;
2255 count = min( count, *len - pos );
2256 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2257 pos += count;
2258 remove_data( req, bytes_read );
2259 if (eol) break;
2261 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2263 *len = 0;
2264 TRACE( "returning empty string %u\n", res);
2265 LeaveCriticalSection( &req->read_section );
2266 INTERNET_SetLastError(res);
2267 return FALSE;
2270 LeaveCriticalSection( &req->read_section );
2272 if (pos < *len)
2274 if (pos && buffer[pos - 1] == '\r') pos--;
2275 *len = pos + 1;
2277 buffer[*len - 1] = 0;
2278 TRACE( "returning %s\n", debugstr_a(buffer));
2279 return TRUE;
2282 /* check if we have reached the end of the data to read (the read section must be held) */
2283 static BOOL end_of_read_data( http_request_t *req )
2285 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2288 /* fetch some more data into the read buffer (the read section must be held) */
2289 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2291 DWORD res, read=0;
2293 if(req->read_size == sizeof(req->read_buf))
2294 return ERROR_SUCCESS;
2296 if(req->read_pos) {
2297 if(req->read_size)
2298 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2299 req->read_pos = 0;
2302 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2303 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2304 req->read_size += read;
2306 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2307 if(read_bytes)
2308 *read_bytes = read;
2309 return res;
2312 /* return the size of data available to be read immediately (the read section must be held) */
2313 static DWORD get_avail_data( http_request_t *req )
2315 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2318 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2320 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2321 DWORD avail = 0;
2323 if(req->netconn)
2324 NETCON_query_data_available(req->netconn, &avail);
2325 return netconn_stream->content_length == ~0u
2326 ? avail
2327 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2330 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2332 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2333 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2336 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2337 DWORD *read, read_mode_t read_mode)
2339 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2340 int len = 0;
2342 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2344 if(read_mode == READMODE_NOBLOCK)
2345 size = min(size, netconn_get_avail_data(stream, req));
2347 if(size && req->netconn) {
2348 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2349 len = 0;
2350 if(!len)
2351 netconn_stream->content_length = netconn_stream->content_read;
2354 netconn_stream->content_read += *read = len;
2355 TRACE("read %u bytes\n", len);
2356 return ERROR_SUCCESS;
2359 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2361 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2362 BYTE buf[1024];
2363 DWORD avail;
2364 int len;
2366 if(netconn_end_of_data(stream, req))
2367 return TRUE;
2369 do {
2370 avail = netconn_get_avail_data(stream, req);
2371 if(!avail)
2372 return FALSE;
2374 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2375 return FALSE;
2377 netconn_stream->content_read += len;
2378 }while(netconn_stream->content_read < netconn_stream->content_length);
2380 return TRUE;
2383 static void netconn_destroy(data_stream_t *stream)
2387 static const data_stream_vtbl_t netconn_stream_vtbl = {
2388 netconn_get_avail_data,
2389 netconn_end_of_data,
2390 netconn_read,
2391 netconn_drain_content,
2392 netconn_destroy
2395 /* read some more data into the read buffer (the read section must be held) */
2396 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2398 DWORD res;
2399 int len;
2401 if (stream->buf_pos)
2403 /* move existing data to the start of the buffer */
2404 if(stream->buf_size)
2405 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2406 stream->buf_pos = 0;
2409 if (maxlen == -1) maxlen = sizeof(stream->buf);
2411 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2412 maxlen - stream->buf_size, 0, &len );
2413 if(res == ERROR_SUCCESS)
2414 stream->buf_size += len;
2416 return res;
2419 /* remove some amount of data from the read buffer (the read section must be held) */
2420 static void remove_chunked_data(chunked_stream_t *stream, int count)
2422 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2423 else stream->buf_pos += count;
2426 /* discard data contents until we reach end of line (the read section must be held) */
2427 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2429 DWORD res;
2433 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2434 if (eol)
2436 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2437 break;
2439 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2440 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2441 } while (stream->buf_size);
2442 return ERROR_SUCCESS;
2445 /* read the size of the next chunk (the read section must be held) */
2446 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2448 /* TODOO */
2449 DWORD chunk_size = 0, res;
2451 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2452 return res;
2454 for (;;)
2456 while (stream->buf_size)
2458 char ch = stream->buf[stream->buf_pos];
2459 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2460 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2461 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2462 else if (ch == ';' || ch == '\r' || ch == '\n')
2464 TRACE( "reading %u byte chunk\n", chunk_size );
2465 stream->chunk_size = chunk_size;
2466 req->contentLength += chunk_size;
2467 return discard_chunked_eol(stream, req);
2469 remove_chunked_data(stream, 1);
2471 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2472 if (!stream->buf_size)
2474 stream->chunk_size = 0;
2475 return ERROR_SUCCESS;
2480 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2482 /* Allow reading only from read buffer */
2483 return 0;
2486 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2488 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2489 return !chunked_stream->chunk_size;
2492 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2493 DWORD *read, read_mode_t read_mode)
2495 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2496 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2498 if(chunked_stream->chunk_size == ~0u) {
2499 res = start_next_chunk(chunked_stream, req);
2500 if(res != ERROR_SUCCESS)
2501 return res;
2504 while(size && chunked_stream->chunk_size) {
2505 if(chunked_stream->buf_size) {
2506 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2508 /* this could block */
2509 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2510 break;
2512 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2513 remove_chunked_data(chunked_stream, read_bytes);
2514 }else {
2515 read_bytes = min(size, chunked_stream->chunk_size);
2517 if(read_mode == READMODE_NOBLOCK) {
2518 DWORD avail;
2520 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2521 break;
2522 if(read_bytes > avail)
2523 read_bytes = avail;
2525 /* this could block */
2526 if(read_bytes == chunked_stream->chunk_size)
2527 break;
2530 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2531 if(res != ERROR_SUCCESS)
2532 break;
2535 chunked_stream->chunk_size -= read_bytes;
2536 size -= read_bytes;
2537 ret_read += read_bytes;
2538 if(!chunked_stream->chunk_size) {
2539 assert(read_mode != READMODE_NOBLOCK);
2540 res = start_next_chunk(chunked_stream, req);
2541 if(res != ERROR_SUCCESS)
2542 break;
2545 if(read_mode == READMODE_ASYNC)
2546 read_mode = READMODE_NOBLOCK;
2549 TRACE("read %u bytes\n", ret_read);
2550 *read = ret_read;
2551 return res;
2554 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2556 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2558 /* FIXME: we can do better */
2559 return !chunked_stream->chunk_size;
2562 static void chunked_destroy(data_stream_t *stream)
2564 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2565 heap_free(chunked_stream);
2568 static const data_stream_vtbl_t chunked_stream_vtbl = {
2569 chunked_get_avail_data,
2570 chunked_end_of_data,
2571 chunked_read,
2572 chunked_drain_content,
2573 chunked_destroy
2576 /* set the request content length based on the headers */
2577 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2579 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2580 WCHAR encoding[20];
2581 DWORD size;
2583 if(status_code == HTTP_STATUS_NO_CONTENT) {
2584 request->contentLength = request->netconn_stream.content_length = 0;
2585 return ERROR_SUCCESS;
2588 size = sizeof(request->contentLength);
2589 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2590 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2591 request->contentLength = ~0u;
2592 request->netconn_stream.content_length = request->contentLength;
2593 request->netconn_stream.content_read = request->read_size;
2595 size = sizeof(encoding);
2596 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2597 !strcmpiW(encoding, szChunked))
2599 chunked_stream_t *chunked_stream;
2601 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2602 if(!chunked_stream)
2603 return ERROR_OUTOFMEMORY;
2605 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2606 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2607 chunked_stream->chunk_size = ~0u;
2609 if(request->read_size) {
2610 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2611 chunked_stream->buf_size = request->read_size;
2612 request->read_size = request->read_pos = 0;
2615 request->data_stream = &chunked_stream->data_stream;
2616 request->contentLength = ~0u;
2617 request->read_chunked = TRUE;
2620 if(request->decoding) {
2621 int encoding_idx;
2623 static const WCHAR gzipW[] = {'g','z','i','p',0};
2625 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2626 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2627 return init_gzip_stream(request);
2630 return ERROR_SUCCESS;
2633 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2635 INTERNET_ASYNC_RESULT iar;
2637 iar.dwResult = result;
2638 iar.dwError = error;
2640 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2641 sizeof(INTERNET_ASYNC_RESULT));
2644 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2646 DWORD res, read = 0, avail = 0;
2647 read_mode_t mode;
2649 TRACE("%p\n", req);
2651 EnterCriticalSection( &req->read_section );
2653 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2654 res = refill_read_buffer(req, mode, &read);
2655 if(res == ERROR_SUCCESS && !first_notif)
2656 avail = get_avail_data(req);
2658 LeaveCriticalSection( &req->read_section );
2660 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2661 WARN("res %u read %u, closing connection\n", res, read);
2662 http_release_netconn(req, FALSE);
2665 if(res == ERROR_SUCCESS)
2666 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2667 else
2668 send_request_complete(req, 0, res);
2671 /* read data from the http connection (the read section must be held) */
2672 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2674 DWORD current_read = 0, ret_read = 0;
2675 read_mode_t read_mode;
2676 DWORD res = ERROR_SUCCESS;
2678 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2680 EnterCriticalSection( &req->read_section );
2682 if(req->read_size) {
2683 ret_read = min(size, req->read_size);
2684 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2685 req->read_size -= ret_read;
2686 req->read_pos += ret_read;
2687 if(read_mode == READMODE_ASYNC)
2688 read_mode = READMODE_NOBLOCK;
2691 if(ret_read < size) {
2692 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2693 ret_read += current_read;
2696 LeaveCriticalSection( &req->read_section );
2698 *read = ret_read;
2699 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2701 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2702 BOOL res;
2703 DWORD written;
2705 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2706 if(!res)
2707 WARN("WriteFile failed: %u\n", GetLastError());
2710 if(size && !ret_read)
2711 http_release_netconn(req, res == ERROR_SUCCESS);
2713 return res;
2717 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2719 http_request_t *req = (http_request_t*)hdr;
2720 DWORD res;
2722 EnterCriticalSection( &req->read_section );
2723 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2724 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2726 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2727 if(res == ERROR_SUCCESS)
2728 res = hdr->dwError;
2729 LeaveCriticalSection( &req->read_section );
2731 return res;
2734 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2736 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2737 http_request_t *req = (http_request_t*)workRequest->hdr;
2738 DWORD res;
2740 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2742 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2743 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2745 send_request_complete(req, res == ERROR_SUCCESS, res);
2748 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2749 DWORD flags, DWORD_PTR context)
2751 http_request_t *req = (http_request_t*)hdr;
2752 DWORD res, size, read, error = ERROR_SUCCESS;
2754 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2755 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2757 if (buffers->dwStructSize != sizeof(*buffers))
2758 return ERROR_INVALID_PARAMETER;
2760 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2762 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2764 WORKREQUEST workRequest;
2766 if (TryEnterCriticalSection( &req->read_section ))
2768 if (get_avail_data(req))
2770 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2771 &buffers->dwBufferLength, FALSE);
2772 size = buffers->dwBufferLength;
2773 LeaveCriticalSection( &req->read_section );
2774 goto done;
2776 LeaveCriticalSection( &req->read_section );
2779 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2780 workRequest.hdr = WININET_AddRef(&req->hdr);
2781 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2783 INTERNET_AsyncCall(&workRequest);
2785 return ERROR_IO_PENDING;
2788 read = 0;
2789 size = buffers->dwBufferLength;
2791 EnterCriticalSection( &req->read_section );
2792 if(hdr->dwError == ERROR_SUCCESS)
2793 hdr->dwError = INTERNET_HANDLE_IN_USE;
2794 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2795 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2797 while(1) {
2798 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2799 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2800 if(res != ERROR_SUCCESS)
2801 break;
2803 read += buffers->dwBufferLength;
2804 if(read == size || end_of_read_data(req))
2805 break;
2807 LeaveCriticalSection( &req->read_section );
2809 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2810 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2811 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2812 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2814 EnterCriticalSection( &req->read_section );
2817 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2818 hdr->dwError = ERROR_SUCCESS;
2819 else
2820 error = hdr->dwError;
2822 LeaveCriticalSection( &req->read_section );
2823 size = buffers->dwBufferLength;
2824 buffers->dwBufferLength = read;
2826 done:
2827 if (res == ERROR_SUCCESS) {
2828 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2829 &size, sizeof(size));
2832 return res==ERROR_SUCCESS ? error : res;
2835 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2837 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2838 http_request_t *req = (http_request_t*)workRequest->hdr;
2839 DWORD res;
2841 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2843 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2844 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2846 send_request_complete(req, res == ERROR_SUCCESS, res);
2849 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2850 DWORD flags, DWORD_PTR context)
2853 http_request_t *req = (http_request_t*)hdr;
2854 DWORD res, size, read, error = ERROR_SUCCESS;
2856 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2857 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2859 if (buffers->dwStructSize != sizeof(*buffers))
2860 return ERROR_INVALID_PARAMETER;
2862 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2864 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2866 WORKREQUEST workRequest;
2868 if (TryEnterCriticalSection( &req->read_section ))
2870 if (get_avail_data(req))
2872 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2873 &buffers->dwBufferLength, FALSE);
2874 size = buffers->dwBufferLength;
2875 LeaveCriticalSection( &req->read_section );
2876 goto done;
2878 LeaveCriticalSection( &req->read_section );
2881 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2882 workRequest.hdr = WININET_AddRef(&req->hdr);
2883 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2885 INTERNET_AsyncCall(&workRequest);
2887 return ERROR_IO_PENDING;
2890 read = 0;
2891 size = buffers->dwBufferLength;
2893 EnterCriticalSection( &req->read_section );
2894 if(hdr->dwError == ERROR_SUCCESS)
2895 hdr->dwError = INTERNET_HANDLE_IN_USE;
2896 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2897 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2899 while(1) {
2900 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2901 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2902 if(res != ERROR_SUCCESS)
2903 break;
2905 read += buffers->dwBufferLength;
2906 if(read == size || end_of_read_data(req))
2907 break;
2909 LeaveCriticalSection( &req->read_section );
2911 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2912 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2913 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2914 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2916 EnterCriticalSection( &req->read_section );
2919 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2920 hdr->dwError = ERROR_SUCCESS;
2921 else
2922 error = hdr->dwError;
2924 LeaveCriticalSection( &req->read_section );
2925 size = buffers->dwBufferLength;
2926 buffers->dwBufferLength = read;
2928 done:
2929 if (res == ERROR_SUCCESS) {
2930 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2931 &size, sizeof(size));
2934 return res==ERROR_SUCCESS ? error : res;
2937 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2939 DWORD res;
2940 http_request_t *request = (http_request_t*)hdr;
2942 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2944 *written = 0;
2945 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2946 if (res == ERROR_SUCCESS)
2947 request->bytesWritten += *written;
2949 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2950 return res;
2953 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2955 http_request_t *req = (http_request_t*)workRequest->hdr;
2957 HTTP_ReceiveRequestData(req, FALSE);
2960 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2962 http_request_t *req = (http_request_t*)hdr;
2964 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2966 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2968 WORKREQUEST workRequest;
2970 /* never wait, if we can't enter the section we queue an async request right away */
2971 if (TryEnterCriticalSection( &req->read_section ))
2973 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2974 if ((*available = get_avail_data( req ))) goto done;
2975 if (end_of_read_data( req )) goto done;
2976 LeaveCriticalSection( &req->read_section );
2979 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2980 workRequest.hdr = WININET_AddRef( &req->hdr );
2982 INTERNET_AsyncCall(&workRequest);
2984 return ERROR_IO_PENDING;
2987 EnterCriticalSection( &req->read_section );
2989 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2991 refill_read_buffer( req, READMODE_ASYNC, NULL );
2992 *available = get_avail_data( req );
2995 done:
2996 LeaveCriticalSection( &req->read_section );
2998 TRACE( "returning %u\n", *available );
2999 return ERROR_SUCCESS;
3002 static const object_vtbl_t HTTPREQVtbl = {
3003 HTTPREQ_Destroy,
3004 HTTPREQ_CloseConnection,
3005 HTTPREQ_QueryOption,
3006 HTTPREQ_SetOption,
3007 HTTPREQ_ReadFile,
3008 HTTPREQ_ReadFileExA,
3009 HTTPREQ_ReadFileExW,
3010 HTTPREQ_WriteFile,
3011 HTTPREQ_QueryDataAvailable,
3012 NULL
3015 /***********************************************************************
3016 * HTTP_HttpOpenRequestW (internal)
3018 * Open a HTTP request handle
3020 * RETURNS
3021 * HINTERNET a HTTP request handle on success
3022 * NULL on failure
3025 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3026 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3027 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3028 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3030 appinfo_t *hIC = session->appInfo;
3031 http_request_t *request;
3032 DWORD len, res = ERROR_SUCCESS;
3034 TRACE("-->\n");
3036 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3037 if(!request)
3038 return ERROR_OUTOFMEMORY;
3040 request->hdr.htype = WH_HHTTPREQ;
3041 request->hdr.dwFlags = dwFlags;
3042 request->hdr.dwContext = dwContext;
3043 request->contentLength = ~0u;
3045 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3046 request->data_stream = &request->netconn_stream.data_stream;
3048 InitializeCriticalSection( &request->read_section );
3050 WININET_AddRef( &session->hdr );
3051 request->session = session;
3052 list_add_head( &session->hdr.children, &request->hdr.entry );
3054 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3055 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3056 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3057 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3059 if (lpszObjectName && *lpszObjectName) {
3060 HRESULT rc;
3062 len = 0;
3063 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3064 if (rc != E_POINTER)
3065 len = strlenW(lpszObjectName)+1;
3066 request->path = heap_alloc(len*sizeof(WCHAR));
3067 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3068 URL_ESCAPE_SPACES_ONLY);
3069 if (rc != S_OK)
3071 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3072 strcpyW(request->path,lpszObjectName);
3074 }else {
3075 static const WCHAR slashW[] = {'/',0};
3077 request->path = heap_strdupW(slashW);
3080 if (lpszReferrer && *lpszReferrer)
3081 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3083 if (lpszAcceptTypes)
3085 int i;
3086 for (i = 0; lpszAcceptTypes[i]; i++)
3088 if (!*lpszAcceptTypes[i]) continue;
3089 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3090 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3091 HTTP_ADDHDR_FLAG_REQ |
3092 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3096 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3097 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3099 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3100 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3101 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3103 WCHAR *host_name;
3105 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3107 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3108 if (!host_name) {
3109 res = ERROR_OUTOFMEMORY;
3110 goto lend;
3113 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3114 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3115 heap_free(host_name);
3117 else
3118 HTTP_ProcessHeader(request, hostW, session->hostName,
3119 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3121 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3122 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3123 INTERNET_DEFAULT_HTTPS_PORT :
3124 INTERNET_DEFAULT_HTTP_PORT);
3126 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3127 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3128 INTERNET_DEFAULT_HTTPS_PORT :
3129 INTERNET_DEFAULT_HTTP_PORT);
3131 if (hIC->proxy && hIC->proxy[0])
3132 HTTP_DealWithProxy( hIC, session, request );
3134 INTERNET_SendCallback(&session->hdr, dwContext,
3135 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3136 sizeof(HINTERNET));
3138 lend:
3139 TRACE("<-- %u (%p)\n", res, request);
3141 if(res != ERROR_SUCCESS) {
3142 WININET_Release( &request->hdr );
3143 *ret = NULL;
3144 return res;
3147 *ret = request->hdr.hInternet;
3148 return ERROR_SUCCESS;
3151 /***********************************************************************
3152 * HttpOpenRequestW (WININET.@)
3154 * Open a HTTP request handle
3156 * RETURNS
3157 * HINTERNET a HTTP request handle on success
3158 * NULL on failure
3161 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3162 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3163 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3164 DWORD dwFlags, DWORD_PTR dwContext)
3166 http_session_t *session;
3167 HINTERNET handle = NULL;
3168 DWORD res;
3170 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3171 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3172 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3173 dwFlags, dwContext);
3174 if(lpszAcceptTypes!=NULL)
3176 int i;
3177 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3178 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3181 session = (http_session_t*) get_handle_object( hHttpSession );
3182 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3184 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3185 goto lend;
3189 * My tests seem to show that the windows version does not
3190 * become asynchronous until after this point. And anyhow
3191 * if this call was asynchronous then how would you get the
3192 * necessary HINTERNET pointer returned by this function.
3195 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3196 lpszVersion, lpszReferrer, lpszAcceptTypes,
3197 dwFlags, dwContext, &handle);
3198 lend:
3199 if( session )
3200 WININET_Release( &session->hdr );
3201 TRACE("returning %p\n", handle);
3202 if(res != ERROR_SUCCESS)
3203 SetLastError(res);
3204 return handle;
3207 static const LPCWSTR header_lookup[] = {
3208 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3209 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3210 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3211 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3212 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3213 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3214 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3215 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3216 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3217 szDate, /* HTTP_QUERY_DATE = 9 */
3218 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3219 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3220 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3221 szURI, /* HTTP_QUERY_URI = 13 */
3222 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3223 NULL, /* HTTP_QUERY_COST = 15 */
3224 NULL, /* HTTP_QUERY_LINK = 16 */
3225 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3226 NULL, /* HTTP_QUERY_VERSION = 18 */
3227 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3228 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3229 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3230 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3231 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3232 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3233 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3234 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3235 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3236 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3237 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3238 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3239 NULL, /* HTTP_QUERY_FROM = 31 */
3240 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3241 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3242 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3243 szReferer, /* HTTP_QUERY_REFERER = 35 */
3244 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3245 szServer, /* HTTP_QUERY_SERVER = 37 */
3246 NULL, /* HTTP_TITLE = 38 */
3247 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3248 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3249 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3250 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3251 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3252 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3253 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3254 NULL, /* HTTP_QUERY_REFRESH = 46 */
3255 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3256 szAge, /* HTTP_QUERY_AGE = 48 */
3257 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3258 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3259 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3260 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3261 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3262 szETag, /* HTTP_QUERY_ETAG = 54 */
3263 hostW, /* HTTP_QUERY_HOST = 55 */
3264 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3265 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3266 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3267 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3268 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3269 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3270 szRange, /* HTTP_QUERY_RANGE = 62 */
3271 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3272 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3273 szVary, /* HTTP_QUERY_VARY = 65 */
3274 szVia, /* HTTP_QUERY_VIA = 66 */
3275 szWarning, /* HTTP_QUERY_WARNING = 67 */
3276 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3277 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3278 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3281 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3283 /***********************************************************************
3284 * HTTP_HttpQueryInfoW (internal)
3286 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3287 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3289 LPHTTPHEADERW lphttpHdr = NULL;
3290 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3291 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3292 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3293 INT index = -1;
3295 /* Find requested header structure */
3296 switch (level)
3298 case HTTP_QUERY_CUSTOM:
3299 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3300 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3301 break;
3302 case HTTP_QUERY_RAW_HEADERS_CRLF:
3304 LPWSTR headers;
3305 DWORD len = 0;
3306 DWORD res = ERROR_INVALID_PARAMETER;
3308 if (request_only)
3309 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3310 else
3311 headers = request->rawHeaders;
3313 if (headers)
3314 len = strlenW(headers) * sizeof(WCHAR);
3316 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3318 len += sizeof(WCHAR);
3319 res = ERROR_INSUFFICIENT_BUFFER;
3321 else if (lpBuffer)
3323 if (headers)
3324 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3325 else
3327 len = strlenW(szCrLf) * sizeof(WCHAR);
3328 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3330 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3331 res = ERROR_SUCCESS;
3333 *lpdwBufferLength = len;
3335 if (request_only) heap_free(headers);
3336 return res;
3338 case HTTP_QUERY_RAW_HEADERS:
3340 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3341 DWORD i, size = 0;
3342 LPWSTR pszString = lpBuffer;
3344 for (i = 0; ppszRawHeaderLines[i]; i++)
3345 size += strlenW(ppszRawHeaderLines[i]) + 1;
3347 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3349 HTTP_FreeTokens(ppszRawHeaderLines);
3350 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3351 return ERROR_INSUFFICIENT_BUFFER;
3353 if (pszString)
3355 for (i = 0; ppszRawHeaderLines[i]; i++)
3357 DWORD len = strlenW(ppszRawHeaderLines[i]);
3358 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3359 pszString += len+1;
3361 *pszString = '\0';
3362 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3364 *lpdwBufferLength = size * sizeof(WCHAR);
3365 HTTP_FreeTokens(ppszRawHeaderLines);
3367 return ERROR_SUCCESS;
3369 case HTTP_QUERY_STATUS_TEXT:
3370 if (request->statusText)
3372 DWORD len = strlenW(request->statusText);
3373 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3375 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3376 return ERROR_INSUFFICIENT_BUFFER;
3378 if (lpBuffer)
3380 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3381 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3383 *lpdwBufferLength = len * sizeof(WCHAR);
3384 return ERROR_SUCCESS;
3386 break;
3387 case HTTP_QUERY_VERSION:
3388 if (request->version)
3390 DWORD len = strlenW(request->version);
3391 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3393 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3394 return ERROR_INSUFFICIENT_BUFFER;
3396 if (lpBuffer)
3398 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3399 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3401 *lpdwBufferLength = len * sizeof(WCHAR);
3402 return ERROR_SUCCESS;
3404 break;
3405 case HTTP_QUERY_CONTENT_ENCODING:
3406 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3407 requested_index,request_only);
3408 break;
3409 default:
3410 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3412 if (level < LAST_TABLE_HEADER && header_lookup[level])
3413 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3414 requested_index,request_only);
3417 if (index >= 0)
3418 lphttpHdr = &request->custHeaders[index];
3420 /* Ensure header satisfies requested attributes */
3421 if (!lphttpHdr ||
3422 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3423 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3425 return ERROR_HTTP_HEADER_NOT_FOUND;
3428 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3430 /* coalesce value to requested type */
3431 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3433 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3434 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3436 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3438 time_t tmpTime;
3439 struct tm tmpTM;
3440 SYSTEMTIME *STHook;
3442 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3444 tmpTM = *gmtime(&tmpTime);
3445 STHook = (SYSTEMTIME *)lpBuffer;
3446 STHook->wDay = tmpTM.tm_mday;
3447 STHook->wHour = tmpTM.tm_hour;
3448 STHook->wMilliseconds = 0;
3449 STHook->wMinute = tmpTM.tm_min;
3450 STHook->wDayOfWeek = tmpTM.tm_wday;
3451 STHook->wMonth = tmpTM.tm_mon + 1;
3452 STHook->wSecond = tmpTM.tm_sec;
3453 STHook->wYear = tmpTM.tm_year;
3455 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3456 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3457 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3459 else if (lphttpHdr->lpszValue)
3461 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3463 if (len > *lpdwBufferLength)
3465 *lpdwBufferLength = len;
3466 return ERROR_INSUFFICIENT_BUFFER;
3468 if (lpBuffer)
3470 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3471 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3473 *lpdwBufferLength = len - sizeof(WCHAR);
3475 return ERROR_SUCCESS;
3478 /***********************************************************************
3479 * HttpQueryInfoW (WININET.@)
3481 * Queries for information about an HTTP request
3483 * RETURNS
3484 * TRUE on success
3485 * FALSE on failure
3488 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3489 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3491 http_request_t *request;
3492 DWORD res;
3494 if (TRACE_ON(wininet)) {
3495 #define FE(x) { x, #x }
3496 static const wininet_flag_info query_flags[] = {
3497 FE(HTTP_QUERY_MIME_VERSION),
3498 FE(HTTP_QUERY_CONTENT_TYPE),
3499 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3500 FE(HTTP_QUERY_CONTENT_ID),
3501 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3502 FE(HTTP_QUERY_CONTENT_LENGTH),
3503 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3504 FE(HTTP_QUERY_ALLOW),
3505 FE(HTTP_QUERY_PUBLIC),
3506 FE(HTTP_QUERY_DATE),
3507 FE(HTTP_QUERY_EXPIRES),
3508 FE(HTTP_QUERY_LAST_MODIFIED),
3509 FE(HTTP_QUERY_MESSAGE_ID),
3510 FE(HTTP_QUERY_URI),
3511 FE(HTTP_QUERY_DERIVED_FROM),
3512 FE(HTTP_QUERY_COST),
3513 FE(HTTP_QUERY_LINK),
3514 FE(HTTP_QUERY_PRAGMA),
3515 FE(HTTP_QUERY_VERSION),
3516 FE(HTTP_QUERY_STATUS_CODE),
3517 FE(HTTP_QUERY_STATUS_TEXT),
3518 FE(HTTP_QUERY_RAW_HEADERS),
3519 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3520 FE(HTTP_QUERY_CONNECTION),
3521 FE(HTTP_QUERY_ACCEPT),
3522 FE(HTTP_QUERY_ACCEPT_CHARSET),
3523 FE(HTTP_QUERY_ACCEPT_ENCODING),
3524 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3525 FE(HTTP_QUERY_AUTHORIZATION),
3526 FE(HTTP_QUERY_CONTENT_ENCODING),
3527 FE(HTTP_QUERY_FORWARDED),
3528 FE(HTTP_QUERY_FROM),
3529 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3530 FE(HTTP_QUERY_LOCATION),
3531 FE(HTTP_QUERY_ORIG_URI),
3532 FE(HTTP_QUERY_REFERER),
3533 FE(HTTP_QUERY_RETRY_AFTER),
3534 FE(HTTP_QUERY_SERVER),
3535 FE(HTTP_QUERY_TITLE),
3536 FE(HTTP_QUERY_USER_AGENT),
3537 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3538 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3539 FE(HTTP_QUERY_ACCEPT_RANGES),
3540 FE(HTTP_QUERY_SET_COOKIE),
3541 FE(HTTP_QUERY_COOKIE),
3542 FE(HTTP_QUERY_REQUEST_METHOD),
3543 FE(HTTP_QUERY_REFRESH),
3544 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3545 FE(HTTP_QUERY_AGE),
3546 FE(HTTP_QUERY_CACHE_CONTROL),
3547 FE(HTTP_QUERY_CONTENT_BASE),
3548 FE(HTTP_QUERY_CONTENT_LOCATION),
3549 FE(HTTP_QUERY_CONTENT_MD5),
3550 FE(HTTP_QUERY_CONTENT_RANGE),
3551 FE(HTTP_QUERY_ETAG),
3552 FE(HTTP_QUERY_HOST),
3553 FE(HTTP_QUERY_IF_MATCH),
3554 FE(HTTP_QUERY_IF_NONE_MATCH),
3555 FE(HTTP_QUERY_IF_RANGE),
3556 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3557 FE(HTTP_QUERY_MAX_FORWARDS),
3558 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3559 FE(HTTP_QUERY_RANGE),
3560 FE(HTTP_QUERY_TRANSFER_ENCODING),
3561 FE(HTTP_QUERY_UPGRADE),
3562 FE(HTTP_QUERY_VARY),
3563 FE(HTTP_QUERY_VIA),
3564 FE(HTTP_QUERY_WARNING),
3565 FE(HTTP_QUERY_CUSTOM)
3567 static const wininet_flag_info modifier_flags[] = {
3568 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3569 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3570 FE(HTTP_QUERY_FLAG_NUMBER),
3571 FE(HTTP_QUERY_FLAG_COALESCE)
3573 #undef FE
3574 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3575 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3576 DWORD i;
3578 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3579 TRACE(" Attribute:");
3580 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3581 if (query_flags[i].val == info) {
3582 TRACE(" %s", query_flags[i].name);
3583 break;
3586 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3587 TRACE(" Unknown (%08x)", info);
3590 TRACE(" Modifier:");
3591 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3592 if (modifier_flags[i].val & info_mod) {
3593 TRACE(" %s", modifier_flags[i].name);
3594 info_mod &= ~ modifier_flags[i].val;
3598 if (info_mod) {
3599 TRACE(" Unknown (%08x)", info_mod);
3601 TRACE("\n");
3604 request = (http_request_t*) get_handle_object( hHttpRequest );
3605 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3607 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3608 goto lend;
3611 if (lpBuffer == NULL)
3612 *lpdwBufferLength = 0;
3613 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3614 lpBuffer, lpdwBufferLength, lpdwIndex);
3616 lend:
3617 if( request )
3618 WININET_Release( &request->hdr );
3620 TRACE("%u <--\n", res);
3621 if(res != ERROR_SUCCESS)
3622 SetLastError(res);
3623 return res == ERROR_SUCCESS;
3626 /***********************************************************************
3627 * HttpQueryInfoA (WININET.@)
3629 * Queries for information about an HTTP request
3631 * RETURNS
3632 * TRUE on success
3633 * FALSE on failure
3636 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3637 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3639 BOOL result;
3640 DWORD len;
3641 WCHAR* bufferW;
3643 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3644 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3646 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3647 lpdwBufferLength, lpdwIndex );
3650 if (lpBuffer)
3652 DWORD alloclen;
3653 len = (*lpdwBufferLength)*sizeof(WCHAR);
3654 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3656 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3657 if (alloclen < len)
3658 alloclen = len;
3660 else
3661 alloclen = len;
3662 bufferW = heap_alloc(alloclen);
3663 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3664 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3665 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3666 } else
3668 bufferW = NULL;
3669 len = 0;
3672 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3673 &len, lpdwIndex );
3674 if( result )
3676 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3677 lpBuffer, *lpdwBufferLength, NULL, NULL );
3678 *lpdwBufferLength = len - 1;
3680 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3682 else
3683 /* since the strings being returned from HttpQueryInfoW should be
3684 * only ASCII characters, it is reasonable to assume that all of
3685 * the Unicode characters can be reduced to a single byte */
3686 *lpdwBufferLength = len / sizeof(WCHAR);
3688 heap_free( bufferW );
3689 return result;
3692 /***********************************************************************
3693 * HTTP_GetRedirectURL (internal)
3695 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3697 static WCHAR szHttp[] = {'h','t','t','p',0};
3698 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3699 http_session_t *session = request->session;
3700 URL_COMPONENTSW urlComponents;
3701 DWORD url_length = 0;
3702 LPWSTR orig_url;
3703 LPWSTR combined_url;
3705 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3706 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3707 urlComponents.dwSchemeLength = 0;
3708 urlComponents.lpszHostName = session->hostName;
3709 urlComponents.dwHostNameLength = 0;
3710 urlComponents.nPort = session->hostPort;
3711 urlComponents.lpszUserName = session->userName;
3712 urlComponents.dwUserNameLength = 0;
3713 urlComponents.lpszPassword = NULL;
3714 urlComponents.dwPasswordLength = 0;
3715 urlComponents.lpszUrlPath = request->path;
3716 urlComponents.dwUrlPathLength = 0;
3717 urlComponents.lpszExtraInfo = NULL;
3718 urlComponents.dwExtraInfoLength = 0;
3720 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3721 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3722 return NULL;
3724 orig_url = heap_alloc(url_length);
3726 /* convert from bytes to characters */
3727 url_length = url_length / sizeof(WCHAR) - 1;
3728 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3730 heap_free(orig_url);
3731 return NULL;
3734 url_length = 0;
3735 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3736 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3738 heap_free(orig_url);
3739 return NULL;
3741 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3743 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3745 heap_free(orig_url);
3746 heap_free(combined_url);
3747 return NULL;
3749 heap_free(orig_url);
3750 return combined_url;
3754 /***********************************************************************
3755 * HTTP_HandleRedirect (internal)
3757 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3759 http_session_t *session = request->session;
3760 appinfo_t *hIC = session->appInfo;
3761 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3762 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3763 int index;
3765 if(lpszUrl[0]=='/')
3767 /* if it's an absolute path, keep the same session info */
3768 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3770 else
3772 URL_COMPONENTSW urlComponents;
3773 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3774 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3775 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3776 static WCHAR szHttp[] = {'h','t','t','p',0};
3777 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3779 userName[0] = 0;
3780 hostName[0] = 0;
3781 protocol[0] = 0;
3783 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3784 urlComponents.lpszScheme = protocol;
3785 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3786 urlComponents.lpszHostName = hostName;
3787 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3788 urlComponents.lpszUserName = userName;
3789 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3790 urlComponents.lpszPassword = NULL;
3791 urlComponents.dwPasswordLength = 0;
3792 urlComponents.lpszUrlPath = path;
3793 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3794 urlComponents.lpszExtraInfo = NULL;
3795 urlComponents.dwExtraInfoLength = 0;
3796 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3797 return INTERNET_GetLastError();
3799 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3800 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3802 TRACE("redirect from secure page to non-secure page\n");
3803 /* FIXME: warn about from secure redirect to non-secure page */
3804 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3806 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3807 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3809 TRACE("redirect from non-secure page to secure page\n");
3810 /* FIXME: notify about redirect to secure page */
3811 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3814 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3816 if (lstrlenW(protocol)>4) /*https*/
3817 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3818 else /*http*/
3819 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3822 #if 0
3824 * This upsets redirects to binary files on sourceforge.net
3825 * and gives an html page instead of the target file
3826 * Examination of the HTTP request sent by native wininet.dll
3827 * reveals that it doesn't send a referrer in that case.
3828 * Maybe there's a flag that enables this, or maybe a referrer
3829 * shouldn't be added in case of a redirect.
3832 /* consider the current host as the referrer */
3833 if (session->lpszServerName && *session->lpszServerName)
3834 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3835 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3836 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3837 #endif
3839 heap_free(session->hostName);
3840 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3841 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3843 int len;
3844 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3845 len = lstrlenW(hostName);
3846 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3847 session->hostName = heap_alloc(len*sizeof(WCHAR));
3848 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3850 else
3851 session->hostName = heap_strdupW(hostName);
3853 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3855 heap_free(session->userName);
3856 session->userName = NULL;
3857 if (userName[0])
3858 session->userName = heap_strdupW(userName);
3860 reset_data_stream(request);
3862 if(!using_proxy) {
3863 if(strcmpiW(session->serverName, hostName)) {
3864 heap_free(session->serverName);
3865 session->serverName = heap_strdupW(hostName);
3867 session->serverPort = urlComponents.nPort;
3870 heap_free(request->path);
3871 request->path=NULL;
3872 if (*path)
3874 DWORD needed = 0;
3875 HRESULT rc;
3877 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3878 if (rc != E_POINTER)
3879 needed = strlenW(path)+1;
3880 request->path = heap_alloc(needed*sizeof(WCHAR));
3881 rc = UrlEscapeW(path, request->path, &needed,
3882 URL_ESCAPE_SPACES_ONLY);
3883 if (rc != S_OK)
3885 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3886 strcpyW(request->path,path);
3890 /* Remove custom content-type/length headers on redirects. */
3891 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3892 if (0 <= index)
3893 HTTP_DeleteCustomHeader(request, index);
3894 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3895 if (0 <= index)
3896 HTTP_DeleteCustomHeader(request, index);
3898 return ERROR_SUCCESS;
3901 /***********************************************************************
3902 * HTTP_build_req (internal)
3904 * concatenate all the strings in the request together
3906 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3908 LPCWSTR *t;
3909 LPWSTR str;
3911 for( t = list; *t ; t++ )
3912 len += strlenW( *t );
3913 len++;
3915 str = heap_alloc(len*sizeof(WCHAR));
3916 *str = 0;
3918 for( t = list; *t ; t++ )
3919 strcatW( str, *t );
3921 return str;
3924 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3926 LPWSTR lpszPath;
3927 LPWSTR requestString;
3928 INT len;
3929 INT cnt;
3930 INT responseLen;
3931 char *ascii_req;
3932 DWORD res;
3933 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3934 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3935 http_session_t *session = request->session;
3937 TRACE("\n");
3939 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3940 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3941 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3942 heap_free( lpszPath );
3944 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3945 NULL, 0, NULL, NULL );
3946 len--; /* the nul terminator isn't needed */
3947 ascii_req = heap_alloc(len);
3948 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3949 heap_free( requestString );
3951 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3953 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3954 heap_free( ascii_req );
3955 if (res != ERROR_SUCCESS)
3956 return res;
3958 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3959 if (!responseLen)
3960 return ERROR_HTTP_INVALID_HEADER;
3962 return ERROR_SUCCESS;
3965 static void HTTP_InsertCookies(http_request_t *request)
3967 DWORD cookie_size, size, cnt = 0;
3968 HTTPHEADERW *host;
3969 WCHAR *cookies;
3971 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3973 host = HTTP_GetHeader(request, hostW);
3974 if(!host)
3975 return;
3977 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3978 return;
3980 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
3981 if(!(cookies = heap_alloc(size)))
3982 return;
3984 cnt += sprintfW(cookies, cookieW);
3985 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
3986 strcatW(cookies, szCrLf);
3988 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
3990 heap_free(cookies);
3993 static WORD HTTP_ParseDay(LPCWSTR day)
3995 static const WCHAR days[7][4] = {{ 's','u','n',0 },
3996 { 'm','o','n',0 },
3997 { 't','u','e',0 },
3998 { 'w','e','d',0 },
3999 { 't','h','u',0 },
4000 { 'f','r','i',0 },
4001 { 's','a','t',0 }};
4002 int i;
4003 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4004 if (!strcmpiW(day, days[i]))
4005 return i;
4007 /* Invalid */
4008 return 7;
4011 static WORD HTTP_ParseMonth(LPCWSTR month)
4013 static const WCHAR jan[] = { 'j','a','n',0 };
4014 static const WCHAR feb[] = { 'f','e','b',0 };
4015 static const WCHAR mar[] = { 'm','a','r',0 };
4016 static const WCHAR apr[] = { 'a','p','r',0 };
4017 static const WCHAR may[] = { 'm','a','y',0 };
4018 static const WCHAR jun[] = { 'j','u','n',0 };
4019 static const WCHAR jul[] = { 'j','u','l',0 };
4020 static const WCHAR aug[] = { 'a','u','g',0 };
4021 static const WCHAR sep[] = { 's','e','p',0 };
4022 static const WCHAR oct[] = { 'o','c','t',0 };
4023 static const WCHAR nov[] = { 'n','o','v',0 };
4024 static const WCHAR dec[] = { 'd','e','c',0 };
4026 if (!strcmpiW(month, jan)) return 1;
4027 if (!strcmpiW(month, feb)) return 2;
4028 if (!strcmpiW(month, mar)) return 3;
4029 if (!strcmpiW(month, apr)) return 4;
4030 if (!strcmpiW(month, may)) return 5;
4031 if (!strcmpiW(month, jun)) return 6;
4032 if (!strcmpiW(month, jul)) return 7;
4033 if (!strcmpiW(month, aug)) return 8;
4034 if (!strcmpiW(month, sep)) return 9;
4035 if (!strcmpiW(month, oct)) return 10;
4036 if (!strcmpiW(month, nov)) return 11;
4037 if (!strcmpiW(month, dec)) return 12;
4038 /* Invalid */
4039 return 0;
4042 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4043 * optionally preceded by whitespace.
4044 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4045 * st, and sets *str to the first character after the time format.
4047 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4049 LPCWSTR ptr = *str;
4050 WCHAR *nextPtr;
4051 unsigned long num;
4053 while (isspaceW(*ptr))
4054 ptr++;
4056 num = strtoulW(ptr, &nextPtr, 10);
4057 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4059 ERR("unexpected time format %s\n", debugstr_w(ptr));
4060 return FALSE;
4062 if (num > 23)
4064 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4065 return FALSE;
4067 ptr = nextPtr + 1;
4068 st->wHour = (WORD)num;
4069 num = strtoulW(ptr, &nextPtr, 10);
4070 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4072 ERR("unexpected time format %s\n", debugstr_w(ptr));
4073 return FALSE;
4075 if (num > 59)
4077 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4078 return FALSE;
4080 ptr = nextPtr + 1;
4081 st->wMinute = (WORD)num;
4082 num = strtoulW(ptr, &nextPtr, 10);
4083 if (!nextPtr || nextPtr <= ptr)
4085 ERR("unexpected time format %s\n", debugstr_w(ptr));
4086 return FALSE;
4088 if (num > 59)
4090 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4091 return FALSE;
4093 ptr = nextPtr + 1;
4094 *str = ptr;
4095 st->wSecond = (WORD)num;
4096 return TRUE;
4099 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4101 static const WCHAR gmt[]= { 'G','M','T',0 };
4102 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4103 LPCWSTR ptr;
4104 SYSTEMTIME st = { 0 };
4105 unsigned long num;
4107 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4108 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4109 *dayPtr = *ptr;
4110 *dayPtr = 0;
4111 st.wDayOfWeek = HTTP_ParseDay(day);
4112 if (st.wDayOfWeek >= 7)
4114 ERR("unexpected weekday %s\n", debugstr_w(day));
4115 return FALSE;
4118 while (isspaceW(*ptr))
4119 ptr++;
4121 for (monthPtr = month; !isspace(*ptr) &&
4122 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4123 monthPtr++, ptr++)
4124 *monthPtr = *ptr;
4125 *monthPtr = 0;
4126 st.wMonth = HTTP_ParseMonth(month);
4127 if (!st.wMonth || st.wMonth > 12)
4129 ERR("unexpected month %s\n", debugstr_w(month));
4130 return FALSE;
4133 while (isspaceW(*ptr))
4134 ptr++;
4136 num = strtoulW(ptr, &nextPtr, 10);
4137 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4139 ERR("unexpected day %s\n", debugstr_w(ptr));
4140 return FALSE;
4142 ptr = nextPtr;
4143 st.wDay = (WORD)num;
4145 while (isspaceW(*ptr))
4146 ptr++;
4148 if (!HTTP_ParseTime(&st, &ptr))
4149 return FALSE;
4151 while (isspaceW(*ptr))
4152 ptr++;
4154 num = strtoulW(ptr, &nextPtr, 10);
4155 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4157 ERR("unexpected year %s\n", debugstr_w(ptr));
4158 return FALSE;
4160 ptr = nextPtr;
4161 st.wYear = (WORD)num;
4163 while (isspaceW(*ptr))
4164 ptr++;
4166 /* asctime() doesn't report a timezone, but some web servers do, so accept
4167 * with or without GMT.
4169 if (*ptr && strcmpW(ptr, gmt))
4171 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4172 return FALSE;
4174 return SystemTimeToFileTime(&st, ft);
4177 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4179 static const WCHAR gmt[]= { 'G','M','T',0 };
4180 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4181 LPCWSTR ptr;
4182 unsigned long num;
4183 SYSTEMTIME st = { 0 };
4185 ptr = strchrW(value, ',');
4186 if (!ptr)
4187 return FALSE;
4188 if (ptr - value != 3)
4190 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4191 return FALSE;
4193 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4194 day[3] = 0;
4195 st.wDayOfWeek = HTTP_ParseDay(day);
4196 if (st.wDayOfWeek > 6)
4198 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4199 return FALSE;
4201 ptr++;
4203 while (isspaceW(*ptr))
4204 ptr++;
4206 num = strtoulW(ptr, &nextPtr, 10);
4207 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4209 ERR("unexpected day %s\n", debugstr_w(value));
4210 return FALSE;
4212 ptr = nextPtr;
4213 st.wDay = (WORD)num;
4215 while (isspaceW(*ptr))
4216 ptr++;
4218 for (monthPtr = month; !isspace(*ptr) &&
4219 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4220 monthPtr++, ptr++)
4221 *monthPtr = *ptr;
4222 *monthPtr = 0;
4223 st.wMonth = HTTP_ParseMonth(month);
4224 if (!st.wMonth || st.wMonth > 12)
4226 ERR("unexpected month %s\n", debugstr_w(month));
4227 return FALSE;
4230 while (isspaceW(*ptr))
4231 ptr++;
4233 num = strtoulW(ptr, &nextPtr, 10);
4234 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4236 ERR("unexpected year %s\n", debugstr_w(value));
4237 return FALSE;
4239 ptr = nextPtr;
4240 st.wYear = (WORD)num;
4242 if (!HTTP_ParseTime(&st, &ptr))
4243 return FALSE;
4245 while (isspaceW(*ptr))
4246 ptr++;
4248 if (strcmpW(ptr, gmt))
4250 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4251 return FALSE;
4253 return SystemTimeToFileTime(&st, ft);
4256 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4257 * which may not be the only formats actually seen in the wild.
4258 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4259 * should be accepted as well.
4261 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4263 static const WCHAR zero[] = { '0',0 };
4264 BOOL ret;
4266 if (!strcmpW(value, zero))
4268 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4269 ret = TRUE;
4271 else if (strchrW(value, ','))
4272 ret = HTTP_ParseRfc1123Date(value, ft);
4273 else
4275 ret = HTTP_ParseDateAsAsctime(value, ft);
4276 if (!ret)
4277 ERR("unexpected date format %s\n", debugstr_w(value));
4279 return ret;
4282 static void HTTP_ProcessExpires(http_request_t *request)
4284 BOOL expirationFound = FALSE;
4285 int headerIndex;
4287 /* Look for a Cache-Control header with a max-age directive, as it takes
4288 * precedence over the Expires header.
4290 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4291 if (headerIndex != -1)
4293 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4294 LPWSTR ptr;
4296 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4298 LPWSTR comma = strchrW(ptr, ','), end, equal;
4300 if (comma)
4301 end = comma;
4302 else
4303 end = ptr + strlenW(ptr);
4304 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4306 if (*equal == '=')
4308 static const WCHAR max_age[] = {
4309 'm','a','x','-','a','g','e',0 };
4311 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4313 LPWSTR nextPtr;
4314 unsigned long age;
4316 age = strtoulW(equal + 1, &nextPtr, 10);
4317 if (nextPtr > equal + 1)
4319 LARGE_INTEGER ft;
4321 NtQuerySystemTime( &ft );
4322 /* Age is in seconds, FILETIME resolution is in
4323 * 100 nanosecond intervals.
4325 ft.QuadPart += age * (ULONGLONG)1000000;
4326 request->expires.dwLowDateTime = ft.u.LowPart;
4327 request->expires.dwHighDateTime = ft.u.HighPart;
4328 expirationFound = TRUE;
4332 if (comma)
4334 ptr = comma + 1;
4335 while (isspaceW(*ptr))
4336 ptr++;
4338 else
4339 ptr = NULL;
4342 if (!expirationFound)
4344 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4345 if (headerIndex != -1)
4347 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4348 FILETIME ft;
4350 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4352 expirationFound = TRUE;
4353 request->expires = ft;
4357 if (!expirationFound)
4359 LARGE_INTEGER t;
4361 /* With no known age, default to 10 minutes until expiration. */
4362 NtQuerySystemTime( &t );
4363 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4364 request->expires.dwLowDateTime = t.u.LowPart;
4365 request->expires.dwHighDateTime = t.u.HighPart;
4369 static void HTTP_ProcessLastModified(http_request_t *request)
4371 int headerIndex;
4373 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4374 if (headerIndex != -1)
4376 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4377 FILETIME ft;
4379 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4380 request->last_modified = ft;
4384 static void http_process_keep_alive(http_request_t *req)
4386 int index;
4388 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4389 if(index != -1)
4390 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4391 else
4392 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4395 static void HTTP_CacheRequest(http_request_t *request)
4397 WCHAR url[INTERNET_MAX_URL_LENGTH];
4398 WCHAR cacheFileName[MAX_PATH+1];
4399 BOOL b;
4401 b = HTTP_GetRequestURL(request, url);
4402 if(!b) {
4403 WARN("Could not get URL\n");
4404 return;
4407 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4408 if(b) {
4409 heap_free(request->cacheFile);
4410 CloseHandle(request->hCacheFile);
4412 request->cacheFile = heap_strdupW(cacheFileName);
4413 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4414 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4415 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4416 WARN("Could not create file: %u\n", GetLastError());
4417 request->hCacheFile = NULL;
4419 }else {
4420 WARN("Could not create cache entry: %08x\n", GetLastError());
4424 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4426 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4427 http_session_t *session = request->session;
4428 netconn_t *netconn = NULL;
4429 server_t *server;
4430 DWORD res;
4432 assert(!request->netconn);
4433 reset_data_stream(request);
4435 server = get_server(session->serverName, session->serverPort);
4436 if(!server)
4437 return ERROR_OUTOFMEMORY;
4439 res = HTTP_ResolveName(request, server);
4440 if(res != ERROR_SUCCESS) {
4441 server_release(server);
4442 return res;
4445 EnterCriticalSection(&connection_pool_cs);
4447 while(!list_empty(&server->conn_pool)) {
4448 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4449 list_remove(&netconn->pool_entry);
4451 if(NETCON_is_alive(netconn))
4452 break;
4454 TRACE("connection %p closed during idle\n", netconn);
4455 free_netconn(netconn);
4456 netconn = NULL;
4459 LeaveCriticalSection(&connection_pool_cs);
4461 if(netconn) {
4462 TRACE("<-- reusing %p netconn\n", netconn);
4463 request->netconn = netconn;
4464 *reusing = TRUE;
4465 return ERROR_SUCCESS;
4468 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4469 INTERNET_STATUS_CONNECTING_TO_SERVER,
4470 server->addr_str,
4471 strlen(server->addr_str)+1);
4473 res = create_netconn(is_https, server, request->security_flags, &netconn);
4474 server_release(server);
4475 if(res != ERROR_SUCCESS) {
4476 ERR("create_netconn failed: %u\n", res);
4477 return res;
4480 request->netconn = netconn;
4482 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4483 INTERNET_STATUS_CONNECTED_TO_SERVER,
4484 server->addr_str, strlen(server->addr_str)+1);
4486 if(is_https) {
4487 /* Note: we differ from Microsoft's WinINet here. they seem to have
4488 * a bug that causes no status callbacks to be sent when starting
4489 * a tunnel to a proxy server using the CONNECT verb. i believe our
4490 * behaviour to be more correct and to not cause any incompatibilities
4491 * because using a secure connection through a proxy server is a rare
4492 * case that would be hard for anyone to depend on */
4493 if(session->appInfo->proxy)
4494 res = HTTP_SecureProxyConnect(request);
4495 if(res == ERROR_SUCCESS)
4496 res = NETCON_secure_connect(request->netconn, session->hostName);
4497 if(res != ERROR_SUCCESS)
4499 WARN("Couldn't connect securely to host\n");
4501 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4502 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4503 || res == ERROR_INTERNET_INVALID_CA
4504 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4505 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4506 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4507 || res == ERROR_INTERNET_SEC_INVALID_CERT
4508 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4509 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4513 if(res != ERROR_SUCCESS) {
4514 http_release_netconn(request, FALSE);
4515 return res;
4518 *reusing = FALSE;
4519 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4520 return ERROR_SUCCESS;
4523 /***********************************************************************
4524 * HTTP_HttpSendRequestW (internal)
4526 * Sends the specified request to the HTTP server
4528 * RETURNS
4529 * ERROR_SUCCESS on success
4530 * win32 error code on failure
4533 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4534 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4535 DWORD dwContentLength, BOOL bEndRequest)
4537 INT cnt;
4538 BOOL redirected = FALSE;
4539 LPWSTR requestString = NULL;
4540 INT responseLen;
4541 BOOL loop_next;
4542 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4543 static const WCHAR szContentLength[] =
4544 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4545 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4546 DWORD res;
4548 TRACE("--> %p\n", request);
4550 assert(request->hdr.htype == WH_HHTTPREQ);
4552 /* if the verb is NULL default to GET */
4553 if (!request->verb)
4554 request->verb = heap_strdupW(szGET);
4556 if (dwContentLength || strcmpW(request->verb, szGET))
4558 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4559 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4560 request->bytesToWrite = dwContentLength;
4562 if (request->session->appInfo->agent)
4564 WCHAR *agent_header;
4565 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4566 int len;
4568 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4569 agent_header = heap_alloc(len * sizeof(WCHAR));
4570 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4572 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4573 heap_free(agent_header);
4575 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4577 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4578 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4580 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4582 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4583 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4584 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4589 DWORD len;
4590 BOOL reusing_connection;
4591 char *ascii_req;
4593 loop_next = FALSE;
4595 /* like native, just in case the caller forgot to call InternetReadFile
4596 * for all the data */
4597 drain_content(request);
4598 if(redirected) {
4599 request->contentLength = ~0u;
4600 request->bytesToWrite = 0;
4603 if (TRACE_ON(wininet))
4605 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4606 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4609 HTTP_FixURL(request);
4610 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4612 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4614 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4615 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4617 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4618 HTTP_InsertCookies(request);
4620 /* add the headers the caller supplied */
4621 if( lpszHeaders && dwHeaderLength )
4623 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4624 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4627 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4629 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4630 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4631 heap_free(url);
4633 else
4634 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4637 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4639 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4640 break;
4642 /* send the request as ASCII, tack on the optional data */
4643 if (!lpOptional || redirected)
4644 dwOptionalLength = 0;
4645 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4646 NULL, 0, NULL, NULL );
4647 ascii_req = heap_alloc(len + dwOptionalLength);
4648 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4649 ascii_req, len, NULL, NULL );
4650 if( lpOptional )
4651 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4652 len = (len + dwOptionalLength - 1);
4653 ascii_req[len] = 0;
4654 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4656 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4657 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4659 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4660 heap_free( ascii_req );
4661 if(res != ERROR_SUCCESS) {
4662 TRACE("send failed: %u\n", res);
4663 if(!reusing_connection)
4664 break;
4665 http_release_netconn(request, FALSE);
4666 loop_next = TRUE;
4667 continue;
4670 request->bytesWritten = dwOptionalLength;
4672 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4673 INTERNET_STATUS_REQUEST_SENT,
4674 &len, sizeof(DWORD));
4676 if (bEndRequest)
4678 DWORD dwBufferSize;
4679 DWORD dwStatusCode;
4681 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4682 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4684 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4685 /* FIXME: We should know that connection is closed before sending
4686 * headers. Otherwise wrong callbacks are executed */
4687 if(!responseLen && reusing_connection) {
4688 TRACE("Connection closed by server, reconnecting\n");
4689 http_release_netconn(request, FALSE);
4690 loop_next = TRUE;
4691 continue;
4694 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4695 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4696 sizeof(DWORD));
4698 http_process_keep_alive(request);
4699 HTTP_ProcessCookies(request);
4700 HTTP_ProcessExpires(request);
4701 HTTP_ProcessLastModified(request);
4703 dwBufferSize = sizeof(dwStatusCode);
4704 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4705 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4706 dwStatusCode = 0;
4708 res = set_content_length(request, dwStatusCode);
4709 if(res != ERROR_SUCCESS)
4710 goto lend;
4711 if(!request->contentLength)
4712 http_release_netconn(request, TRUE);
4714 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4716 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4717 dwBufferSize=sizeof(szNewLocation);
4718 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4719 dwStatusCode == HTTP_STATUS_MOVED ||
4720 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4721 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4722 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4724 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4725 dwStatusCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4727 heap_free(request->verb);
4728 request->verb = heap_strdupW(szGET);
4730 drain_content(request);
4731 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4733 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4734 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4735 res = HTTP_HandleRedirect(request, new_url);
4736 if (res == ERROR_SUCCESS)
4738 heap_free(requestString);
4739 loop_next = TRUE;
4741 heap_free( new_url );
4743 redirected = TRUE;
4746 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4748 WCHAR szAuthValue[2048];
4749 dwBufferSize=2048;
4750 if (dwStatusCode == HTTP_STATUS_DENIED)
4752 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4753 DWORD dwIndex = 0;
4754 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4756 if (HTTP_DoAuthorization(request, szAuthValue,
4757 &request->authInfo,
4758 request->session->userName,
4759 request->session->password,
4760 Host->lpszValue))
4762 heap_free(requestString);
4763 loop_next = TRUE;
4764 break;
4768 if(!loop_next) {
4769 TRACE("Cleaning wrong authorization data\n");
4770 destroy_authinfo(request->authInfo);
4771 request->authInfo = NULL;
4774 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4776 DWORD dwIndex = 0;
4777 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4779 if (HTTP_DoAuthorization(request, szAuthValue,
4780 &request->proxyAuthInfo,
4781 request->session->appInfo->proxyUsername,
4782 request->session->appInfo->proxyPassword,
4783 NULL))
4785 loop_next = TRUE;
4786 break;
4790 if(!loop_next) {
4791 TRACE("Cleaning wrong proxy authorization data\n");
4792 destroy_authinfo(request->proxyAuthInfo);
4793 request->proxyAuthInfo = NULL;
4798 else
4799 res = ERROR_SUCCESS;
4801 while (loop_next);
4803 if(res == ERROR_SUCCESS)
4804 HTTP_CacheRequest(request);
4806 lend:
4807 heap_free(requestString);
4809 /* TODO: send notification for P3P header */
4811 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4813 if (res == ERROR_SUCCESS) {
4814 if(request->contentLength && request->bytesWritten == request->bytesToWrite)
4815 HTTP_ReceiveRequestData(request, TRUE);
4816 else
4817 send_request_complete(request,
4818 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4819 }else {
4820 send_request_complete(request, 0, res);
4824 TRACE("<--\n");
4825 return res;
4828 /***********************************************************************
4830 * Helper functions for the HttpSendRequest(Ex) functions
4833 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4835 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4836 http_request_t *request = (http_request_t*) workRequest->hdr;
4838 TRACE("%p\n", request);
4840 HTTP_HttpSendRequestW(request, req->lpszHeader,
4841 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4842 req->dwContentLength, req->bEndRequest);
4844 heap_free(req->lpszHeader);
4848 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4850 INT responseLen;
4851 DWORD dwCode, dwCodeLength;
4852 DWORD dwBufferSize;
4853 DWORD res = ERROR_SUCCESS;
4855 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4856 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4858 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4859 if (!responseLen)
4860 res = ERROR_HTTP_HEADER_NOT_FOUND;
4862 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4863 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4865 /* process cookies here. Is this right? */
4866 http_process_keep_alive(request);
4867 HTTP_ProcessCookies(request);
4868 HTTP_ProcessExpires(request);
4869 HTTP_ProcessLastModified(request);
4871 dwCodeLength = sizeof(dwCode);
4872 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4873 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
4874 dwCode = 0;
4876 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
4877 if(!request->contentLength)
4878 http_release_netconn(request, TRUE);
4881 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4883 if (dwCode == HTTP_STATUS_REDIRECT ||
4884 dwCode == HTTP_STATUS_MOVED ||
4885 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
4886 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
4888 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4889 dwBufferSize=sizeof(szNewLocation);
4890 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4892 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4893 dwCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4895 heap_free(request->verb);
4896 request->verb = heap_strdupW(szGET);
4898 drain_content(request);
4899 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4901 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4902 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4903 res = HTTP_HandleRedirect(request, new_url);
4904 if (res == ERROR_SUCCESS)
4905 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4906 heap_free( new_url );
4912 if (res == ERROR_SUCCESS && request->contentLength)
4913 HTTP_ReceiveRequestData(request, TRUE);
4914 else
4915 send_request_complete(request, res == ERROR_SUCCESS, res);
4917 return res;
4920 /***********************************************************************
4921 * HttpEndRequestA (WININET.@)
4923 * Ends an HTTP request that was started by HttpSendRequestEx
4925 * RETURNS
4926 * TRUE if successful
4927 * FALSE on failure
4930 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4931 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4933 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4935 if (lpBuffersOut)
4937 SetLastError(ERROR_INVALID_PARAMETER);
4938 return FALSE;
4941 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4944 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4946 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4947 http_request_t *request = (http_request_t*)work->hdr;
4949 TRACE("%p\n", request);
4951 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4954 /***********************************************************************
4955 * HttpEndRequestW (WININET.@)
4957 * Ends an HTTP request that was started by HttpSendRequestEx
4959 * RETURNS
4960 * TRUE if successful
4961 * FALSE on failure
4964 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4965 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4967 http_request_t *request;
4968 DWORD res;
4970 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4972 if (lpBuffersOut)
4974 SetLastError(ERROR_INVALID_PARAMETER);
4975 return FALSE;
4978 request = (http_request_t*) get_handle_object( hRequest );
4980 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4982 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4983 if (request)
4984 WININET_Release( &request->hdr );
4985 return FALSE;
4987 request->hdr.dwFlags |= dwFlags;
4989 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4991 WORKREQUEST work;
4992 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
4994 work.asyncproc = AsyncHttpEndRequestProc;
4995 work.hdr = WININET_AddRef( &request->hdr );
4997 work_endrequest = &work.u.HttpEndRequestW;
4998 work_endrequest->dwFlags = dwFlags;
4999 work_endrequest->dwContext = dwContext;
5001 INTERNET_AsyncCall(&work);
5002 res = ERROR_IO_PENDING;
5004 else
5005 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5007 WININET_Release( &request->hdr );
5008 TRACE("%u <--\n", res);
5009 if(res != ERROR_SUCCESS)
5010 SetLastError(res);
5011 return res == ERROR_SUCCESS;
5014 /***********************************************************************
5015 * HttpSendRequestExA (WININET.@)
5017 * Sends the specified request to the HTTP server and allows chunked
5018 * transfers.
5020 * RETURNS
5021 * Success: TRUE
5022 * Failure: FALSE, call GetLastError() for more information.
5024 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5025 LPINTERNET_BUFFERSA lpBuffersIn,
5026 LPINTERNET_BUFFERSA lpBuffersOut,
5027 DWORD dwFlags, DWORD_PTR dwContext)
5029 INTERNET_BUFFERSW BuffersInW;
5030 BOOL rc = FALSE;
5031 DWORD headerlen;
5032 LPWSTR header = NULL;
5034 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5035 lpBuffersOut, dwFlags, dwContext);
5037 if (lpBuffersIn)
5039 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5040 if (lpBuffersIn->lpcszHeader)
5042 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5043 lpBuffersIn->dwHeadersLength,0,0);
5044 header = heap_alloc(headerlen*sizeof(WCHAR));
5045 if (!(BuffersInW.lpcszHeader = header))
5047 SetLastError(ERROR_OUTOFMEMORY);
5048 return FALSE;
5050 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5051 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5052 header, headerlen);
5054 else
5055 BuffersInW.lpcszHeader = NULL;
5056 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5057 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5058 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5059 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5060 BuffersInW.Next = NULL;
5063 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5065 heap_free(header);
5066 return rc;
5069 /***********************************************************************
5070 * HttpSendRequestExW (WININET.@)
5072 * Sends the specified request to the HTTP server and allows chunked
5073 * transfers
5075 * RETURNS
5076 * Success: TRUE
5077 * Failure: FALSE, call GetLastError() for more information.
5079 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5080 LPINTERNET_BUFFERSW lpBuffersIn,
5081 LPINTERNET_BUFFERSW lpBuffersOut,
5082 DWORD dwFlags, DWORD_PTR dwContext)
5084 http_request_t *request;
5085 http_session_t *session;
5086 appinfo_t *hIC;
5087 DWORD res;
5089 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5090 lpBuffersOut, dwFlags, dwContext);
5092 request = (http_request_t*) get_handle_object( hRequest );
5094 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5096 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5097 goto lend;
5100 session = request->session;
5101 assert(session->hdr.htype == WH_HHTTPSESSION);
5102 hIC = session->appInfo;
5103 assert(hIC->hdr.htype == WH_HINIT);
5105 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5107 WORKREQUEST workRequest;
5108 struct WORKREQ_HTTPSENDREQUESTW *req;
5110 workRequest.asyncproc = AsyncHttpSendRequestProc;
5111 workRequest.hdr = WININET_AddRef( &request->hdr );
5112 req = &workRequest.u.HttpSendRequestW;
5113 if (lpBuffersIn)
5115 DWORD size = 0;
5117 if (lpBuffersIn->lpcszHeader)
5119 if (lpBuffersIn->dwHeadersLength == ~0u)
5120 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5121 else
5122 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5124 req->lpszHeader = heap_alloc(size);
5125 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5127 else req->lpszHeader = NULL;
5129 req->dwHeaderLength = size / sizeof(WCHAR);
5130 req->lpOptional = lpBuffersIn->lpvBuffer;
5131 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5132 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5134 else
5136 req->lpszHeader = NULL;
5137 req->dwHeaderLength = 0;
5138 req->lpOptional = NULL;
5139 req->dwOptionalLength = 0;
5140 req->dwContentLength = 0;
5143 req->bEndRequest = FALSE;
5145 INTERNET_AsyncCall(&workRequest);
5147 * This is from windows.
5149 res = ERROR_IO_PENDING;
5151 else
5153 if (lpBuffersIn)
5154 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5155 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5156 lpBuffersIn->dwBufferTotal, FALSE);
5157 else
5158 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5161 lend:
5162 if ( request )
5163 WININET_Release( &request->hdr );
5165 TRACE("<---\n");
5166 SetLastError(res);
5167 return res == ERROR_SUCCESS;
5170 /***********************************************************************
5171 * HttpSendRequestW (WININET.@)
5173 * Sends the specified request to the HTTP server
5175 * RETURNS
5176 * TRUE on success
5177 * FALSE on failure
5180 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5181 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5183 http_request_t *request;
5184 http_session_t *session = NULL;
5185 appinfo_t *hIC = NULL;
5186 DWORD res = ERROR_SUCCESS;
5188 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5189 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5191 request = (http_request_t*) get_handle_object( hHttpRequest );
5192 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5194 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5195 goto lend;
5198 session = request->session;
5199 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5201 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5202 goto lend;
5205 hIC = session->appInfo;
5206 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5208 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5209 goto lend;
5212 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5214 WORKREQUEST workRequest;
5215 struct WORKREQ_HTTPSENDREQUESTW *req;
5217 workRequest.asyncproc = AsyncHttpSendRequestProc;
5218 workRequest.hdr = WININET_AddRef( &request->hdr );
5219 req = &workRequest.u.HttpSendRequestW;
5220 if (lpszHeaders)
5222 DWORD size;
5224 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5225 else size = dwHeaderLength * sizeof(WCHAR);
5227 req->lpszHeader = heap_alloc(size);
5228 memcpy(req->lpszHeader, lpszHeaders, size);
5230 else
5231 req->lpszHeader = 0;
5232 req->dwHeaderLength = dwHeaderLength;
5233 req->lpOptional = lpOptional;
5234 req->dwOptionalLength = dwOptionalLength;
5235 req->dwContentLength = dwOptionalLength;
5236 req->bEndRequest = TRUE;
5238 INTERNET_AsyncCall(&workRequest);
5240 * This is from windows.
5242 res = ERROR_IO_PENDING;
5244 else
5246 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5247 dwHeaderLength, lpOptional, dwOptionalLength,
5248 dwOptionalLength, TRUE);
5250 lend:
5251 if( request )
5252 WININET_Release( &request->hdr );
5254 SetLastError(res);
5255 return res == ERROR_SUCCESS;
5258 /***********************************************************************
5259 * HttpSendRequestA (WININET.@)
5261 * Sends the specified request to the HTTP server
5263 * RETURNS
5264 * TRUE on success
5265 * FALSE on failure
5268 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5269 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5271 BOOL result;
5272 LPWSTR szHeaders=NULL;
5273 DWORD nLen=dwHeaderLength;
5274 if(lpszHeaders!=NULL)
5276 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5277 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5278 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5280 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5281 heap_free(szHeaders);
5282 return result;
5285 /***********************************************************************
5286 * HTTPSESSION_Destroy (internal)
5288 * Deallocate session handle
5291 static void HTTPSESSION_Destroy(object_header_t *hdr)
5293 http_session_t *session = (http_session_t*) hdr;
5295 TRACE("%p\n", session);
5297 WININET_Release(&session->appInfo->hdr);
5299 heap_free(session->hostName);
5300 heap_free(session->serverName);
5301 heap_free(session->password);
5302 heap_free(session->userName);
5305 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5307 switch(option) {
5308 case INTERNET_OPTION_HANDLE_TYPE:
5309 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5311 if (*size < sizeof(ULONG))
5312 return ERROR_INSUFFICIENT_BUFFER;
5314 *size = sizeof(DWORD);
5315 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5316 return ERROR_SUCCESS;
5319 return INET_QueryOption(hdr, option, buffer, size, unicode);
5322 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5324 http_session_t *ses = (http_session_t*)hdr;
5326 switch(option) {
5327 case INTERNET_OPTION_USERNAME:
5329 heap_free(ses->userName);
5330 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5331 return ERROR_SUCCESS;
5333 case INTERNET_OPTION_PASSWORD:
5335 heap_free(ses->password);
5336 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5337 return ERROR_SUCCESS;
5339 default: break;
5342 return ERROR_INTERNET_INVALID_OPTION;
5345 static const object_vtbl_t HTTPSESSIONVtbl = {
5346 HTTPSESSION_Destroy,
5347 NULL,
5348 HTTPSESSION_QueryOption,
5349 HTTPSESSION_SetOption,
5350 NULL,
5351 NULL,
5352 NULL,
5353 NULL,
5354 NULL
5358 /***********************************************************************
5359 * HTTP_Connect (internal)
5361 * Create http session handle
5363 * RETURNS
5364 * HINTERNET a session handle on success
5365 * NULL on failure
5368 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5369 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5370 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5371 DWORD dwInternalFlags, HINTERNET *ret)
5373 http_session_t *session = NULL;
5375 TRACE("-->\n");
5377 if (!lpszServerName || !lpszServerName[0])
5378 return ERROR_INVALID_PARAMETER;
5380 assert( hIC->hdr.htype == WH_HINIT );
5382 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5383 if (!session)
5384 return ERROR_OUTOFMEMORY;
5387 * According to my tests. The name is not resolved until a request is sent
5390 session->hdr.htype = WH_HHTTPSESSION;
5391 session->hdr.dwFlags = dwFlags;
5392 session->hdr.dwContext = dwContext;
5393 session->hdr.dwInternalFlags |= dwInternalFlags;
5395 WININET_AddRef( &hIC->hdr );
5396 session->appInfo = hIC;
5397 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5399 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5400 if(hIC->proxyBypass)
5401 FIXME("Proxy bypass is ignored.\n");
5403 session->serverName = heap_strdupW(lpszServerName);
5404 session->hostName = heap_strdupW(lpszServerName);
5405 if (lpszUserName && lpszUserName[0])
5406 session->userName = heap_strdupW(lpszUserName);
5407 if (lpszPassword && lpszPassword[0])
5408 session->password = heap_strdupW(lpszPassword);
5409 session->serverPort = serverPort;
5410 session->hostPort = serverPort;
5412 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5413 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5415 INTERNET_SendCallback(&hIC->hdr, dwContext,
5416 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5417 sizeof(HINTERNET));
5421 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5422 * windows
5425 TRACE("%p --> %p\n", hIC, session);
5427 *ret = session->hdr.hInternet;
5428 return ERROR_SUCCESS;
5431 /***********************************************************************
5432 * HTTP_clear_response_headers (internal)
5434 * clear out any old response headers
5436 static void HTTP_clear_response_headers( http_request_t *request )
5438 DWORD i;
5440 for( i=0; i<request->nCustHeaders; i++)
5442 if( !request->custHeaders[i].lpszField )
5443 continue;
5444 if( !request->custHeaders[i].lpszValue )
5445 continue;
5446 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5447 continue;
5448 HTTP_DeleteCustomHeader( request, i );
5449 i--;
5453 /***********************************************************************
5454 * HTTP_GetResponseHeaders (internal)
5456 * Read server response
5458 * RETURNS
5460 * TRUE on success
5461 * FALSE on error
5463 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5465 INT cbreaks = 0;
5466 WCHAR buffer[MAX_REPLY_LEN];
5467 DWORD buflen = MAX_REPLY_LEN;
5468 BOOL bSuccess = FALSE;
5469 INT rc = 0;
5470 char bufferA[MAX_REPLY_LEN];
5471 LPWSTR status_code = NULL, status_text = NULL;
5472 DWORD cchMaxRawHeaders = 1024;
5473 LPWSTR lpszRawHeaders = NULL;
5474 LPWSTR temp;
5475 DWORD cchRawHeaders = 0;
5476 BOOL codeHundred = FALSE;
5478 TRACE("-->\n");
5480 if(!request->netconn)
5481 goto lend;
5483 do {
5484 static const WCHAR szHundred[] = {'1','0','0',0};
5486 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5488 buflen = MAX_REPLY_LEN;
5489 if (!read_line(request, bufferA, &buflen))
5490 goto lend;
5492 /* clear old response headers (eg. from a redirect response) */
5493 if (clear) {
5494 HTTP_clear_response_headers( request );
5495 clear = FALSE;
5498 rc += buflen;
5499 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5500 /* check is this a status code line? */
5501 if (!strncmpW(buffer, g_szHttp1_0, 4))
5503 /* split the version from the status code */
5504 status_code = strchrW( buffer, ' ' );
5505 if( !status_code )
5506 goto lend;
5507 *status_code++=0;
5509 /* split the status code from the status text */
5510 status_text = strchrW( status_code, ' ' );
5511 if( !status_text )
5512 goto lend;
5513 *status_text++=0;
5515 TRACE("version [%s] status code [%s] status text [%s]\n",
5516 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5518 codeHundred = (!strcmpW(status_code, szHundred));
5520 else if (!codeHundred)
5522 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5524 heap_free(request->version);
5525 heap_free(request->statusText);
5527 request->version = heap_strdupW(g_szHttp1_0);
5528 request->statusText = heap_strdupW(szOK);
5530 heap_free(request->rawHeaders);
5531 request->rawHeaders = heap_strdupW(szDefaultHeader);
5533 bSuccess = TRUE;
5534 goto lend;
5536 } while (codeHundred);
5538 /* Add status code */
5539 HTTP_ProcessHeader(request, szStatus, status_code,
5540 HTTP_ADDHDR_FLAG_REPLACE);
5542 heap_free(request->version);
5543 heap_free(request->statusText);
5545 request->version = heap_strdupW(buffer);
5546 request->statusText = heap_strdupW(status_text);
5548 /* Restore the spaces */
5549 *(status_code-1) = ' ';
5550 *(status_text-1) = ' ';
5552 /* regenerate raw headers */
5553 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5554 if (!lpszRawHeaders) goto lend;
5556 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5557 cchMaxRawHeaders *= 2;
5558 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5559 if (temp == NULL) goto lend;
5560 lpszRawHeaders = temp;
5561 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5562 cchRawHeaders += (buflen-1);
5563 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5564 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5565 lpszRawHeaders[cchRawHeaders] = '\0';
5567 /* Parse each response line */
5570 buflen = MAX_REPLY_LEN;
5571 if (read_line(request, bufferA, &buflen))
5573 LPWSTR * pFieldAndValue;
5575 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5577 if (!bufferA[0]) break;
5578 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5580 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5581 if (pFieldAndValue)
5583 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5584 cchMaxRawHeaders *= 2;
5585 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5586 if (temp == NULL) goto lend;
5587 lpszRawHeaders = temp;
5588 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5589 cchRawHeaders += (buflen-1);
5590 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5591 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5592 lpszRawHeaders[cchRawHeaders] = '\0';
5594 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5595 HTTP_ADDREQ_FLAG_ADD );
5597 HTTP_FreeTokens(pFieldAndValue);
5600 else
5602 cbreaks++;
5603 if (cbreaks >= 2)
5604 break;
5606 }while(1);
5608 /* make sure the response header is terminated with an empty line. Some apps really
5609 truly care about that empty line being there for some reason. Just add it to the
5610 header. */
5611 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5613 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5614 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5615 if (temp == NULL) goto lend;
5616 lpszRawHeaders = temp;
5619 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5621 heap_free(request->rawHeaders);
5622 request->rawHeaders = lpszRawHeaders;
5623 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5624 bSuccess = TRUE;
5626 lend:
5628 TRACE("<--\n");
5629 if (bSuccess)
5630 return rc;
5631 else
5633 heap_free(lpszRawHeaders);
5634 return 0;
5638 /***********************************************************************
5639 * HTTP_InterpretHttpHeader (internal)
5641 * Parse server response
5643 * RETURNS
5645 * Pointer to array of field, value, NULL on success.
5646 * NULL on error.
5648 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5650 LPWSTR * pTokenPair;
5651 LPWSTR pszColon;
5652 INT len;
5654 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5656 pszColon = strchrW(buffer, ':');
5657 /* must have two tokens */
5658 if (!pszColon)
5660 HTTP_FreeTokens(pTokenPair);
5661 if (buffer[0])
5662 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5663 return NULL;
5666 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5667 if (!pTokenPair[0])
5669 HTTP_FreeTokens(pTokenPair);
5670 return NULL;
5672 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5673 pTokenPair[0][pszColon - buffer] = '\0';
5675 /* skip colon */
5676 pszColon++;
5677 len = strlenW(pszColon);
5678 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5679 if (!pTokenPair[1])
5681 HTTP_FreeTokens(pTokenPair);
5682 return NULL;
5684 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5686 strip_spaces(pTokenPair[0]);
5687 strip_spaces(pTokenPair[1]);
5689 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5690 return pTokenPair;
5693 /***********************************************************************
5694 * HTTP_ProcessHeader (internal)
5696 * Stuff header into header tables according to <dwModifier>
5700 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5702 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5704 LPHTTPHEADERW lphttpHdr = NULL;
5705 INT index = -1;
5706 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5707 DWORD res = ERROR_HTTP_INVALID_HEADER;
5709 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5711 /* REPLACE wins out over ADD */
5712 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5713 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5715 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5716 index = -1;
5717 else
5718 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5720 if (index >= 0)
5722 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5723 return ERROR_HTTP_INVALID_HEADER;
5724 lphttpHdr = &request->custHeaders[index];
5726 else if (value)
5728 HTTPHEADERW hdr;
5730 hdr.lpszField = (LPWSTR)field;
5731 hdr.lpszValue = (LPWSTR)value;
5732 hdr.wFlags = hdr.wCount = 0;
5734 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5735 hdr.wFlags |= HDR_ISREQUEST;
5737 return HTTP_InsertCustomHeader(request, &hdr);
5739 /* no value to delete */
5740 else return ERROR_SUCCESS;
5742 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5743 lphttpHdr->wFlags |= HDR_ISREQUEST;
5744 else
5745 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5747 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5749 HTTP_DeleteCustomHeader( request, index );
5751 if (value)
5753 HTTPHEADERW hdr;
5755 hdr.lpszField = (LPWSTR)field;
5756 hdr.lpszValue = (LPWSTR)value;
5757 hdr.wFlags = hdr.wCount = 0;
5759 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5760 hdr.wFlags |= HDR_ISREQUEST;
5762 return HTTP_InsertCustomHeader(request, &hdr);
5765 return ERROR_SUCCESS;
5767 else if (dwModifier & COALESCEFLAGS)
5769 LPWSTR lpsztmp;
5770 WCHAR ch = 0;
5771 INT len = 0;
5772 INT origlen = strlenW(lphttpHdr->lpszValue);
5773 INT valuelen = strlenW(value);
5775 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5777 ch = ',';
5778 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5780 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5782 ch = ';';
5783 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5786 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5788 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5789 if (lpsztmp)
5791 lphttpHdr->lpszValue = lpsztmp;
5792 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5793 if (ch > 0)
5795 lphttpHdr->lpszValue[origlen] = ch;
5796 origlen++;
5797 lphttpHdr->lpszValue[origlen] = ' ';
5798 origlen++;
5801 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5802 lphttpHdr->lpszValue[len] = '\0';
5803 res = ERROR_SUCCESS;
5805 else
5807 WARN("heap_realloc (%d bytes) failed\n",len+1);
5808 res = ERROR_OUTOFMEMORY;
5811 TRACE("<-- %d\n", res);
5812 return res;
5815 /***********************************************************************
5816 * HTTP_GetCustomHeaderIndex (internal)
5818 * Return index of custom header from header array
5821 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5822 int requested_index, BOOL request_only)
5824 DWORD index;
5826 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5828 for (index = 0; index < request->nCustHeaders; index++)
5830 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5831 continue;
5833 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5834 continue;
5836 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5837 continue;
5839 if (requested_index == 0)
5840 break;
5841 requested_index --;
5844 if (index >= request->nCustHeaders)
5845 index = -1;
5847 TRACE("Return: %d\n", index);
5848 return index;
5852 /***********************************************************************
5853 * HTTP_InsertCustomHeader (internal)
5855 * Insert header into array
5858 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5860 INT count;
5861 LPHTTPHEADERW lph = NULL;
5863 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5864 count = request->nCustHeaders + 1;
5865 if (count > 1)
5866 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5867 else
5868 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5870 if (!lph)
5871 return ERROR_OUTOFMEMORY;
5873 request->custHeaders = lph;
5874 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5875 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5876 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5877 request->custHeaders[count-1].wCount= lpHdr->wCount;
5878 request->nCustHeaders++;
5880 return ERROR_SUCCESS;
5884 /***********************************************************************
5885 * HTTP_DeleteCustomHeader (internal)
5887 * Delete header from array
5888 * If this function is called, the indexs may change.
5890 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5892 if( request->nCustHeaders <= 0 )
5893 return FALSE;
5894 if( index >= request->nCustHeaders )
5895 return FALSE;
5896 request->nCustHeaders--;
5898 heap_free(request->custHeaders[index].lpszField);
5899 heap_free(request->custHeaders[index].lpszValue);
5901 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5902 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5903 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5905 return TRUE;
5909 /***********************************************************************
5910 * HTTP_VerifyValidHeader (internal)
5912 * Verify the given header is not invalid for the given http request
5915 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5917 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5918 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5919 return ERROR_HTTP_INVALID_HEADER;
5921 return ERROR_SUCCESS;
5924 /***********************************************************************
5925 * IsHostInProxyBypassList (@)
5927 * Undocumented
5930 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5932 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5933 return FALSE;
5936 /***********************************************************************
5937 * InternetShowSecurityInfoByURLA (@)
5939 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5941 FIXME("stub: %s %p\n", url, window);
5942 return FALSE;
5945 /***********************************************************************
5946 * InternetShowSecurityInfoByURLW (@)
5948 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5950 FIXME("stub: %s %p\n", debugstr_w(url), window);
5951 return FALSE;