widl: Default to the cdecl calling convention in client/server functions.
[wine/multimedia.git] / dlls / wininet / http.c
blobf8386b8bb5dff14a07da16fe196ddec999a375de
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 MAXHOSTNAME 100
143 #define MAX_FIELD_VALUE_LEN 256
144 #define MAX_FIELD_LEN 256
146 #define HTTP_REFERER szReferer
147 #define HTTP_ACCEPT szAccept
148 #define HTTP_USERAGENT szUser_Agent
150 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
151 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
152 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
154 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
155 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
156 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
158 #define COLLECT_TIME 60000
160 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
162 struct HttpAuthInfo
164 LPWSTR scheme;
165 CredHandle cred;
166 CtxtHandle ctx;
167 TimeStamp exp;
168 ULONG attr;
169 ULONG max_token;
170 void *auth_data;
171 unsigned int auth_data_len;
172 BOOL finished; /* finished authenticating */
176 typedef struct _basicAuthorizationData
178 struct list entry;
180 LPWSTR host;
181 LPWSTR realm;
182 LPSTR authorization;
183 UINT authorizationLen;
184 } basicAuthorizationData;
186 typedef struct _authorizationData
188 struct list entry;
190 LPWSTR host;
191 LPWSTR scheme;
192 LPWSTR domain;
193 UINT domain_len;
194 LPWSTR user;
195 UINT user_len;
196 LPWSTR password;
197 UINT password_len;
198 } authorizationData;
200 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
201 static struct list authorizationCache = LIST_INIT(authorizationCache);
203 static CRITICAL_SECTION authcache_cs;
204 static CRITICAL_SECTION_DEBUG critsect_debug =
206 0, 0, &authcache_cs,
207 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
208 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
210 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
212 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
213 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
214 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
215 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
216 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
217 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
218 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
219 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
220 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
221 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
222 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
224 static CRITICAL_SECTION connection_pool_cs;
225 static CRITICAL_SECTION_DEBUG connection_pool_debug =
227 0, 0, &connection_pool_cs,
228 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
229 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
231 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
233 static struct list connection_pool = LIST_INIT(connection_pool);
234 static BOOL collector_running;
236 void server_addref(server_t *server)
238 InterlockedIncrement(&server->ref);
241 void server_release(server_t *server)
243 if(InterlockedDecrement(&server->ref))
244 return;
246 if(!server->ref)
247 server->keep_until = GetTickCount64() + COLLECT_TIME;
250 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
252 server_t *iter, *server = NULL;
254 EnterCriticalSection(&connection_pool_cs);
256 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
257 if(iter->port == port && !strcmpW(iter->name, name)) {
258 server = iter;
259 server_addref(server);
260 break;
264 if(!server) {
265 server = heap_alloc(sizeof(*server));
266 if(server) {
267 server->addr_len = 0;
268 server->ref = 1;
269 server->port = port;
270 list_init(&server->conn_pool);
271 server->name = heap_strdupW(name);
272 if(server->name) {
273 list_add_head(&connection_pool, &server->entry);
274 }else {
275 heap_free(server);
276 server = NULL;
281 LeaveCriticalSection(&connection_pool_cs);
283 return server;
286 BOOL collect_connections(BOOL collect_all)
288 netconn_t *netconn, *netconn_safe;
289 server_t *server, *server_safe;
290 BOOL remaining = FALSE;
291 DWORD64 now;
293 now = GetTickCount64();
295 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
296 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
297 if(collect_all || netconn->keep_until < now) {
298 TRACE("freeing %p\n", netconn);
299 list_remove(&netconn->pool_entry);
300 free_netconn(netconn);
301 }else {
302 remaining = TRUE;
306 if(!server->ref) {
307 if(collect_all || server->keep_until < now) {
308 list_remove(&server->entry);
310 heap_free(server->name);
311 heap_free(server);
312 }else {
313 remaining = TRUE;
318 return remaining;
321 static DWORD WINAPI collect_connections_proc(void *arg)
323 BOOL remaining_conns;
325 do {
326 /* FIXME: Use more sophisticated method */
327 Sleep(5000);
329 EnterCriticalSection(&connection_pool_cs);
331 remaining_conns = collect_connections(FALSE);
332 if(!remaining_conns)
333 collector_running = FALSE;
335 LeaveCriticalSection(&connection_pool_cs);
336 }while(remaining_conns);
338 FreeLibraryAndExitThread(WININET_hModule, 0);
341 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
343 int HeaderIndex = 0;
344 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
345 if (HeaderIndex == -1)
346 return NULL;
347 else
348 return &req->custHeaders[HeaderIndex];
351 typedef enum {
352 READMODE_SYNC,
353 READMODE_ASYNC,
354 READMODE_NOBLOCK
355 } read_mode_t;
357 struct data_stream_vtbl_t {
358 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
359 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
360 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
361 BOOL (*drain_content)(data_stream_t*,http_request_t*);
362 void (*destroy)(data_stream_t*);
365 typedef struct {
366 data_stream_t data_stream;
368 BYTE buf[READ_BUFFER_SIZE];
369 DWORD buf_size;
370 DWORD buf_pos;
371 DWORD chunk_size;
372 } chunked_stream_t;
374 static inline void destroy_data_stream(data_stream_t *stream)
376 stream->vtbl->destroy(stream);
379 static void reset_data_stream(http_request_t *req)
381 destroy_data_stream(req->data_stream);
382 req->data_stream = &req->netconn_stream.data_stream;
383 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
384 req->read_chunked = req->read_gzip = FALSE;
387 #ifdef HAVE_ZLIB
389 typedef struct {
390 data_stream_t stream;
391 data_stream_t *parent_stream;
392 z_stream zstream;
393 BYTE buf[READ_BUFFER_SIZE];
394 DWORD buf_size;
395 DWORD buf_pos;
396 BOOL end_of_data;
397 } gzip_stream_t;
399 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
401 /* Allow reading only from read buffer */
402 return 0;
405 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
407 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
408 return gzip_stream->end_of_data;
411 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
412 DWORD *read, read_mode_t read_mode)
414 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
415 z_stream *zstream = &gzip_stream->zstream;
416 DWORD current_read, ret_read = 0;
417 BOOL end;
418 int zres;
419 DWORD res = ERROR_SUCCESS;
421 while(size && !gzip_stream->end_of_data) {
422 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
424 if(gzip_stream->buf_size <= 64 && !end) {
425 if(gzip_stream->buf_pos) {
426 if(gzip_stream->buf_size)
427 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
428 gzip_stream->buf_pos = 0;
430 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
431 sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
432 gzip_stream->buf_size += current_read;
433 if(res != ERROR_SUCCESS)
434 break;
435 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
436 if(!current_read && !end) {
437 if(read_mode != READMODE_NOBLOCK) {
438 WARN("unexpected end of data\n");
439 gzip_stream->end_of_data = TRUE;
441 break;
443 if(gzip_stream->buf_size <= 64 && !end)
444 continue;
447 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
448 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
449 zstream->next_out = buf+ret_read;
450 zstream->avail_out = size;
451 zres = inflate(&gzip_stream->zstream, 0);
452 current_read = size - zstream->avail_out;
453 size -= current_read;
454 ret_read += current_read;
455 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
456 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
457 if(zres == Z_STREAM_END) {
458 TRACE("end of data\n");
459 gzip_stream->end_of_data = TRUE;
460 inflateEnd(zstream);
461 }else if(zres != Z_OK) {
462 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
463 if(!ret_read)
464 res = ERROR_INTERNET_DECODING_FAILED;
465 break;
468 if(ret_read && read_mode == READMODE_ASYNC)
469 read_mode = READMODE_NOBLOCK;
472 TRACE("read %u bytes\n", ret_read);
473 *read = ret_read;
474 return res;
477 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
479 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
480 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
483 static void gzip_destroy(data_stream_t *stream)
485 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
487 destroy_data_stream(gzip_stream->parent_stream);
489 if(!gzip_stream->end_of_data)
490 inflateEnd(&gzip_stream->zstream);
491 heap_free(gzip_stream);
494 static const data_stream_vtbl_t gzip_stream_vtbl = {
495 gzip_get_avail_data,
496 gzip_end_of_data,
497 gzip_read,
498 gzip_drain_content,
499 gzip_destroy
502 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
504 return heap_alloc(items*size);
507 static void wininet_zfree(voidpf opaque, voidpf address)
509 heap_free(address);
512 static DWORD init_gzip_stream(http_request_t *req)
514 gzip_stream_t *gzip_stream;
515 int index, zres;
517 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
518 if(!gzip_stream)
519 return ERROR_OUTOFMEMORY;
521 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
522 gzip_stream->zstream.zalloc = wininet_zalloc;
523 gzip_stream->zstream.zfree = wininet_zfree;
525 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
526 if(zres != Z_OK) {
527 ERR("inflateInit failed: %d\n", zres);
528 heap_free(gzip_stream);
529 return ERROR_OUTOFMEMORY;
532 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
533 if(index != -1)
534 HTTP_DeleteCustomHeader(req, index);
536 if(req->read_size) {
537 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
538 gzip_stream->buf_size = req->read_size;
539 req->read_pos = req->read_size = 0;
542 req->read_gzip = TRUE;
543 gzip_stream->parent_stream = req->data_stream;
544 req->data_stream = &gzip_stream->stream;
545 return ERROR_SUCCESS;
548 #else
550 static DWORD init_gzip_stream(http_request_t *req)
552 ERR("gzip stream not supported, missing zlib.\n");
553 return ERROR_SUCCESS;
556 #endif
558 /***********************************************************************
559 * HTTP_Tokenize (internal)
561 * Tokenize a string, allocating memory for the tokens.
563 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
565 LPWSTR * token_array;
566 int tokens = 0;
567 int i;
568 LPCWSTR next_token;
570 if (string)
572 /* empty string has no tokens */
573 if (*string)
574 tokens++;
575 /* count tokens */
576 for (i = 0; string[i]; i++)
578 if (!strncmpW(string+i, token_string, strlenW(token_string)))
580 DWORD j;
581 tokens++;
582 /* we want to skip over separators, but not the null terminator */
583 for (j = 0; j < strlenW(token_string) - 1; j++)
584 if (!string[i+j])
585 break;
586 i += j;
591 /* add 1 for terminating NULL */
592 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
593 token_array[tokens] = NULL;
594 if (!tokens)
595 return token_array;
596 for (i = 0; i < tokens; i++)
598 int len;
599 next_token = strstrW(string, token_string);
600 if (!next_token) next_token = string+strlenW(string);
601 len = next_token - string;
602 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
603 memcpy(token_array[i], string, len*sizeof(WCHAR));
604 token_array[i][len] = '\0';
605 string = next_token+strlenW(token_string);
607 return token_array;
610 /***********************************************************************
611 * HTTP_FreeTokens (internal)
613 * Frees memory returned from HTTP_Tokenize.
615 static void HTTP_FreeTokens(LPWSTR * token_array)
617 int i;
618 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
619 heap_free(token_array);
622 static void HTTP_FixURL(http_request_t *request)
624 static const WCHAR szSlash[] = { '/',0 };
625 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
627 /* If we don't have a path we set it to root */
628 if (NULL == request->path)
629 request->path = heap_strdupW(szSlash);
630 else /* remove \r and \n*/
632 int nLen = strlenW(request->path);
633 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
635 nLen--;
636 request->path[nLen]='\0';
638 /* Replace '\' with '/' */
639 while (nLen>0) {
640 nLen--;
641 if (request->path[nLen] == '\\') request->path[nLen]='/';
645 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
646 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
647 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
649 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
650 *fixurl = '/';
651 strcpyW(fixurl + 1, request->path);
652 heap_free( request->path );
653 request->path = fixurl;
657 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
659 LPWSTR requestString;
660 DWORD len, n;
661 LPCWSTR *req;
662 UINT i;
663 LPWSTR p;
665 static const WCHAR szSpace[] = { ' ',0 };
666 static const WCHAR szColon[] = { ':',' ',0 };
667 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
669 /* allocate space for an array of all the string pointers to be added */
670 len = (request->nCustHeaders)*4 + 10;
671 req = heap_alloc(len*sizeof(LPCWSTR));
673 /* add the verb, path and HTTP version string */
674 n = 0;
675 req[n++] = verb;
676 req[n++] = szSpace;
677 req[n++] = path;
678 req[n++] = szSpace;
679 req[n++] = version;
681 /* Append custom request headers */
682 for (i = 0; i < request->nCustHeaders; i++)
684 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
686 req[n++] = szCrLf;
687 req[n++] = request->custHeaders[i].lpszField;
688 req[n++] = szColon;
689 req[n++] = request->custHeaders[i].lpszValue;
691 TRACE("Adding custom header %s (%s)\n",
692 debugstr_w(request->custHeaders[i].lpszField),
693 debugstr_w(request->custHeaders[i].lpszValue));
697 if( n >= len )
698 ERR("oops. buffer overrun\n");
700 req[n] = NULL;
701 requestString = HTTP_build_req( req, 4 );
702 heap_free( req );
705 * Set (header) termination string for request
706 * Make sure there's exactly two new lines at the end of the request
708 p = &requestString[strlenW(requestString)-1];
709 while ( (*p == '\n') || (*p == '\r') )
710 p--;
711 strcpyW( p+1, sztwocrlf );
713 return requestString;
716 static void HTTP_ProcessCookies( http_request_t *request )
718 int HeaderIndex;
719 int numCookies = 0;
720 LPHTTPHEADERW setCookieHeader;
722 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
723 return;
725 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
727 HTTPHEADERW *host;
728 const WCHAR *data;
729 WCHAR *name;
731 setCookieHeader = &request->custHeaders[HeaderIndex];
733 if (!setCookieHeader->lpszValue)
734 continue;
736 host = HTTP_GetHeader(request, hostW);
737 if(!host)
738 continue;
740 data = strchrW(setCookieHeader->lpszValue, '=');
741 if(!data)
742 continue;
744 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
745 if(!name)
746 continue;
748 data++;
749 set_cookie(host->lpszValue, request->path, name, data);
750 heap_free(name);
754 static void strip_spaces(LPWSTR start)
756 LPWSTR str = start;
757 LPWSTR end;
759 while (*str == ' ' && *str != '\0')
760 str++;
762 if (str != start)
763 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
765 end = start + strlenW(start) - 1;
766 while (end >= start && *end == ' ')
768 *end = '\0';
769 end--;
773 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
775 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
776 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
777 BOOL is_basic;
778 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
779 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
780 if (is_basic && pszRealm)
782 LPCWSTR token;
783 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
784 LPCWSTR realm;
785 ptr++;
786 *pszRealm=NULL;
787 token = strchrW(ptr,'=');
788 if (!token)
789 return TRUE;
790 realm = ptr;
791 while (*realm == ' ' && *realm != '\0')
792 realm++;
793 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
794 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
796 token++;
797 while (*token == ' ' && *token != '\0')
798 token++;
799 if (*token == '\0')
800 return TRUE;
801 *pszRealm = heap_strdupW(token);
802 strip_spaces(*pszRealm);
806 return is_basic;
809 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
811 if (!authinfo) return;
813 if (SecIsValidHandle(&authinfo->ctx))
814 DeleteSecurityContext(&authinfo->ctx);
815 if (SecIsValidHandle(&authinfo->cred))
816 FreeCredentialsHandle(&authinfo->cred);
818 heap_free(authinfo->auth_data);
819 heap_free(authinfo->scheme);
820 heap_free(authinfo);
823 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
825 basicAuthorizationData *ad;
826 UINT rc = 0;
828 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
830 EnterCriticalSection(&authcache_cs);
831 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
833 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
835 TRACE("Authorization found in cache\n");
836 *auth_data = heap_alloc(ad->authorizationLen);
837 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
838 rc = ad->authorizationLen;
839 break;
842 LeaveCriticalSection(&authcache_cs);
843 return rc;
846 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
848 struct list *cursor;
849 basicAuthorizationData* ad = NULL;
851 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
853 EnterCriticalSection(&authcache_cs);
854 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
856 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
857 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
859 ad = check;
860 break;
864 if (ad)
866 TRACE("Found match in cache, replacing\n");
867 heap_free(ad->authorization);
868 ad->authorization = heap_alloc(auth_data_len);
869 memcpy(ad->authorization, auth_data, auth_data_len);
870 ad->authorizationLen = auth_data_len;
872 else
874 ad = heap_alloc(sizeof(basicAuthorizationData));
875 ad->host = heap_strdupW(host);
876 ad->host = heap_strdupW(realm);
877 ad->authorization = heap_alloc(auth_data_len);
878 memcpy(ad->authorization, auth_data, auth_data_len);
879 ad->authorizationLen = auth_data_len;
880 list_add_head(&basicAuthorizationCache,&ad->entry);
881 TRACE("authorization cached\n");
883 LeaveCriticalSection(&authcache_cs);
886 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
887 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
889 authorizationData *ad;
891 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
893 EnterCriticalSection(&authcache_cs);
894 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
895 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
896 TRACE("Authorization found in cache\n");
898 nt_auth_identity->User = heap_strdupW(ad->user);
899 nt_auth_identity->Password = heap_strdupW(ad->password);
900 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
901 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
902 (!nt_auth_identity->Domain && ad->domain_len)) {
903 heap_free(nt_auth_identity->User);
904 heap_free(nt_auth_identity->Password);
905 heap_free(nt_auth_identity->Domain);
906 break;
909 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
910 nt_auth_identity->UserLength = ad->user_len;
911 nt_auth_identity->PasswordLength = ad->password_len;
912 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
913 nt_auth_identity->DomainLength = ad->domain_len;
914 LeaveCriticalSection(&authcache_cs);
915 return TRUE;
918 LeaveCriticalSection(&authcache_cs);
920 return FALSE;
923 static void cache_authorization(LPWSTR host, LPWSTR scheme,
924 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
926 authorizationData *ad;
927 BOOL found = FALSE;
929 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
931 EnterCriticalSection(&authcache_cs);
932 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
933 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
934 found = TRUE;
935 break;
938 if(found) {
939 heap_free(ad->user);
940 heap_free(ad->password);
941 heap_free(ad->domain);
942 } else {
943 ad = heap_alloc(sizeof(authorizationData));
944 if(!ad) {
945 LeaveCriticalSection(&authcache_cs);
946 return;
949 ad->host = heap_strdupW(host);
950 ad->scheme = heap_strdupW(scheme);
951 list_add_head(&authorizationCache, &ad->entry);
954 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
955 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
956 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
957 ad->user_len = nt_auth_identity->UserLength;
958 ad->password_len = nt_auth_identity->PasswordLength;
959 ad->domain_len = nt_auth_identity->DomainLength;
961 if(!ad->host || !ad->scheme || !ad->user || !ad->password
962 || (nt_auth_identity->Domain && !ad->domain)) {
963 heap_free(ad->host);
964 heap_free(ad->scheme);
965 heap_free(ad->user);
966 heap_free(ad->password);
967 heap_free(ad->domain);
968 list_remove(&ad->entry);
969 heap_free(ad);
972 LeaveCriticalSection(&authcache_cs);
975 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
976 struct HttpAuthInfo **ppAuthInfo,
977 LPWSTR domain_and_username, LPWSTR password,
978 LPWSTR host )
980 SECURITY_STATUS sec_status;
981 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
982 BOOL first = FALSE;
983 LPWSTR szRealm = NULL;
985 TRACE("%s\n", debugstr_w(pszAuthValue));
987 if (!pAuthInfo)
989 TimeStamp exp;
991 first = TRUE;
992 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
993 if (!pAuthInfo)
994 return FALSE;
996 SecInvalidateHandle(&pAuthInfo->cred);
997 SecInvalidateHandle(&pAuthInfo->ctx);
998 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
999 pAuthInfo->attr = 0;
1000 pAuthInfo->auth_data = NULL;
1001 pAuthInfo->auth_data_len = 0;
1002 pAuthInfo->finished = FALSE;
1004 if (is_basic_auth_value(pszAuthValue,NULL))
1006 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1007 pAuthInfo->scheme = heap_strdupW(szBasic);
1008 if (!pAuthInfo->scheme)
1010 heap_free(pAuthInfo);
1011 return FALSE;
1014 else
1016 PVOID pAuthData;
1017 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1019 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1020 if (!pAuthInfo->scheme)
1022 heap_free(pAuthInfo);
1023 return FALSE;
1026 if (domain_and_username)
1028 WCHAR *user = strchrW(domain_and_username, '\\');
1029 WCHAR *domain = domain_and_username;
1031 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1033 pAuthData = &nt_auth_identity;
1035 if (user) user++;
1036 else
1038 user = domain_and_username;
1039 domain = NULL;
1042 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1043 nt_auth_identity.User = user;
1044 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1045 nt_auth_identity.Domain = domain;
1046 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1047 nt_auth_identity.Password = password;
1048 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1050 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1052 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1053 pAuthData = &nt_auth_identity;
1054 else
1055 /* use default credentials */
1056 pAuthData = NULL;
1058 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1059 SECPKG_CRED_OUTBOUND, NULL,
1060 pAuthData, NULL,
1061 NULL, &pAuthInfo->cred,
1062 &exp);
1064 if(pAuthData && !domain_and_username) {
1065 heap_free(nt_auth_identity.User);
1066 heap_free(nt_auth_identity.Domain);
1067 heap_free(nt_auth_identity.Password);
1070 if (sec_status == SEC_E_OK)
1072 PSecPkgInfoW sec_pkg_info;
1073 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1074 if (sec_status == SEC_E_OK)
1076 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1077 FreeContextBuffer(sec_pkg_info);
1080 if (sec_status != SEC_E_OK)
1082 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1083 debugstr_w(pAuthInfo->scheme), sec_status);
1084 heap_free(pAuthInfo->scheme);
1085 heap_free(pAuthInfo);
1086 return FALSE;
1089 *ppAuthInfo = pAuthInfo;
1091 else if (pAuthInfo->finished)
1092 return FALSE;
1094 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1095 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1097 ERR("authentication scheme changed from %s to %s\n",
1098 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1099 return FALSE;
1102 if (is_basic_auth_value(pszAuthValue,&szRealm))
1104 int userlen;
1105 int passlen;
1106 char *auth_data = NULL;
1107 UINT auth_data_len = 0;
1109 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1111 if (!domain_and_username)
1113 if (host && szRealm)
1114 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1115 if (auth_data_len == 0)
1117 heap_free(szRealm);
1118 return FALSE;
1121 else
1123 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1124 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1126 /* length includes a nul terminator, which will be re-used for the ':' */
1127 auth_data = heap_alloc(userlen + 1 + passlen);
1128 if (!auth_data)
1130 heap_free(szRealm);
1131 return FALSE;
1134 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1135 auth_data[userlen] = ':';
1136 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1137 auth_data_len = userlen + 1 + passlen;
1138 if (host && szRealm)
1139 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1142 pAuthInfo->auth_data = auth_data;
1143 pAuthInfo->auth_data_len = auth_data_len;
1144 pAuthInfo->finished = TRUE;
1145 heap_free(szRealm);
1146 return TRUE;
1148 else
1150 LPCWSTR pszAuthData;
1151 SecBufferDesc out_desc, in_desc;
1152 SecBuffer out, in;
1153 unsigned char *buffer;
1154 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1155 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1157 in.BufferType = SECBUFFER_TOKEN;
1158 in.cbBuffer = 0;
1159 in.pvBuffer = NULL;
1161 in_desc.ulVersion = 0;
1162 in_desc.cBuffers = 1;
1163 in_desc.pBuffers = &in;
1165 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1166 if (*pszAuthData == ' ')
1168 pszAuthData++;
1169 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1170 in.pvBuffer = heap_alloc(in.cbBuffer);
1171 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1174 buffer = heap_alloc(pAuthInfo->max_token);
1176 out.BufferType = SECBUFFER_TOKEN;
1177 out.cbBuffer = pAuthInfo->max_token;
1178 out.pvBuffer = buffer;
1180 out_desc.ulVersion = 0;
1181 out_desc.cBuffers = 1;
1182 out_desc.pBuffers = &out;
1184 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1185 first ? NULL : &pAuthInfo->ctx,
1186 first ? request->session->serverName : NULL,
1187 context_req, 0, SECURITY_NETWORK_DREP,
1188 in.pvBuffer ? &in_desc : NULL,
1189 0, &pAuthInfo->ctx, &out_desc,
1190 &pAuthInfo->attr, &pAuthInfo->exp);
1191 if (sec_status == SEC_E_OK)
1193 pAuthInfo->finished = TRUE;
1194 pAuthInfo->auth_data = out.pvBuffer;
1195 pAuthInfo->auth_data_len = out.cbBuffer;
1196 TRACE("sending last auth packet\n");
1198 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1200 pAuthInfo->auth_data = out.pvBuffer;
1201 pAuthInfo->auth_data_len = out.cbBuffer;
1202 TRACE("sending next auth packet\n");
1204 else
1206 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1207 heap_free(out.pvBuffer);
1208 destroy_authinfo(pAuthInfo);
1209 *ppAuthInfo = NULL;
1210 return FALSE;
1214 return TRUE;
1217 /***********************************************************************
1218 * HTTP_HttpAddRequestHeadersW (internal)
1220 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1221 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1223 LPWSTR lpszStart;
1224 LPWSTR lpszEnd;
1225 LPWSTR buffer;
1226 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1228 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1230 if( dwHeaderLength == ~0U )
1231 len = strlenW(lpszHeader);
1232 else
1233 len = dwHeaderLength;
1234 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1235 lstrcpynW( buffer, lpszHeader, len + 1);
1237 lpszStart = buffer;
1241 LPWSTR * pFieldAndValue;
1243 lpszEnd = lpszStart;
1245 while (*lpszEnd != '\0')
1247 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1248 break;
1249 lpszEnd++;
1252 if (*lpszStart == '\0')
1253 break;
1255 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1257 *lpszEnd = '\0';
1258 lpszEnd++; /* Jump over newline */
1260 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1261 if (*lpszStart == '\0')
1263 /* Skip 0-length headers */
1264 lpszStart = lpszEnd;
1265 res = ERROR_SUCCESS;
1266 continue;
1268 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1269 if (pFieldAndValue)
1271 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1272 if (res == ERROR_SUCCESS)
1273 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1274 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1275 HTTP_FreeTokens(pFieldAndValue);
1278 lpszStart = lpszEnd;
1279 } while (res == ERROR_SUCCESS);
1281 heap_free(buffer);
1282 return res;
1285 /***********************************************************************
1286 * HttpAddRequestHeadersW (WININET.@)
1288 * Adds one or more HTTP header to the request handler
1290 * NOTE
1291 * On Windows if dwHeaderLength includes the trailing '\0', then
1292 * HttpAddRequestHeadersW() adds it too. However this results in an
1293 * invalid Http header which is rejected by some servers so we probably
1294 * don't need to match Windows on that point.
1296 * RETURNS
1297 * TRUE on success
1298 * FALSE on failure
1301 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1302 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1304 http_request_t *request;
1305 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1307 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1309 if (!lpszHeader)
1310 return TRUE;
1312 request = (http_request_t*) get_handle_object( hHttpRequest );
1313 if (request && request->hdr.htype == WH_HHTTPREQ)
1314 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1315 if( request )
1316 WININET_Release( &request->hdr );
1318 if(res != ERROR_SUCCESS)
1319 SetLastError(res);
1320 return res == ERROR_SUCCESS;
1323 /***********************************************************************
1324 * HttpAddRequestHeadersA (WININET.@)
1326 * Adds one or more HTTP header to the request handler
1328 * RETURNS
1329 * TRUE on success
1330 * FALSE on failure
1333 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1334 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1336 DWORD len;
1337 LPWSTR hdr;
1338 BOOL r;
1340 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1342 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1343 hdr = heap_alloc(len*sizeof(WCHAR));
1344 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1345 if( dwHeaderLength != ~0U )
1346 dwHeaderLength = len;
1348 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1350 heap_free( hdr );
1351 return r;
1354 /***********************************************************************
1355 * HttpOpenRequestA (WININET.@)
1357 * Open a HTTP request handle
1359 * RETURNS
1360 * HINTERNET a HTTP request handle on success
1361 * NULL on failure
1364 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1365 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1366 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1367 DWORD dwFlags, DWORD_PTR dwContext)
1369 LPWSTR szVerb = NULL, szObjectName = NULL;
1370 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1371 INT acceptTypesCount;
1372 HINTERNET rc = FALSE;
1373 LPCSTR *types;
1375 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1376 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1377 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1378 dwFlags, dwContext);
1380 if (lpszVerb)
1382 szVerb = heap_strdupAtoW(lpszVerb);
1383 if ( !szVerb )
1384 goto end;
1387 if (lpszObjectName)
1389 szObjectName = heap_strdupAtoW(lpszObjectName);
1390 if ( !szObjectName )
1391 goto end;
1394 if (lpszVersion)
1396 szVersion = heap_strdupAtoW(lpszVersion);
1397 if ( !szVersion )
1398 goto end;
1401 if (lpszReferrer)
1403 szReferrer = heap_strdupAtoW(lpszReferrer);
1404 if ( !szReferrer )
1405 goto end;
1408 if (lpszAcceptTypes)
1410 acceptTypesCount = 0;
1411 types = lpszAcceptTypes;
1412 while (*types)
1414 __TRY
1416 /* find out how many there are */
1417 if (*types && **types)
1419 TRACE("accept type: %s\n", debugstr_a(*types));
1420 acceptTypesCount++;
1423 __EXCEPT_PAGE_FAULT
1425 WARN("invalid accept type pointer\n");
1427 __ENDTRY;
1428 types++;
1430 szAcceptTypes = heap_alloc(sizeof(WCHAR *) * (acceptTypesCount+1));
1431 if (!szAcceptTypes) goto end;
1433 acceptTypesCount = 0;
1434 types = lpszAcceptTypes;
1435 while (*types)
1437 __TRY
1439 if (*types && **types)
1440 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1442 __EXCEPT_PAGE_FAULT
1444 /* ignore invalid pointer */
1446 __ENDTRY;
1447 types++;
1449 szAcceptTypes[acceptTypesCount] = NULL;
1452 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1453 szVersion, szReferrer,
1454 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1456 end:
1457 if (szAcceptTypes)
1459 acceptTypesCount = 0;
1460 while (szAcceptTypes[acceptTypesCount])
1462 heap_free(szAcceptTypes[acceptTypesCount]);
1463 acceptTypesCount++;
1465 heap_free(szAcceptTypes);
1467 heap_free(szReferrer);
1468 heap_free(szVersion);
1469 heap_free(szObjectName);
1470 heap_free(szVerb);
1471 return rc;
1474 /***********************************************************************
1475 * HTTP_EncodeBase64
1477 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1479 UINT n = 0, x;
1480 static const CHAR HTTP_Base64Enc[] =
1481 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1483 while( len > 0 )
1485 /* first 6 bits, all from bin[0] */
1486 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1487 x = (bin[0] & 3) << 4;
1489 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1490 if( len == 1 )
1492 base64[n++] = HTTP_Base64Enc[x];
1493 base64[n++] = '=';
1494 base64[n++] = '=';
1495 break;
1497 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1498 x = ( bin[1] & 0x0f ) << 2;
1500 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1501 if( len == 2 )
1503 base64[n++] = HTTP_Base64Enc[x];
1504 base64[n++] = '=';
1505 break;
1507 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1509 /* last 6 bits, all from bin [2] */
1510 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1511 bin += 3;
1512 len -= 3;
1514 base64[n] = 0;
1515 return n;
1518 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1519 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1520 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1521 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1522 static const signed char HTTP_Base64Dec[256] =
1524 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1525 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1526 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1527 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1528 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1529 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1530 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1531 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1532 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1533 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1534 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1535 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1536 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1537 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1538 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1539 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1540 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1541 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1542 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1543 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1544 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1545 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1546 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1547 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1548 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1549 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1551 #undef CH
1553 /***********************************************************************
1554 * HTTP_DecodeBase64
1556 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1558 unsigned int n = 0;
1560 while(*base64)
1562 signed char in[4];
1564 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1565 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1566 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1567 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1569 WARN("invalid base64: %s\n", debugstr_w(base64));
1570 return 0;
1572 if (bin)
1573 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1574 n++;
1576 if ((base64[2] == '=') && (base64[3] == '='))
1577 break;
1578 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1579 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1581 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1582 return 0;
1584 if (bin)
1585 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1586 n++;
1588 if (base64[3] == '=')
1589 break;
1590 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1591 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1593 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1594 return 0;
1596 if (bin)
1597 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1598 n++;
1600 base64 += 4;
1603 return n;
1606 /***********************************************************************
1607 * HTTP_InsertAuthorization
1609 * Insert or delete the authorization field in the request header.
1611 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1613 if (pAuthInfo)
1615 static const WCHAR wszSpace[] = {' ',0};
1616 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1617 unsigned int len;
1618 WCHAR *authorization = NULL;
1620 if (pAuthInfo->auth_data_len)
1622 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1623 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1624 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1625 if (!authorization)
1626 return FALSE;
1628 strcpyW(authorization, pAuthInfo->scheme);
1629 strcatW(authorization, wszSpace);
1630 HTTP_EncodeBase64(pAuthInfo->auth_data,
1631 pAuthInfo->auth_data_len,
1632 authorization+strlenW(authorization));
1634 /* clear the data as it isn't valid now that it has been sent to the
1635 * server, unless it's Basic authentication which doesn't do
1636 * connection tracking */
1637 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1639 heap_free(pAuthInfo->auth_data);
1640 pAuthInfo->auth_data = NULL;
1641 pAuthInfo->auth_data_len = 0;
1645 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1647 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1648 heap_free(authorization);
1650 return TRUE;
1653 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
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 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1662 strcpyW( url, new_location );
1664 else
1666 static const WCHAR slash[] = { '/',0 };
1667 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1668 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1669 http_session_t *session = req->session;
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 TRACE("url=%s\n", debugstr_w(url));
1684 return url;
1687 /***********************************************************************
1688 * HTTP_DealWithProxy
1690 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1692 WCHAR buf[MAXHOSTNAME];
1693 WCHAR protoProxy[MAXHOSTNAME + 15];
1694 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1695 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1696 static WCHAR szNul[] = { 0 };
1697 URL_COMPONENTSW UrlComponents;
1698 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1699 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1700 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1702 memset( &UrlComponents, 0, sizeof UrlComponents );
1703 UrlComponents.dwStructSize = sizeof UrlComponents;
1704 UrlComponents.lpszHostName = buf;
1705 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1707 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1708 return FALSE;
1709 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1710 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1711 sprintfW(proxy, szFormat, protoProxy);
1712 else
1713 strcpyW(proxy, protoProxy);
1714 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1715 return FALSE;
1716 if( UrlComponents.dwHostNameLength == 0 )
1717 return FALSE;
1719 if( !request->path )
1720 request->path = szNul;
1722 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1723 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1725 heap_free(session->serverName);
1726 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1727 session->serverPort = UrlComponents.nPort;
1729 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1730 return TRUE;
1733 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
1735 socklen_t addr_len;
1736 const void *addr;
1738 if(server->addr_len)
1739 return ERROR_SUCCESS;
1741 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1742 INTERNET_STATUS_RESOLVING_NAME,
1743 server->name,
1744 (strlenW(server->name)+1) * sizeof(WCHAR));
1746 addr_len = sizeof(server->addr);
1747 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1748 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1750 switch(server->addr.ss_family) {
1751 case AF_INET:
1752 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1753 break;
1754 case AF_INET6:
1755 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1756 break;
1757 default:
1758 WARN("unsupported family %d\n", server->addr.ss_family);
1759 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1762 server->addr_len = addr_len;
1763 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1764 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1765 INTERNET_STATUS_NAME_RESOLVED,
1766 server->addr_str, strlen(server->addr_str)+1);
1768 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1769 return ERROR_SUCCESS;
1772 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1774 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1775 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1776 static const WCHAR slash[] = { '/',0 };
1777 LPHTTPHEADERW host_header;
1778 LPCWSTR scheme;
1780 host_header = HTTP_GetHeader(req, hostW);
1781 if(!host_header)
1782 return FALSE;
1784 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1785 scheme = https;
1786 else
1787 scheme = http;
1788 strcpyW(buf, scheme);
1789 strcatW(buf, host_header->lpszValue);
1790 if (req->path[0] != '/')
1791 strcatW(buf, slash);
1792 strcatW(buf, req->path);
1793 return TRUE;
1797 /***********************************************************************
1798 * HTTPREQ_Destroy (internal)
1800 * Deallocate request handle
1803 static void HTTPREQ_Destroy(object_header_t *hdr)
1805 http_request_t *request = (http_request_t*) hdr;
1806 DWORD i;
1808 TRACE("\n");
1810 if(request->hCacheFile) {
1811 WCHAR url[INTERNET_MAX_URL_LENGTH];
1813 CloseHandle(request->hCacheFile);
1815 if(HTTP_GetRequestURL(request, url)) {
1816 DWORD headersLen;
1818 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1819 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1820 request->last_modified, NORMAL_CACHE_ENTRY,
1821 request->rawHeaders, headersLen, NULL, 0);
1824 heap_free(request->cacheFile);
1826 DeleteCriticalSection( &request->read_section );
1827 WININET_Release(&request->session->hdr);
1829 destroy_authinfo(request->authInfo);
1830 destroy_authinfo(request->proxyAuthInfo);
1832 heap_free(request->path);
1833 heap_free(request->verb);
1834 heap_free(request->rawHeaders);
1835 heap_free(request->version);
1836 heap_free(request->statusText);
1838 for (i = 0; i < request->nCustHeaders; i++)
1840 heap_free(request->custHeaders[i].lpszField);
1841 heap_free(request->custHeaders[i].lpszValue);
1843 destroy_data_stream(request->data_stream);
1844 heap_free(request->custHeaders);
1847 static void http_release_netconn(http_request_t *req, BOOL reuse)
1849 TRACE("%p %p\n",req, req->netconn);
1851 if(!req->netconn)
1852 return;
1854 if(reuse && req->netconn->keep_alive) {
1855 BOOL run_collector;
1857 EnterCriticalSection(&connection_pool_cs);
1859 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1860 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1861 req->netconn = NULL;
1863 run_collector = !collector_running;
1864 collector_running = TRUE;
1866 LeaveCriticalSection(&connection_pool_cs);
1868 if(run_collector) {
1869 HANDLE thread = NULL;
1870 HMODULE module;
1872 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1873 if(module)
1874 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1875 if(!thread) {
1876 EnterCriticalSection(&connection_pool_cs);
1877 collector_running = FALSE;
1878 LeaveCriticalSection(&connection_pool_cs);
1880 if(module)
1881 FreeLibrary(module);
1884 return;
1887 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1888 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1890 free_netconn(req->netconn);
1891 req->netconn = NULL;
1893 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1894 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1897 static void drain_content(http_request_t *req)
1899 BOOL try_reuse;
1901 if (!req->netconn) return;
1903 if (req->contentLength == -1)
1904 try_reuse = FALSE;
1905 else if(!strcmpW(req->verb, szHEAD))
1906 try_reuse = TRUE;
1907 else
1908 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1910 http_release_netconn(req, try_reuse);
1913 static BOOL HTTP_KeepAlive(http_request_t *request)
1915 WCHAR szVersion[10];
1916 WCHAR szConnectionResponse[20];
1917 DWORD dwBufferSize = sizeof(szVersion);
1918 BOOL keepalive = FALSE;
1920 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1921 * the connection is keep-alive by default */
1922 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1923 && !strcmpiW(szVersion, g_szHttp1_1))
1925 keepalive = TRUE;
1928 dwBufferSize = sizeof(szConnectionResponse);
1929 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1930 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1932 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1935 return keepalive;
1938 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1940 http_request_t *req = (http_request_t*)hdr;
1942 drain_content(req);
1945 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1947 http_request_t *req = (http_request_t*)hdr;
1949 switch(option) {
1950 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1952 http_session_t *session = req->session;
1953 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1955 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1957 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1958 return ERROR_INSUFFICIENT_BUFFER;
1959 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1960 /* FIXME: can't get a SOCKET from our connection since we don't use
1961 * winsock
1963 info->Socket = 0;
1964 /* FIXME: get source port from req->netConnection */
1965 info->SourcePort = 0;
1966 info->DestPort = session->hostPort;
1967 info->Flags = 0;
1968 if (HTTP_KeepAlive(req))
1969 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1970 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1971 info->Flags |= IDSI_FLAG_PROXY;
1972 if (req->netconn->useSSL)
1973 info->Flags |= IDSI_FLAG_SECURE;
1975 return ERROR_SUCCESS;
1978 case INTERNET_OPTION_SECURITY_FLAGS:
1980 DWORD flags;
1982 if (*size < sizeof(ULONG))
1983 return ERROR_INSUFFICIENT_BUFFER;
1985 *size = sizeof(DWORD);
1986 flags = 0;
1987 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1988 flags |= SECURITY_FLAG_SECURE;
1989 flags |= req->security_flags;
1990 if(req->netconn) {
1991 int bits = NETCON_GetCipherStrength(req->netconn);
1992 if (bits >= 128)
1993 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1994 else if (bits >= 56)
1995 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1996 else
1997 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1999 *(DWORD *)buffer = flags;
2000 return ERROR_SUCCESS;
2003 case INTERNET_OPTION_HANDLE_TYPE:
2004 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2006 if (*size < sizeof(ULONG))
2007 return ERROR_INSUFFICIENT_BUFFER;
2009 *size = sizeof(DWORD);
2010 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2011 return ERROR_SUCCESS;
2013 case INTERNET_OPTION_URL: {
2014 WCHAR url[INTERNET_MAX_URL_LENGTH];
2015 HTTPHEADERW *host;
2016 DWORD len;
2017 WCHAR *pch;
2019 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2021 TRACE("INTERNET_OPTION_URL\n");
2023 host = HTTP_GetHeader(req, hostW);
2024 strcpyW(url, httpW);
2025 strcatW(url, host->lpszValue);
2026 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2027 *pch = 0;
2028 strcatW(url, req->path);
2030 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2032 if(unicode) {
2033 len = (strlenW(url)+1) * sizeof(WCHAR);
2034 if(*size < len)
2035 return ERROR_INSUFFICIENT_BUFFER;
2037 *size = len;
2038 strcpyW(buffer, url);
2039 return ERROR_SUCCESS;
2040 }else {
2041 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2042 if(len > *size)
2043 return ERROR_INSUFFICIENT_BUFFER;
2045 *size = len;
2046 return ERROR_SUCCESS;
2050 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2051 INTERNET_CACHE_ENTRY_INFOW *info;
2052 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2053 WCHAR url[INTERNET_MAX_URL_LENGTH];
2054 DWORD nbytes, error;
2055 BOOL ret;
2057 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2059 if (*size < sizeof(*ts))
2061 *size = sizeof(*ts);
2062 return ERROR_INSUFFICIENT_BUFFER;
2064 nbytes = 0;
2065 HTTP_GetRequestURL(req, url);
2066 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2067 error = GetLastError();
2068 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2070 if (!(info = heap_alloc(nbytes)))
2071 return ERROR_OUTOFMEMORY;
2073 GetUrlCacheEntryInfoW(url, info, &nbytes);
2075 ts->ftExpires = info->ExpireTime;
2076 ts->ftLastModified = info->LastModifiedTime;
2078 heap_free(info);
2079 *size = sizeof(*ts);
2080 return ERROR_SUCCESS;
2082 return error;
2085 case INTERNET_OPTION_DATAFILE_NAME: {
2086 DWORD req_size;
2088 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2090 if(!req->cacheFile) {
2091 *size = 0;
2092 return ERROR_INTERNET_ITEM_NOT_FOUND;
2095 if(unicode) {
2096 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2097 if(*size < req_size)
2098 return ERROR_INSUFFICIENT_BUFFER;
2100 *size = req_size;
2101 memcpy(buffer, req->cacheFile, *size);
2102 return ERROR_SUCCESS;
2103 }else {
2104 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2105 if (req_size > *size)
2106 return ERROR_INSUFFICIENT_BUFFER;
2108 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2109 -1, buffer, *size, NULL, NULL);
2110 return ERROR_SUCCESS;
2114 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2115 PCCERT_CONTEXT context;
2117 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2118 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2119 return ERROR_INSUFFICIENT_BUFFER;
2122 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2123 if(context) {
2124 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2125 DWORD len;
2127 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2128 info->ftExpiry = context->pCertInfo->NotAfter;
2129 info->ftStart = context->pCertInfo->NotBefore;
2130 len = CertNameToStrA(context->dwCertEncodingType,
2131 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2132 info->lpszSubjectInfo = LocalAlloc(0, len);
2133 if(info->lpszSubjectInfo)
2134 CertNameToStrA(context->dwCertEncodingType,
2135 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2136 info->lpszSubjectInfo, len);
2137 len = CertNameToStrA(context->dwCertEncodingType,
2138 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2139 info->lpszIssuerInfo = LocalAlloc(0, len);
2140 if(info->lpszIssuerInfo)
2141 CertNameToStrA(context->dwCertEncodingType,
2142 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2143 info->lpszIssuerInfo, len);
2144 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2145 CertFreeCertificateContext(context);
2146 return ERROR_SUCCESS;
2151 return INET_QueryOption(hdr, option, buffer, size, unicode);
2154 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2156 http_request_t *req = (http_request_t*)hdr;
2158 switch(option) {
2159 case INTERNET_OPTION_SECURITY_FLAGS:
2161 DWORD flags;
2163 if (!buffer || size != sizeof(DWORD))
2164 return ERROR_INVALID_PARAMETER;
2165 flags = *(DWORD *)buffer;
2166 TRACE("%08x\n", flags);
2167 req->security_flags = flags;
2168 if(req->netconn)
2169 req->netconn->security_flags = flags;
2170 return ERROR_SUCCESS;
2172 case INTERNET_OPTION_SEND_TIMEOUT:
2173 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2174 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2176 if (size != sizeof(DWORD))
2177 return ERROR_INVALID_PARAMETER;
2179 if(!req->netconn) {
2180 FIXME("unsupported without active connection\n");
2181 return ERROR_SUCCESS;
2184 return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
2185 *(DWORD*)buffer);
2187 case INTERNET_OPTION_USERNAME:
2188 heap_free(req->session->userName);
2189 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2190 return ERROR_SUCCESS;
2192 case INTERNET_OPTION_PASSWORD:
2193 heap_free(req->session->password);
2194 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2195 return ERROR_SUCCESS;
2196 case INTERNET_OPTION_HTTP_DECODING:
2197 if(size != sizeof(BOOL))
2198 return ERROR_INVALID_PARAMETER;
2199 req->decoding = *(BOOL*)buffer;
2200 return ERROR_SUCCESS;
2203 return ERROR_INTERNET_INVALID_OPTION;
2206 /* read some more data into the read buffer (the read section must be held) */
2207 static DWORD read_more_data( http_request_t *req, int maxlen )
2209 DWORD res;
2210 int len;
2212 if (req->read_pos)
2214 /* move existing data to the start of the buffer */
2215 if(req->read_size)
2216 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2217 req->read_pos = 0;
2220 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2222 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2223 maxlen - req->read_size, 0, &len );
2224 if(res == ERROR_SUCCESS)
2225 req->read_size += len;
2227 return res;
2230 /* remove some amount of data from the read buffer (the read section must be held) */
2231 static void remove_data( http_request_t *req, int count )
2233 if (!(req->read_size -= count)) req->read_pos = 0;
2234 else req->read_pos += count;
2237 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2239 int count, bytes_read, pos = 0;
2240 DWORD res;
2242 EnterCriticalSection( &req->read_section );
2243 for (;;)
2245 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2247 if (eol)
2249 count = eol - (req->read_buf + req->read_pos);
2250 bytes_read = count + 1;
2252 else count = bytes_read = req->read_size;
2254 count = min( count, *len - pos );
2255 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2256 pos += count;
2257 remove_data( req, bytes_read );
2258 if (eol) break;
2260 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2262 *len = 0;
2263 TRACE( "returning empty string %u\n", res);
2264 LeaveCriticalSection( &req->read_section );
2265 INTERNET_SetLastError(res);
2266 return FALSE;
2269 LeaveCriticalSection( &req->read_section );
2271 if (pos < *len)
2273 if (pos && buffer[pos - 1] == '\r') pos--;
2274 *len = pos + 1;
2276 buffer[*len - 1] = 0;
2277 TRACE( "returning %s\n", debugstr_a(buffer));
2278 return TRUE;
2281 /* check if we have reached the end of the data to read (the read section must be held) */
2282 static BOOL end_of_read_data( http_request_t *req )
2284 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2287 /* fetch some more data into the read buffer (the read section must be held) */
2288 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2290 DWORD res, read=0;
2292 if(req->read_size == sizeof(req->read_buf))
2293 return ERROR_SUCCESS;
2295 if(req->read_pos) {
2296 if(req->read_size)
2297 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2298 req->read_pos = 0;
2301 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2302 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2303 req->read_size += read;
2305 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2306 if(read_bytes)
2307 *read_bytes = read;
2308 return res;
2311 /* return the size of data available to be read immediately (the read section must be held) */
2312 static DWORD get_avail_data( http_request_t *req )
2314 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2317 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2319 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2320 DWORD avail = 0;
2322 if(req->netconn)
2323 NETCON_query_data_available(req->netconn, &avail);
2324 return netconn_stream->content_length == ~0u
2325 ? avail
2326 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2329 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2331 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2332 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2335 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2336 DWORD *read, read_mode_t read_mode)
2338 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2339 int len = 0;
2341 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2343 if(read_mode == READMODE_NOBLOCK)
2344 size = min(size, netconn_get_avail_data(stream, req));
2346 if(size && req->netconn) {
2347 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2348 len = 0;
2349 if(!len)
2350 netconn_stream->content_length = netconn_stream->content_read;
2353 netconn_stream->content_read += *read = len;
2354 TRACE("read %u bytes\n", len);
2355 return ERROR_SUCCESS;
2358 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2360 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2361 BYTE buf[1024];
2362 DWORD avail;
2363 int len;
2365 if(netconn_end_of_data(stream, req))
2366 return TRUE;
2368 do {
2369 avail = netconn_get_avail_data(stream, req);
2370 if(!avail)
2371 return FALSE;
2373 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2374 return FALSE;
2376 netconn_stream->content_read += len;
2377 }while(netconn_stream->content_read < netconn_stream->content_length);
2379 return TRUE;
2382 static void netconn_destroy(data_stream_t *stream)
2386 static const data_stream_vtbl_t netconn_stream_vtbl = {
2387 netconn_get_avail_data,
2388 netconn_end_of_data,
2389 netconn_read,
2390 netconn_drain_content,
2391 netconn_destroy
2394 /* read some more data into the read buffer (the read section must be held) */
2395 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2397 DWORD res;
2398 int len;
2400 if (stream->buf_pos)
2402 /* move existing data to the start of the buffer */
2403 if(stream->buf_size)
2404 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2405 stream->buf_pos = 0;
2408 if (maxlen == -1) maxlen = sizeof(stream->buf);
2410 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2411 maxlen - stream->buf_size, 0, &len );
2412 if(res == ERROR_SUCCESS)
2413 stream->buf_size += len;
2415 return res;
2418 /* remove some amount of data from the read buffer (the read section must be held) */
2419 static void remove_chunked_data(chunked_stream_t *stream, int count)
2421 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2422 else stream->buf_pos += count;
2425 /* discard data contents until we reach end of line (the read section must be held) */
2426 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2428 DWORD res;
2432 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2433 if (eol)
2435 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2436 break;
2438 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2439 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2440 } while (stream->buf_size);
2441 return ERROR_SUCCESS;
2444 /* read the size of the next chunk (the read section must be held) */
2445 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2447 /* TODOO */
2448 DWORD chunk_size = 0, res;
2450 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2451 return res;
2453 for (;;)
2455 while (stream->buf_size)
2457 char ch = stream->buf[stream->buf_pos];
2458 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2459 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2460 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2461 else if (ch == ';' || ch == '\r' || ch == '\n')
2463 TRACE( "reading %u byte chunk\n", chunk_size );
2464 stream->chunk_size = chunk_size;
2465 req->contentLength += chunk_size;
2466 return discard_chunked_eol(stream, req);
2468 remove_chunked_data(stream, 1);
2470 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2471 if (!stream->buf_size)
2473 stream->chunk_size = 0;
2474 return ERROR_SUCCESS;
2479 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2481 /* Allow reading only from read buffer */
2482 return 0;
2485 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2487 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2488 return !chunked_stream->chunk_size;
2491 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2492 DWORD *read, read_mode_t read_mode)
2494 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2495 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2497 if(chunked_stream->chunk_size == ~0u) {
2498 res = start_next_chunk(chunked_stream, req);
2499 if(res != ERROR_SUCCESS)
2500 return res;
2503 while(size && chunked_stream->chunk_size) {
2504 if(chunked_stream->buf_size) {
2505 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2507 /* this could block */
2508 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2509 break;
2511 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2512 remove_chunked_data(chunked_stream, read_bytes);
2513 }else {
2514 read_bytes = min(size, chunked_stream->chunk_size);
2516 if(read_mode == READMODE_NOBLOCK) {
2517 DWORD avail;
2519 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2520 break;
2521 if(read_bytes > avail)
2522 read_bytes = avail;
2524 /* this could block */
2525 if(read_bytes == chunked_stream->chunk_size)
2526 break;
2529 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2530 if(res != ERROR_SUCCESS)
2531 break;
2534 chunked_stream->chunk_size -= read_bytes;
2535 size -= read_bytes;
2536 ret_read += read_bytes;
2537 if(!chunked_stream->chunk_size) {
2538 assert(read_mode != READMODE_NOBLOCK);
2539 res = start_next_chunk(chunked_stream, req);
2540 if(res != ERROR_SUCCESS)
2541 break;
2544 if(read_mode == READMODE_ASYNC)
2545 read_mode = READMODE_NOBLOCK;
2548 TRACE("read %u bytes\n", ret_read);
2549 *read = ret_read;
2550 return res;
2553 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2555 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2557 /* FIXME: we can do better */
2558 return !chunked_stream->chunk_size;
2561 static void chunked_destroy(data_stream_t *stream)
2563 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2564 heap_free(chunked_stream);
2567 static const data_stream_vtbl_t chunked_stream_vtbl = {
2568 chunked_get_avail_data,
2569 chunked_end_of_data,
2570 chunked_read,
2571 chunked_drain_content,
2572 chunked_destroy
2575 /* set the request content length based on the headers */
2576 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2578 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2579 WCHAR encoding[20];
2580 DWORD size;
2582 if(status_code == HTTP_STATUS_NO_CONTENT) {
2583 request->contentLength = request->netconn_stream.content_length = 0;
2584 return ERROR_SUCCESS;
2587 size = sizeof(request->contentLength);
2588 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2589 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2590 request->contentLength = ~0u;
2591 request->netconn_stream.content_length = request->contentLength;
2592 request->netconn_stream.content_read = request->read_size;
2594 size = sizeof(encoding);
2595 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2596 !strcmpiW(encoding, szChunked))
2598 chunked_stream_t *chunked_stream;
2600 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2601 if(!chunked_stream)
2602 return ERROR_OUTOFMEMORY;
2604 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2605 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2606 chunked_stream->chunk_size = ~0u;
2608 if(request->read_size) {
2609 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2610 chunked_stream->buf_size = request->read_size;
2611 request->read_size = request->read_pos = 0;
2614 request->data_stream = &chunked_stream->data_stream;
2615 request->contentLength = ~0u;
2616 request->read_chunked = TRUE;
2619 if(request->decoding) {
2620 int encoding_idx;
2622 static const WCHAR gzipW[] = {'g','z','i','p',0};
2624 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2625 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2626 return init_gzip_stream(request);
2629 return ERROR_SUCCESS;
2632 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2634 INTERNET_ASYNC_RESULT iar;
2636 iar.dwResult = result;
2637 iar.dwError = error;
2639 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2640 sizeof(INTERNET_ASYNC_RESULT));
2643 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2645 DWORD res, read = 0, avail = 0;
2646 read_mode_t mode;
2648 TRACE("%p\n", req);
2650 EnterCriticalSection( &req->read_section );
2652 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2653 res = refill_read_buffer(req, mode, &read);
2654 if(res == ERROR_SUCCESS && !first_notif)
2655 avail = get_avail_data(req);
2657 LeaveCriticalSection( &req->read_section );
2659 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2660 WARN("res %u read %u, closing connection\n", res, read);
2661 http_release_netconn(req, FALSE);
2664 if(res == ERROR_SUCCESS)
2665 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2666 else
2667 send_request_complete(req, 0, res);
2670 /* read data from the http connection (the read section must be held) */
2671 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2673 DWORD current_read = 0, ret_read = 0;
2674 read_mode_t read_mode;
2675 DWORD res = ERROR_SUCCESS;
2677 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2679 EnterCriticalSection( &req->read_section );
2681 if(req->read_size) {
2682 ret_read = min(size, req->read_size);
2683 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2684 req->read_size -= ret_read;
2685 req->read_pos += ret_read;
2686 if(read_mode == READMODE_ASYNC)
2687 read_mode = READMODE_NOBLOCK;
2690 if(ret_read < size) {
2691 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2692 ret_read += current_read;
2695 LeaveCriticalSection( &req->read_section );
2697 *read = ret_read;
2698 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2700 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2701 BOOL res;
2702 DWORD written;
2704 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2705 if(!res)
2706 WARN("WriteFile failed: %u\n", GetLastError());
2709 if(size && !ret_read)
2710 http_release_netconn(req, res == ERROR_SUCCESS);
2712 return res;
2716 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2718 http_request_t *req = (http_request_t*)hdr;
2719 DWORD res;
2721 EnterCriticalSection( &req->read_section );
2722 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2723 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2725 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2726 if(res == ERROR_SUCCESS)
2727 res = hdr->dwError;
2728 LeaveCriticalSection( &req->read_section );
2730 return res;
2733 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2735 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2736 http_request_t *req = (http_request_t*)workRequest->hdr;
2737 DWORD res;
2739 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2741 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2742 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2744 send_request_complete(req, res == ERROR_SUCCESS, res);
2747 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2748 DWORD flags, DWORD_PTR context)
2750 http_request_t *req = (http_request_t*)hdr;
2751 DWORD res, size, read, error = ERROR_SUCCESS;
2753 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2754 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2756 if (buffers->dwStructSize != sizeof(*buffers))
2757 return ERROR_INVALID_PARAMETER;
2759 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2761 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2763 WORKREQUEST workRequest;
2765 if (TryEnterCriticalSection( &req->read_section ))
2767 if (get_avail_data(req))
2769 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2770 &buffers->dwBufferLength, FALSE);
2771 size = buffers->dwBufferLength;
2772 LeaveCriticalSection( &req->read_section );
2773 goto done;
2775 LeaveCriticalSection( &req->read_section );
2778 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2779 workRequest.hdr = WININET_AddRef(&req->hdr);
2780 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2782 INTERNET_AsyncCall(&workRequest);
2784 return ERROR_IO_PENDING;
2787 read = 0;
2788 size = buffers->dwBufferLength;
2790 EnterCriticalSection( &req->read_section );
2791 if(hdr->dwError == ERROR_SUCCESS)
2792 hdr->dwError = INTERNET_HANDLE_IN_USE;
2793 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2794 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2796 while(1) {
2797 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2798 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2799 if(res != ERROR_SUCCESS)
2800 break;
2802 read += buffers->dwBufferLength;
2803 if(read == size || end_of_read_data(req))
2804 break;
2806 LeaveCriticalSection( &req->read_section );
2808 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2809 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2810 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2811 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2813 EnterCriticalSection( &req->read_section );
2816 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2817 hdr->dwError = ERROR_SUCCESS;
2818 else
2819 error = hdr->dwError;
2821 LeaveCriticalSection( &req->read_section );
2822 size = buffers->dwBufferLength;
2823 buffers->dwBufferLength = read;
2825 done:
2826 if (res == ERROR_SUCCESS) {
2827 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2828 &size, sizeof(size));
2831 return res==ERROR_SUCCESS ? error : res;
2834 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2836 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2837 http_request_t *req = (http_request_t*)workRequest->hdr;
2838 DWORD res;
2840 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2842 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2843 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2845 send_request_complete(req, res == ERROR_SUCCESS, res);
2848 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2849 DWORD flags, DWORD_PTR context)
2852 http_request_t *req = (http_request_t*)hdr;
2853 DWORD res, size, read, error = ERROR_SUCCESS;
2855 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2856 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2858 if (buffers->dwStructSize != sizeof(*buffers))
2859 return ERROR_INVALID_PARAMETER;
2861 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2863 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2865 WORKREQUEST workRequest;
2867 if (TryEnterCriticalSection( &req->read_section ))
2869 if (get_avail_data(req))
2871 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2872 &buffers->dwBufferLength, FALSE);
2873 size = buffers->dwBufferLength;
2874 LeaveCriticalSection( &req->read_section );
2875 goto done;
2877 LeaveCriticalSection( &req->read_section );
2880 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2881 workRequest.hdr = WININET_AddRef(&req->hdr);
2882 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2884 INTERNET_AsyncCall(&workRequest);
2886 return ERROR_IO_PENDING;
2889 read = 0;
2890 size = buffers->dwBufferLength;
2892 EnterCriticalSection( &req->read_section );
2893 if(hdr->dwError == ERROR_SUCCESS)
2894 hdr->dwError = INTERNET_HANDLE_IN_USE;
2895 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2896 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2898 while(1) {
2899 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2900 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2901 if(res != ERROR_SUCCESS)
2902 break;
2904 read += buffers->dwBufferLength;
2905 if(read == size || end_of_read_data(req))
2906 break;
2908 LeaveCriticalSection( &req->read_section );
2910 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2911 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2912 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2913 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2915 EnterCriticalSection( &req->read_section );
2918 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2919 hdr->dwError = ERROR_SUCCESS;
2920 else
2921 error = hdr->dwError;
2923 LeaveCriticalSection( &req->read_section );
2924 size = buffers->dwBufferLength;
2925 buffers->dwBufferLength = read;
2927 done:
2928 if (res == ERROR_SUCCESS) {
2929 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2930 &size, sizeof(size));
2933 return res==ERROR_SUCCESS ? error : res;
2936 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2938 DWORD res;
2939 http_request_t *request = (http_request_t*)hdr;
2941 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2943 *written = 0;
2944 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2945 if (res == ERROR_SUCCESS)
2946 request->bytesWritten += *written;
2948 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2949 return res;
2952 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2954 http_request_t *req = (http_request_t*)workRequest->hdr;
2956 HTTP_ReceiveRequestData(req, FALSE);
2959 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2961 http_request_t *req = (http_request_t*)hdr;
2963 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2965 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2967 WORKREQUEST workRequest;
2969 /* never wait, if we can't enter the section we queue an async request right away */
2970 if (TryEnterCriticalSection( &req->read_section ))
2972 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2973 if ((*available = get_avail_data( req ))) goto done;
2974 if (end_of_read_data( req )) goto done;
2975 LeaveCriticalSection( &req->read_section );
2978 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2979 workRequest.hdr = WININET_AddRef( &req->hdr );
2981 INTERNET_AsyncCall(&workRequest);
2983 return ERROR_IO_PENDING;
2986 EnterCriticalSection( &req->read_section );
2988 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2990 refill_read_buffer( req, READMODE_ASYNC, NULL );
2991 *available = get_avail_data( req );
2994 done:
2995 LeaveCriticalSection( &req->read_section );
2997 TRACE( "returning %u\n", *available );
2998 return ERROR_SUCCESS;
3001 static const object_vtbl_t HTTPREQVtbl = {
3002 HTTPREQ_Destroy,
3003 HTTPREQ_CloseConnection,
3004 HTTPREQ_QueryOption,
3005 HTTPREQ_SetOption,
3006 HTTPREQ_ReadFile,
3007 HTTPREQ_ReadFileExA,
3008 HTTPREQ_ReadFileExW,
3009 HTTPREQ_WriteFile,
3010 HTTPREQ_QueryDataAvailable,
3011 NULL
3014 /***********************************************************************
3015 * HTTP_HttpOpenRequestW (internal)
3017 * Open a HTTP request handle
3019 * RETURNS
3020 * HINTERNET a HTTP request handle on success
3021 * NULL on failure
3024 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3025 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3026 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3027 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3029 appinfo_t *hIC = session->appInfo;
3030 http_request_t *request;
3031 DWORD len, res = ERROR_SUCCESS;
3033 TRACE("-->\n");
3035 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3036 if(!request)
3037 return ERROR_OUTOFMEMORY;
3039 request->hdr.htype = WH_HHTTPREQ;
3040 request->hdr.dwFlags = dwFlags;
3041 request->hdr.dwContext = dwContext;
3042 request->contentLength = ~0u;
3044 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3045 request->data_stream = &request->netconn_stream.data_stream;
3047 InitializeCriticalSection( &request->read_section );
3049 WININET_AddRef( &session->hdr );
3050 request->session = session;
3051 list_add_head( &session->hdr.children, &request->hdr.entry );
3053 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3054 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3055 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3056 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3058 if (lpszObjectName && *lpszObjectName) {
3059 HRESULT rc;
3061 len = 0;
3062 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3063 if (rc != E_POINTER)
3064 len = strlenW(lpszObjectName)+1;
3065 request->path = heap_alloc(len*sizeof(WCHAR));
3066 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3067 URL_ESCAPE_SPACES_ONLY);
3068 if (rc != S_OK)
3070 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3071 strcpyW(request->path,lpszObjectName);
3073 }else {
3074 static const WCHAR slashW[] = {'/',0};
3076 request->path = heap_strdupW(slashW);
3079 if (lpszReferrer && *lpszReferrer)
3080 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3082 if (lpszAcceptTypes)
3084 int i;
3085 for (i = 0; lpszAcceptTypes[i]; i++)
3087 if (!*lpszAcceptTypes[i]) continue;
3088 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3089 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3090 HTTP_ADDHDR_FLAG_REQ |
3091 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3095 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3096 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3098 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3099 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3100 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3102 WCHAR *host_name;
3104 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3106 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3107 if (!host_name) {
3108 res = ERROR_OUTOFMEMORY;
3109 goto lend;
3112 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3113 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3114 heap_free(host_name);
3116 else
3117 HTTP_ProcessHeader(request, hostW, session->hostName,
3118 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3120 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3121 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3122 INTERNET_DEFAULT_HTTPS_PORT :
3123 INTERNET_DEFAULT_HTTP_PORT);
3125 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3126 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3127 INTERNET_DEFAULT_HTTPS_PORT :
3128 INTERNET_DEFAULT_HTTP_PORT);
3130 if (hIC->proxy && hIC->proxy[0])
3131 HTTP_DealWithProxy( hIC, session, request );
3133 INTERNET_SendCallback(&session->hdr, dwContext,
3134 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3135 sizeof(HINTERNET));
3137 lend:
3138 TRACE("<-- %u (%p)\n", res, request);
3140 if(res != ERROR_SUCCESS) {
3141 WININET_Release( &request->hdr );
3142 *ret = NULL;
3143 return res;
3146 *ret = request->hdr.hInternet;
3147 return ERROR_SUCCESS;
3150 /***********************************************************************
3151 * HttpOpenRequestW (WININET.@)
3153 * Open a HTTP request handle
3155 * RETURNS
3156 * HINTERNET a HTTP request handle on success
3157 * NULL on failure
3160 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3161 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3162 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3163 DWORD dwFlags, DWORD_PTR dwContext)
3165 http_session_t *session;
3166 HINTERNET handle = NULL;
3167 DWORD res;
3169 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3170 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3171 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3172 dwFlags, dwContext);
3173 if(lpszAcceptTypes!=NULL)
3175 int i;
3176 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3177 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3180 session = (http_session_t*) get_handle_object( hHttpSession );
3181 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3183 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3184 goto lend;
3188 * My tests seem to show that the windows version does not
3189 * become asynchronous until after this point. And anyhow
3190 * if this call was asynchronous then how would you get the
3191 * necessary HINTERNET pointer returned by this function.
3194 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3195 lpszVersion, lpszReferrer, lpszAcceptTypes,
3196 dwFlags, dwContext, &handle);
3197 lend:
3198 if( session )
3199 WININET_Release( &session->hdr );
3200 TRACE("returning %p\n", handle);
3201 if(res != ERROR_SUCCESS)
3202 SetLastError(res);
3203 return handle;
3206 static const LPCWSTR header_lookup[] = {
3207 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3208 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3209 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3210 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3211 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3212 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3213 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3214 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3215 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3216 szDate, /* HTTP_QUERY_DATE = 9 */
3217 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3218 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3219 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3220 szURI, /* HTTP_QUERY_URI = 13 */
3221 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3222 NULL, /* HTTP_QUERY_COST = 15 */
3223 NULL, /* HTTP_QUERY_LINK = 16 */
3224 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3225 NULL, /* HTTP_QUERY_VERSION = 18 */
3226 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3227 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3228 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3229 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3230 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3231 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3232 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3233 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3234 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3235 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3236 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3237 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3238 NULL, /* HTTP_QUERY_FROM = 31 */
3239 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3240 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3241 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3242 szReferer, /* HTTP_QUERY_REFERER = 35 */
3243 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3244 szServer, /* HTTP_QUERY_SERVER = 37 */
3245 NULL, /* HTTP_TITLE = 38 */
3246 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3247 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3248 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3249 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3250 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3251 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3252 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3253 NULL, /* HTTP_QUERY_REFRESH = 46 */
3254 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3255 szAge, /* HTTP_QUERY_AGE = 48 */
3256 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3257 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3258 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3259 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3260 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3261 szETag, /* HTTP_QUERY_ETAG = 54 */
3262 hostW, /* HTTP_QUERY_HOST = 55 */
3263 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3264 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3265 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3266 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3267 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3268 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3269 szRange, /* HTTP_QUERY_RANGE = 62 */
3270 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3271 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3272 szVary, /* HTTP_QUERY_VARY = 65 */
3273 szVia, /* HTTP_QUERY_VIA = 66 */
3274 szWarning, /* HTTP_QUERY_WARNING = 67 */
3275 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3276 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3277 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3280 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3282 /***********************************************************************
3283 * HTTP_HttpQueryInfoW (internal)
3285 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3286 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3288 LPHTTPHEADERW lphttpHdr = NULL;
3289 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3290 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3291 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3292 INT index = -1;
3294 /* Find requested header structure */
3295 switch (level)
3297 case HTTP_QUERY_CUSTOM:
3298 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3299 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3300 break;
3301 case HTTP_QUERY_RAW_HEADERS_CRLF:
3303 LPWSTR headers;
3304 DWORD len = 0;
3305 DWORD res = ERROR_INVALID_PARAMETER;
3307 if (request_only)
3308 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3309 else
3310 headers = request->rawHeaders;
3312 if (headers)
3313 len = strlenW(headers) * sizeof(WCHAR);
3315 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3317 len += sizeof(WCHAR);
3318 res = ERROR_INSUFFICIENT_BUFFER;
3320 else if (lpBuffer)
3322 if (headers)
3323 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3324 else
3326 len = strlenW(szCrLf) * sizeof(WCHAR);
3327 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3329 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3330 res = ERROR_SUCCESS;
3332 *lpdwBufferLength = len;
3334 if (request_only) heap_free(headers);
3335 return res;
3337 case HTTP_QUERY_RAW_HEADERS:
3339 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3340 DWORD i, size = 0;
3341 LPWSTR pszString = lpBuffer;
3343 for (i = 0; ppszRawHeaderLines[i]; i++)
3344 size += strlenW(ppszRawHeaderLines[i]) + 1;
3346 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3348 HTTP_FreeTokens(ppszRawHeaderLines);
3349 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3350 return ERROR_INSUFFICIENT_BUFFER;
3352 if (pszString)
3354 for (i = 0; ppszRawHeaderLines[i]; i++)
3356 DWORD len = strlenW(ppszRawHeaderLines[i]);
3357 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3358 pszString += len+1;
3360 *pszString = '\0';
3361 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3363 *lpdwBufferLength = size * sizeof(WCHAR);
3364 HTTP_FreeTokens(ppszRawHeaderLines);
3366 return ERROR_SUCCESS;
3368 case HTTP_QUERY_STATUS_TEXT:
3369 if (request->statusText)
3371 DWORD len = strlenW(request->statusText);
3372 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3374 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3375 return ERROR_INSUFFICIENT_BUFFER;
3377 if (lpBuffer)
3379 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3380 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3382 *lpdwBufferLength = len * sizeof(WCHAR);
3383 return ERROR_SUCCESS;
3385 break;
3386 case HTTP_QUERY_VERSION:
3387 if (request->version)
3389 DWORD len = strlenW(request->version);
3390 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3392 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3393 return ERROR_INSUFFICIENT_BUFFER;
3395 if (lpBuffer)
3397 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3398 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3400 *lpdwBufferLength = len * sizeof(WCHAR);
3401 return ERROR_SUCCESS;
3403 break;
3404 case HTTP_QUERY_CONTENT_ENCODING:
3405 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3406 requested_index,request_only);
3407 break;
3408 default:
3409 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3411 if (level < LAST_TABLE_HEADER && header_lookup[level])
3412 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3413 requested_index,request_only);
3416 if (index >= 0)
3417 lphttpHdr = &request->custHeaders[index];
3419 /* Ensure header satisfies requested attributes */
3420 if (!lphttpHdr ||
3421 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3422 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3424 return ERROR_HTTP_HEADER_NOT_FOUND;
3427 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3429 /* coalesce value to requested type */
3430 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3432 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3433 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3435 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3437 time_t tmpTime;
3438 struct tm tmpTM;
3439 SYSTEMTIME *STHook;
3441 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3443 tmpTM = *gmtime(&tmpTime);
3444 STHook = (SYSTEMTIME *)lpBuffer;
3445 STHook->wDay = tmpTM.tm_mday;
3446 STHook->wHour = tmpTM.tm_hour;
3447 STHook->wMilliseconds = 0;
3448 STHook->wMinute = tmpTM.tm_min;
3449 STHook->wDayOfWeek = tmpTM.tm_wday;
3450 STHook->wMonth = tmpTM.tm_mon + 1;
3451 STHook->wSecond = tmpTM.tm_sec;
3452 STHook->wYear = tmpTM.tm_year;
3454 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3455 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3456 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3458 else if (lphttpHdr->lpszValue)
3460 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3462 if (len > *lpdwBufferLength)
3464 *lpdwBufferLength = len;
3465 return ERROR_INSUFFICIENT_BUFFER;
3467 if (lpBuffer)
3469 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3470 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3472 *lpdwBufferLength = len - sizeof(WCHAR);
3474 return ERROR_SUCCESS;
3477 /***********************************************************************
3478 * HttpQueryInfoW (WININET.@)
3480 * Queries for information about an HTTP request
3482 * RETURNS
3483 * TRUE on success
3484 * FALSE on failure
3487 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3488 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3490 http_request_t *request;
3491 DWORD res;
3493 if (TRACE_ON(wininet)) {
3494 #define FE(x) { x, #x }
3495 static const wininet_flag_info query_flags[] = {
3496 FE(HTTP_QUERY_MIME_VERSION),
3497 FE(HTTP_QUERY_CONTENT_TYPE),
3498 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3499 FE(HTTP_QUERY_CONTENT_ID),
3500 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3501 FE(HTTP_QUERY_CONTENT_LENGTH),
3502 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3503 FE(HTTP_QUERY_ALLOW),
3504 FE(HTTP_QUERY_PUBLIC),
3505 FE(HTTP_QUERY_DATE),
3506 FE(HTTP_QUERY_EXPIRES),
3507 FE(HTTP_QUERY_LAST_MODIFIED),
3508 FE(HTTP_QUERY_MESSAGE_ID),
3509 FE(HTTP_QUERY_URI),
3510 FE(HTTP_QUERY_DERIVED_FROM),
3511 FE(HTTP_QUERY_COST),
3512 FE(HTTP_QUERY_LINK),
3513 FE(HTTP_QUERY_PRAGMA),
3514 FE(HTTP_QUERY_VERSION),
3515 FE(HTTP_QUERY_STATUS_CODE),
3516 FE(HTTP_QUERY_STATUS_TEXT),
3517 FE(HTTP_QUERY_RAW_HEADERS),
3518 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3519 FE(HTTP_QUERY_CONNECTION),
3520 FE(HTTP_QUERY_ACCEPT),
3521 FE(HTTP_QUERY_ACCEPT_CHARSET),
3522 FE(HTTP_QUERY_ACCEPT_ENCODING),
3523 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3524 FE(HTTP_QUERY_AUTHORIZATION),
3525 FE(HTTP_QUERY_CONTENT_ENCODING),
3526 FE(HTTP_QUERY_FORWARDED),
3527 FE(HTTP_QUERY_FROM),
3528 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3529 FE(HTTP_QUERY_LOCATION),
3530 FE(HTTP_QUERY_ORIG_URI),
3531 FE(HTTP_QUERY_REFERER),
3532 FE(HTTP_QUERY_RETRY_AFTER),
3533 FE(HTTP_QUERY_SERVER),
3534 FE(HTTP_QUERY_TITLE),
3535 FE(HTTP_QUERY_USER_AGENT),
3536 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3537 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3538 FE(HTTP_QUERY_ACCEPT_RANGES),
3539 FE(HTTP_QUERY_SET_COOKIE),
3540 FE(HTTP_QUERY_COOKIE),
3541 FE(HTTP_QUERY_REQUEST_METHOD),
3542 FE(HTTP_QUERY_REFRESH),
3543 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3544 FE(HTTP_QUERY_AGE),
3545 FE(HTTP_QUERY_CACHE_CONTROL),
3546 FE(HTTP_QUERY_CONTENT_BASE),
3547 FE(HTTP_QUERY_CONTENT_LOCATION),
3548 FE(HTTP_QUERY_CONTENT_MD5),
3549 FE(HTTP_QUERY_CONTENT_RANGE),
3550 FE(HTTP_QUERY_ETAG),
3551 FE(HTTP_QUERY_HOST),
3552 FE(HTTP_QUERY_IF_MATCH),
3553 FE(HTTP_QUERY_IF_NONE_MATCH),
3554 FE(HTTP_QUERY_IF_RANGE),
3555 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3556 FE(HTTP_QUERY_MAX_FORWARDS),
3557 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3558 FE(HTTP_QUERY_RANGE),
3559 FE(HTTP_QUERY_TRANSFER_ENCODING),
3560 FE(HTTP_QUERY_UPGRADE),
3561 FE(HTTP_QUERY_VARY),
3562 FE(HTTP_QUERY_VIA),
3563 FE(HTTP_QUERY_WARNING),
3564 FE(HTTP_QUERY_CUSTOM)
3566 static const wininet_flag_info modifier_flags[] = {
3567 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3568 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3569 FE(HTTP_QUERY_FLAG_NUMBER),
3570 FE(HTTP_QUERY_FLAG_COALESCE)
3572 #undef FE
3573 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3574 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3575 DWORD i;
3577 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3578 TRACE(" Attribute:");
3579 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3580 if (query_flags[i].val == info) {
3581 TRACE(" %s", query_flags[i].name);
3582 break;
3585 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3586 TRACE(" Unknown (%08x)", info);
3589 TRACE(" Modifier:");
3590 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3591 if (modifier_flags[i].val & info_mod) {
3592 TRACE(" %s", modifier_flags[i].name);
3593 info_mod &= ~ modifier_flags[i].val;
3597 if (info_mod) {
3598 TRACE(" Unknown (%08x)", info_mod);
3600 TRACE("\n");
3603 request = (http_request_t*) get_handle_object( hHttpRequest );
3604 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3606 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3607 goto lend;
3610 if (lpBuffer == NULL)
3611 *lpdwBufferLength = 0;
3612 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3613 lpBuffer, lpdwBufferLength, lpdwIndex);
3615 lend:
3616 if( request )
3617 WININET_Release( &request->hdr );
3619 TRACE("%u <--\n", res);
3620 if(res != ERROR_SUCCESS)
3621 SetLastError(res);
3622 return res == ERROR_SUCCESS;
3625 /***********************************************************************
3626 * HttpQueryInfoA (WININET.@)
3628 * Queries for information about an HTTP request
3630 * RETURNS
3631 * TRUE on success
3632 * FALSE on failure
3635 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3636 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3638 BOOL result;
3639 DWORD len;
3640 WCHAR* bufferW;
3642 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3643 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3645 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3646 lpdwBufferLength, lpdwIndex );
3649 if (lpBuffer)
3651 DWORD alloclen;
3652 len = (*lpdwBufferLength)*sizeof(WCHAR);
3653 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3655 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3656 if (alloclen < len)
3657 alloclen = len;
3659 else
3660 alloclen = len;
3661 bufferW = heap_alloc(alloclen);
3662 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3663 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3664 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3665 } else
3667 bufferW = NULL;
3668 len = 0;
3671 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3672 &len, lpdwIndex );
3673 if( result )
3675 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3676 lpBuffer, *lpdwBufferLength, NULL, NULL );
3677 *lpdwBufferLength = len - 1;
3679 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3681 else
3682 /* since the strings being returned from HttpQueryInfoW should be
3683 * only ASCII characters, it is reasonable to assume that all of
3684 * the Unicode characters can be reduced to a single byte */
3685 *lpdwBufferLength = len / sizeof(WCHAR);
3687 heap_free( bufferW );
3688 return result;
3691 /***********************************************************************
3692 * HTTP_GetRedirectURL (internal)
3694 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3696 static WCHAR szHttp[] = {'h','t','t','p',0};
3697 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3698 http_session_t *session = request->session;
3699 URL_COMPONENTSW urlComponents;
3700 DWORD url_length = 0;
3701 LPWSTR orig_url;
3702 LPWSTR combined_url;
3704 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3705 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3706 urlComponents.dwSchemeLength = 0;
3707 urlComponents.lpszHostName = session->hostName;
3708 urlComponents.dwHostNameLength = 0;
3709 urlComponents.nPort = session->hostPort;
3710 urlComponents.lpszUserName = session->userName;
3711 urlComponents.dwUserNameLength = 0;
3712 urlComponents.lpszPassword = NULL;
3713 urlComponents.dwPasswordLength = 0;
3714 urlComponents.lpszUrlPath = request->path;
3715 urlComponents.dwUrlPathLength = 0;
3716 urlComponents.lpszExtraInfo = NULL;
3717 urlComponents.dwExtraInfoLength = 0;
3719 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3720 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3721 return NULL;
3723 orig_url = heap_alloc(url_length);
3725 /* convert from bytes to characters */
3726 url_length = url_length / sizeof(WCHAR) - 1;
3727 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3729 heap_free(orig_url);
3730 return NULL;
3733 url_length = 0;
3734 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3735 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3737 heap_free(orig_url);
3738 return NULL;
3740 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3742 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3744 heap_free(orig_url);
3745 heap_free(combined_url);
3746 return NULL;
3748 heap_free(orig_url);
3749 return combined_url;
3753 /***********************************************************************
3754 * HTTP_HandleRedirect (internal)
3756 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3758 http_session_t *session = request->session;
3759 appinfo_t *hIC = session->appInfo;
3760 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3761 WCHAR path[INTERNET_MAX_URL_LENGTH];
3762 int index;
3764 if(lpszUrl[0]=='/')
3766 /* if it's an absolute path, keep the same session info */
3767 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3769 else
3771 URL_COMPONENTSW urlComponents;
3772 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3773 static WCHAR szHttp[] = {'h','t','t','p',0};
3774 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3776 userName[0] = 0;
3777 hostName[0] = 0;
3778 protocol[0] = 0;
3780 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3781 urlComponents.lpszScheme = protocol;
3782 urlComponents.dwSchemeLength = 32;
3783 urlComponents.lpszHostName = hostName;
3784 urlComponents.dwHostNameLength = MAXHOSTNAME;
3785 urlComponents.lpszUserName = userName;
3786 urlComponents.dwUserNameLength = 1024;
3787 urlComponents.lpszPassword = NULL;
3788 urlComponents.dwPasswordLength = 0;
3789 urlComponents.lpszUrlPath = path;
3790 urlComponents.dwUrlPathLength = 2048;
3791 urlComponents.lpszExtraInfo = NULL;
3792 urlComponents.dwExtraInfoLength = 0;
3793 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3794 return INTERNET_GetLastError();
3796 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3797 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3799 TRACE("redirect from secure page to non-secure page\n");
3800 /* FIXME: warn about from secure redirect to non-secure page */
3801 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3803 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3804 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3806 TRACE("redirect from non-secure page to secure page\n");
3807 /* FIXME: notify about redirect to secure page */
3808 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3811 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3813 if (lstrlenW(protocol)>4) /*https*/
3814 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3815 else /*http*/
3816 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3819 #if 0
3821 * This upsets redirects to binary files on sourceforge.net
3822 * and gives an html page instead of the target file
3823 * Examination of the HTTP request sent by native wininet.dll
3824 * reveals that it doesn't send a referrer in that case.
3825 * Maybe there's a flag that enables this, or maybe a referrer
3826 * shouldn't be added in case of a redirect.
3829 /* consider the current host as the referrer */
3830 if (session->lpszServerName && *session->lpszServerName)
3831 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3832 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3833 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3834 #endif
3836 heap_free(session->hostName);
3837 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3838 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3840 int len;
3841 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3842 len = lstrlenW(hostName);
3843 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3844 session->hostName = heap_alloc(len*sizeof(WCHAR));
3845 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3847 else
3848 session->hostName = heap_strdupW(hostName);
3850 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3852 heap_free(session->userName);
3853 session->userName = NULL;
3854 if (userName[0])
3855 session->userName = heap_strdupW(userName);
3857 reset_data_stream(request);
3859 if(!using_proxy) {
3860 if(strcmpiW(session->serverName, hostName)) {
3861 heap_free(session->serverName);
3862 session->serverName = heap_strdupW(hostName);
3864 session->serverPort = urlComponents.nPort;
3867 heap_free(request->path);
3868 request->path=NULL;
3869 if (*path)
3871 DWORD needed = 0;
3872 HRESULT rc;
3874 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3875 if (rc != E_POINTER)
3876 needed = strlenW(path)+1;
3877 request->path = heap_alloc(needed*sizeof(WCHAR));
3878 rc = UrlEscapeW(path, request->path, &needed,
3879 URL_ESCAPE_SPACES_ONLY);
3880 if (rc != S_OK)
3882 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3883 strcpyW(request->path,path);
3887 /* Remove custom content-type/length headers on redirects. */
3888 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3889 if (0 <= index)
3890 HTTP_DeleteCustomHeader(request, index);
3891 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3892 if (0 <= index)
3893 HTTP_DeleteCustomHeader(request, index);
3895 return ERROR_SUCCESS;
3898 /***********************************************************************
3899 * HTTP_build_req (internal)
3901 * concatenate all the strings in the request together
3903 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3905 LPCWSTR *t;
3906 LPWSTR str;
3908 for( t = list; *t ; t++ )
3909 len += strlenW( *t );
3910 len++;
3912 str = heap_alloc(len*sizeof(WCHAR));
3913 *str = 0;
3915 for( t = list; *t ; t++ )
3916 strcatW( str, *t );
3918 return str;
3921 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3923 LPWSTR lpszPath;
3924 LPWSTR requestString;
3925 INT len;
3926 INT cnt;
3927 INT responseLen;
3928 char *ascii_req;
3929 DWORD res;
3930 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3931 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3932 http_session_t *session = request->session;
3934 TRACE("\n");
3936 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3937 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3938 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3939 heap_free( lpszPath );
3941 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3942 NULL, 0, NULL, NULL );
3943 len--; /* the nul terminator isn't needed */
3944 ascii_req = heap_alloc(len);
3945 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3946 heap_free( requestString );
3948 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3950 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3951 heap_free( ascii_req );
3952 if (res != ERROR_SUCCESS)
3953 return res;
3955 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3956 if (!responseLen)
3957 return ERROR_HTTP_INVALID_HEADER;
3959 return ERROR_SUCCESS;
3962 static void HTTP_InsertCookies(http_request_t *request)
3964 DWORD cookie_size, size, cnt = 0;
3965 HTTPHEADERW *host;
3966 WCHAR *cookies;
3968 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3970 host = HTTP_GetHeader(request, hostW);
3971 if(!host)
3972 return;
3974 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3975 return;
3977 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
3978 if(!(cookies = heap_alloc(size)))
3979 return;
3981 cnt += sprintfW(cookies, cookieW);
3982 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
3983 strcatW(cookies, szCrLf);
3985 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
3987 heap_free(cookies);
3990 static WORD HTTP_ParseDay(LPCWSTR day)
3992 static const WCHAR days[7][4] = {{ 's','u','n',0 },
3993 { 'm','o','n',0 },
3994 { 't','u','e',0 },
3995 { 'w','e','d',0 },
3996 { 't','h','u',0 },
3997 { 'f','r','i',0 },
3998 { 's','a','t',0 }};
3999 int i;
4000 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4001 if (!strcmpiW(day, days[i]))
4002 return i;
4004 /* Invalid */
4005 return 7;
4008 static WORD HTTP_ParseMonth(LPCWSTR month)
4010 static const WCHAR jan[] = { 'j','a','n',0 };
4011 static const WCHAR feb[] = { 'f','e','b',0 };
4012 static const WCHAR mar[] = { 'm','a','r',0 };
4013 static const WCHAR apr[] = { 'a','p','r',0 };
4014 static const WCHAR may[] = { 'm','a','y',0 };
4015 static const WCHAR jun[] = { 'j','u','n',0 };
4016 static const WCHAR jul[] = { 'j','u','l',0 };
4017 static const WCHAR aug[] = { 'a','u','g',0 };
4018 static const WCHAR sep[] = { 's','e','p',0 };
4019 static const WCHAR oct[] = { 'o','c','t',0 };
4020 static const WCHAR nov[] = { 'n','o','v',0 };
4021 static const WCHAR dec[] = { 'd','e','c',0 };
4023 if (!strcmpiW(month, jan)) return 1;
4024 if (!strcmpiW(month, feb)) return 2;
4025 if (!strcmpiW(month, mar)) return 3;
4026 if (!strcmpiW(month, apr)) return 4;
4027 if (!strcmpiW(month, may)) return 5;
4028 if (!strcmpiW(month, jun)) return 6;
4029 if (!strcmpiW(month, jul)) return 7;
4030 if (!strcmpiW(month, aug)) return 8;
4031 if (!strcmpiW(month, sep)) return 9;
4032 if (!strcmpiW(month, oct)) return 10;
4033 if (!strcmpiW(month, nov)) return 11;
4034 if (!strcmpiW(month, dec)) return 12;
4035 /* Invalid */
4036 return 0;
4039 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4040 * optionally preceded by whitespace.
4041 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4042 * st, and sets *str to the first character after the time format.
4044 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4046 LPCWSTR ptr = *str;
4047 WCHAR *nextPtr;
4048 unsigned long num;
4050 while (isspaceW(*ptr))
4051 ptr++;
4053 num = strtoulW(ptr, &nextPtr, 10);
4054 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4056 ERR("unexpected time format %s\n", debugstr_w(ptr));
4057 return FALSE;
4059 if (num > 23)
4061 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4062 return FALSE;
4064 ptr = nextPtr + 1;
4065 st->wHour = (WORD)num;
4066 num = strtoulW(ptr, &nextPtr, 10);
4067 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4069 ERR("unexpected time format %s\n", debugstr_w(ptr));
4070 return FALSE;
4072 if (num > 59)
4074 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4075 return FALSE;
4077 ptr = nextPtr + 1;
4078 st->wMinute = (WORD)num;
4079 num = strtoulW(ptr, &nextPtr, 10);
4080 if (!nextPtr || nextPtr <= ptr)
4082 ERR("unexpected time format %s\n", debugstr_w(ptr));
4083 return FALSE;
4085 if (num > 59)
4087 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4088 return FALSE;
4090 ptr = nextPtr + 1;
4091 *str = ptr;
4092 st->wSecond = (WORD)num;
4093 return TRUE;
4096 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4098 static const WCHAR gmt[]= { 'G','M','T',0 };
4099 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4100 LPCWSTR ptr;
4101 SYSTEMTIME st = { 0 };
4102 unsigned long num;
4104 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4105 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4106 *dayPtr = *ptr;
4107 *dayPtr = 0;
4108 st.wDayOfWeek = HTTP_ParseDay(day);
4109 if (st.wDayOfWeek >= 7)
4111 ERR("unexpected weekday %s\n", debugstr_w(day));
4112 return FALSE;
4115 while (isspaceW(*ptr))
4116 ptr++;
4118 for (monthPtr = month; !isspace(*ptr) &&
4119 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4120 monthPtr++, ptr++)
4121 *monthPtr = *ptr;
4122 *monthPtr = 0;
4123 st.wMonth = HTTP_ParseMonth(month);
4124 if (!st.wMonth || st.wMonth > 12)
4126 ERR("unexpected month %s\n", debugstr_w(month));
4127 return FALSE;
4130 while (isspaceW(*ptr))
4131 ptr++;
4133 num = strtoulW(ptr, &nextPtr, 10);
4134 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4136 ERR("unexpected day %s\n", debugstr_w(ptr));
4137 return FALSE;
4139 ptr = nextPtr;
4140 st.wDay = (WORD)num;
4142 while (isspaceW(*ptr))
4143 ptr++;
4145 if (!HTTP_ParseTime(&st, &ptr))
4146 return FALSE;
4148 while (isspaceW(*ptr))
4149 ptr++;
4151 num = strtoulW(ptr, &nextPtr, 10);
4152 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4154 ERR("unexpected year %s\n", debugstr_w(ptr));
4155 return FALSE;
4157 ptr = nextPtr;
4158 st.wYear = (WORD)num;
4160 while (isspaceW(*ptr))
4161 ptr++;
4163 /* asctime() doesn't report a timezone, but some web servers do, so accept
4164 * with or without GMT.
4166 if (*ptr && strcmpW(ptr, gmt))
4168 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4169 return FALSE;
4171 return SystemTimeToFileTime(&st, ft);
4174 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4176 static const WCHAR gmt[]= { 'G','M','T',0 };
4177 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4178 LPCWSTR ptr;
4179 unsigned long num;
4180 SYSTEMTIME st = { 0 };
4182 ptr = strchrW(value, ',');
4183 if (!ptr)
4184 return FALSE;
4185 if (ptr - value != 3)
4187 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4188 return FALSE;
4190 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4191 day[3] = 0;
4192 st.wDayOfWeek = HTTP_ParseDay(day);
4193 if (st.wDayOfWeek > 6)
4195 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4196 return FALSE;
4198 ptr++;
4200 while (isspaceW(*ptr))
4201 ptr++;
4203 num = strtoulW(ptr, &nextPtr, 10);
4204 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4206 ERR("unexpected day %s\n", debugstr_w(value));
4207 return FALSE;
4209 ptr = nextPtr;
4210 st.wDay = (WORD)num;
4212 while (isspaceW(*ptr))
4213 ptr++;
4215 for (monthPtr = month; !isspace(*ptr) &&
4216 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4217 monthPtr++, ptr++)
4218 *monthPtr = *ptr;
4219 *monthPtr = 0;
4220 st.wMonth = HTTP_ParseMonth(month);
4221 if (!st.wMonth || st.wMonth > 12)
4223 ERR("unexpected month %s\n", debugstr_w(month));
4224 return FALSE;
4227 while (isspaceW(*ptr))
4228 ptr++;
4230 num = strtoulW(ptr, &nextPtr, 10);
4231 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4233 ERR("unexpected year %s\n", debugstr_w(value));
4234 return FALSE;
4236 ptr = nextPtr;
4237 st.wYear = (WORD)num;
4239 if (!HTTP_ParseTime(&st, &ptr))
4240 return FALSE;
4242 while (isspaceW(*ptr))
4243 ptr++;
4245 if (strcmpW(ptr, gmt))
4247 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4248 return FALSE;
4250 return SystemTimeToFileTime(&st, ft);
4253 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4254 * which may not be the only formats actually seen in the wild.
4255 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4256 * should be accepted as well.
4258 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4260 static const WCHAR zero[] = { '0',0 };
4261 BOOL ret;
4263 if (!strcmpW(value, zero))
4265 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4266 ret = TRUE;
4268 else if (strchrW(value, ','))
4269 ret = HTTP_ParseRfc1123Date(value, ft);
4270 else
4272 ret = HTTP_ParseDateAsAsctime(value, ft);
4273 if (!ret)
4274 ERR("unexpected date format %s\n", debugstr_w(value));
4276 return ret;
4279 static void HTTP_ProcessExpires(http_request_t *request)
4281 BOOL expirationFound = FALSE;
4282 int headerIndex;
4284 /* Look for a Cache-Control header with a max-age directive, as it takes
4285 * precedence over the Expires header.
4287 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4288 if (headerIndex != -1)
4290 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4291 LPWSTR ptr;
4293 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4295 LPWSTR comma = strchrW(ptr, ','), end, equal;
4297 if (comma)
4298 end = comma;
4299 else
4300 end = ptr + strlenW(ptr);
4301 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4303 if (*equal == '=')
4305 static const WCHAR max_age[] = {
4306 'm','a','x','-','a','g','e',0 };
4308 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4310 LPWSTR nextPtr;
4311 unsigned long age;
4313 age = strtoulW(equal + 1, &nextPtr, 10);
4314 if (nextPtr > equal + 1)
4316 LARGE_INTEGER ft;
4318 NtQuerySystemTime( &ft );
4319 /* Age is in seconds, FILETIME resolution is in
4320 * 100 nanosecond intervals.
4322 ft.QuadPart += age * (ULONGLONG)1000000;
4323 request->expires.dwLowDateTime = ft.u.LowPart;
4324 request->expires.dwHighDateTime = ft.u.HighPart;
4325 expirationFound = TRUE;
4329 if (comma)
4331 ptr = comma + 1;
4332 while (isspaceW(*ptr))
4333 ptr++;
4335 else
4336 ptr = NULL;
4339 if (!expirationFound)
4341 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4342 if (headerIndex != -1)
4344 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4345 FILETIME ft;
4347 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4349 expirationFound = TRUE;
4350 request->expires = ft;
4354 if (!expirationFound)
4356 LARGE_INTEGER t;
4358 /* With no known age, default to 10 minutes until expiration. */
4359 NtQuerySystemTime( &t );
4360 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4361 request->expires.dwLowDateTime = t.u.LowPart;
4362 request->expires.dwHighDateTime = t.u.HighPart;
4366 static void HTTP_ProcessLastModified(http_request_t *request)
4368 int headerIndex;
4370 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4371 if (headerIndex != -1)
4373 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4374 FILETIME ft;
4376 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4377 request->last_modified = ft;
4381 static void http_process_keep_alive(http_request_t *req)
4383 int index;
4385 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4386 if(index != -1)
4387 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4388 else
4389 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4392 static void HTTP_CacheRequest(http_request_t *request)
4394 WCHAR url[INTERNET_MAX_URL_LENGTH];
4395 WCHAR cacheFileName[MAX_PATH+1];
4396 BOOL b;
4398 b = HTTP_GetRequestURL(request, url);
4399 if(!b) {
4400 WARN("Could not get URL\n");
4401 return;
4404 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4405 if(b) {
4406 heap_free(request->cacheFile);
4407 CloseHandle(request->hCacheFile);
4409 request->cacheFile = heap_strdupW(cacheFileName);
4410 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4411 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4412 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4413 WARN("Could not create file: %u\n", GetLastError());
4414 request->hCacheFile = NULL;
4416 }else {
4417 WARN("Could not create cache entry: %08x\n", GetLastError());
4421 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4423 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4424 http_session_t *session = request->session;
4425 netconn_t *netconn = NULL;
4426 server_t *server;
4427 DWORD res;
4429 assert(!request->netconn);
4430 reset_data_stream(request);
4432 server = get_server(session->serverName, session->serverPort);
4433 if(!server)
4434 return ERROR_OUTOFMEMORY;
4436 res = HTTP_ResolveName(request, server);
4437 if(res != ERROR_SUCCESS) {
4438 server_release(server);
4439 return res;
4442 EnterCriticalSection(&connection_pool_cs);
4444 while(!list_empty(&server->conn_pool)) {
4445 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4446 list_remove(&netconn->pool_entry);
4448 if(NETCON_is_alive(netconn))
4449 break;
4451 TRACE("connection %p closed during idle\n", netconn);
4452 free_netconn(netconn);
4453 netconn = NULL;
4456 LeaveCriticalSection(&connection_pool_cs);
4458 if(netconn) {
4459 TRACE("<-- reusing %p netconn\n", netconn);
4460 request->netconn = netconn;
4461 *reusing = TRUE;
4462 return ERROR_SUCCESS;
4465 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4466 INTERNET_STATUS_CONNECTING_TO_SERVER,
4467 server->addr_str,
4468 strlen(server->addr_str)+1);
4470 res = create_netconn(is_https, server, request->security_flags, &netconn);
4471 server_release(server);
4472 if(res != ERROR_SUCCESS) {
4473 ERR("create_netconn failed: %u\n", res);
4474 return res;
4477 request->netconn = netconn;
4479 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4480 INTERNET_STATUS_CONNECTED_TO_SERVER,
4481 server->addr_str, strlen(server->addr_str)+1);
4483 if(is_https) {
4484 /* Note: we differ from Microsoft's WinINet here. they seem to have
4485 * a bug that causes no status callbacks to be sent when starting
4486 * a tunnel to a proxy server using the CONNECT verb. i believe our
4487 * behaviour to be more correct and to not cause any incompatibilities
4488 * because using a secure connection through a proxy server is a rare
4489 * case that would be hard for anyone to depend on */
4490 if(session->appInfo->proxy)
4491 res = HTTP_SecureProxyConnect(request);
4492 if(res == ERROR_SUCCESS)
4493 res = NETCON_secure_connect(request->netconn, session->hostName);
4494 if(res != ERROR_SUCCESS)
4496 WARN("Couldn't connect securely to host\n");
4498 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4499 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4500 || res == ERROR_INTERNET_INVALID_CA
4501 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4502 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4503 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4504 || res == ERROR_INTERNET_SEC_INVALID_CERT
4505 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4506 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4510 if(res != ERROR_SUCCESS) {
4511 http_release_netconn(request, FALSE);
4512 return res;
4515 *reusing = FALSE;
4516 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4517 return ERROR_SUCCESS;
4520 /***********************************************************************
4521 * HTTP_HttpSendRequestW (internal)
4523 * Sends the specified request to the HTTP server
4525 * RETURNS
4526 * ERROR_SUCCESS on success
4527 * win32 error code on failure
4530 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4531 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4532 DWORD dwContentLength, BOOL bEndRequest)
4534 INT cnt;
4535 BOOL redirected = FALSE;
4536 LPWSTR requestString = NULL;
4537 INT responseLen;
4538 BOOL loop_next;
4539 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4540 static const WCHAR szContentLength[] =
4541 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4542 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4543 DWORD res;
4545 TRACE("--> %p\n", request);
4547 assert(request->hdr.htype == WH_HHTTPREQ);
4549 /* if the verb is NULL default to GET */
4550 if (!request->verb)
4551 request->verb = heap_strdupW(szGET);
4553 if (dwContentLength || strcmpW(request->verb, szGET))
4555 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4556 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4557 request->bytesToWrite = dwContentLength;
4559 if (request->session->appInfo->agent)
4561 WCHAR *agent_header;
4562 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4563 int len;
4565 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4566 agent_header = heap_alloc(len * sizeof(WCHAR));
4567 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4569 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4570 heap_free(agent_header);
4572 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4574 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4575 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4577 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4579 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4580 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4581 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4586 DWORD len;
4587 BOOL reusing_connection;
4588 char *ascii_req;
4590 loop_next = FALSE;
4592 /* like native, just in case the caller forgot to call InternetReadFile
4593 * for all the data */
4594 drain_content(request);
4595 if(redirected) {
4596 request->contentLength = ~0u;
4597 request->bytesToWrite = 0;
4600 if (TRACE_ON(wininet))
4602 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4603 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4606 HTTP_FixURL(request);
4607 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4609 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4611 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4612 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4614 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4615 HTTP_InsertCookies(request);
4617 /* add the headers the caller supplied */
4618 if( lpszHeaders && dwHeaderLength )
4620 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4621 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4624 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4626 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4627 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4628 heap_free(url);
4630 else
4631 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4634 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4636 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4637 break;
4639 /* send the request as ASCII, tack on the optional data */
4640 if (!lpOptional || redirected)
4641 dwOptionalLength = 0;
4642 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4643 NULL, 0, NULL, NULL );
4644 ascii_req = heap_alloc(len + dwOptionalLength);
4645 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4646 ascii_req, len, NULL, NULL );
4647 if( lpOptional )
4648 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4649 len = (len + dwOptionalLength - 1);
4650 ascii_req[len] = 0;
4651 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4653 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4654 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4656 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4657 heap_free( ascii_req );
4658 if(res != ERROR_SUCCESS) {
4659 TRACE("send failed: %u\n", res);
4660 if(!reusing_connection)
4661 break;
4662 http_release_netconn(request, FALSE);
4663 loop_next = TRUE;
4664 continue;
4667 request->bytesWritten = dwOptionalLength;
4669 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4670 INTERNET_STATUS_REQUEST_SENT,
4671 &len, sizeof(DWORD));
4673 if (bEndRequest)
4675 DWORD dwBufferSize;
4676 DWORD dwStatusCode;
4678 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4679 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4681 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4682 /* FIXME: We should know that connection is closed before sending
4683 * headers. Otherwise wrong callbacks are executed */
4684 if(!responseLen && reusing_connection) {
4685 TRACE("Connection closed by server, reconnecting\n");
4686 http_release_netconn(request, FALSE);
4687 loop_next = TRUE;
4688 continue;
4691 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4692 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4693 sizeof(DWORD));
4695 http_process_keep_alive(request);
4696 HTTP_ProcessCookies(request);
4697 HTTP_ProcessExpires(request);
4698 HTTP_ProcessLastModified(request);
4700 dwBufferSize = sizeof(dwStatusCode);
4701 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4702 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4703 dwStatusCode = 0;
4705 res = set_content_length(request, dwStatusCode);
4706 if(res != ERROR_SUCCESS)
4707 goto lend;
4708 if(!request->contentLength)
4709 http_release_netconn(request, TRUE);
4711 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4713 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4714 dwBufferSize=sizeof(szNewLocation);
4715 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4716 dwStatusCode == HTTP_STATUS_MOVED ||
4717 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4718 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4719 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4721 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4722 dwStatusCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4724 heap_free(request->verb);
4725 request->verb = heap_strdupW(szGET);
4727 drain_content(request);
4728 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4730 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4731 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4732 res = HTTP_HandleRedirect(request, new_url);
4733 if (res == ERROR_SUCCESS)
4735 heap_free(requestString);
4736 loop_next = TRUE;
4738 heap_free( new_url );
4740 redirected = TRUE;
4743 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4745 WCHAR szAuthValue[2048];
4746 dwBufferSize=2048;
4747 if (dwStatusCode == HTTP_STATUS_DENIED)
4749 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4750 DWORD dwIndex = 0;
4751 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4753 if (HTTP_DoAuthorization(request, szAuthValue,
4754 &request->authInfo,
4755 request->session->userName,
4756 request->session->password,
4757 Host->lpszValue))
4759 heap_free(requestString);
4760 loop_next = TRUE;
4761 break;
4765 if(!loop_next) {
4766 TRACE("Cleaning wrong authorization data\n");
4767 destroy_authinfo(request->authInfo);
4768 request->authInfo = NULL;
4771 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4773 DWORD dwIndex = 0;
4774 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4776 if (HTTP_DoAuthorization(request, szAuthValue,
4777 &request->proxyAuthInfo,
4778 request->session->appInfo->proxyUsername,
4779 request->session->appInfo->proxyPassword,
4780 NULL))
4782 loop_next = TRUE;
4783 break;
4787 if(!loop_next) {
4788 TRACE("Cleaning wrong proxy authorization data\n");
4789 destroy_authinfo(request->proxyAuthInfo);
4790 request->proxyAuthInfo = NULL;
4795 else
4796 res = ERROR_SUCCESS;
4798 while (loop_next);
4800 if(res == ERROR_SUCCESS)
4801 HTTP_CacheRequest(request);
4803 lend:
4804 heap_free(requestString);
4806 /* TODO: send notification for P3P header */
4808 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4810 if (res == ERROR_SUCCESS) {
4811 if(request->contentLength && request->bytesWritten == request->bytesToWrite)
4812 HTTP_ReceiveRequestData(request, TRUE);
4813 else
4814 send_request_complete(request,
4815 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4816 }else {
4817 send_request_complete(request, 0, res);
4821 TRACE("<--\n");
4822 return res;
4825 /***********************************************************************
4827 * Helper functions for the HttpSendRequest(Ex) functions
4830 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4832 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4833 http_request_t *request = (http_request_t*) workRequest->hdr;
4835 TRACE("%p\n", request);
4837 HTTP_HttpSendRequestW(request, req->lpszHeader,
4838 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4839 req->dwContentLength, req->bEndRequest);
4841 heap_free(req->lpszHeader);
4845 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4847 INT responseLen;
4848 DWORD dwCode, dwCodeLength;
4849 DWORD dwBufferSize;
4850 DWORD res = ERROR_SUCCESS;
4852 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4853 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4855 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4856 if (!responseLen)
4857 res = ERROR_HTTP_HEADER_NOT_FOUND;
4859 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4860 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4862 /* process cookies here. Is this right? */
4863 http_process_keep_alive(request);
4864 HTTP_ProcessCookies(request);
4865 HTTP_ProcessExpires(request);
4866 HTTP_ProcessLastModified(request);
4868 dwCodeLength = sizeof(dwCode);
4869 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4870 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
4871 dwCode = 0;
4873 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
4874 if(!request->contentLength)
4875 http_release_netconn(request, TRUE);
4878 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4880 if (dwCode == HTTP_STATUS_REDIRECT ||
4881 dwCode == HTTP_STATUS_MOVED ||
4882 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
4883 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
4885 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4886 dwBufferSize=sizeof(szNewLocation);
4887 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4889 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4890 dwCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4892 heap_free(request->verb);
4893 request->verb = heap_strdupW(szGET);
4895 drain_content(request);
4896 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4898 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4899 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4900 res = HTTP_HandleRedirect(request, new_url);
4901 if (res == ERROR_SUCCESS)
4902 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4903 heap_free( new_url );
4909 if (res == ERROR_SUCCESS && request->contentLength)
4910 HTTP_ReceiveRequestData(request, TRUE);
4911 else
4912 send_request_complete(request, res == ERROR_SUCCESS, res);
4914 return res;
4917 /***********************************************************************
4918 * HttpEndRequestA (WININET.@)
4920 * Ends an HTTP request that was started by HttpSendRequestEx
4922 * RETURNS
4923 * TRUE if successful
4924 * FALSE on failure
4927 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4928 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4930 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4932 if (lpBuffersOut)
4934 SetLastError(ERROR_INVALID_PARAMETER);
4935 return FALSE;
4938 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4941 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4943 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4944 http_request_t *request = (http_request_t*)work->hdr;
4946 TRACE("%p\n", request);
4948 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4951 /***********************************************************************
4952 * HttpEndRequestW (WININET.@)
4954 * Ends an HTTP request that was started by HttpSendRequestEx
4956 * RETURNS
4957 * TRUE if successful
4958 * FALSE on failure
4961 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4962 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4964 http_request_t *request;
4965 DWORD res;
4967 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4969 if (lpBuffersOut)
4971 SetLastError(ERROR_INVALID_PARAMETER);
4972 return FALSE;
4975 request = (http_request_t*) get_handle_object( hRequest );
4977 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4979 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4980 if (request)
4981 WININET_Release( &request->hdr );
4982 return FALSE;
4984 request->hdr.dwFlags |= dwFlags;
4986 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4988 WORKREQUEST work;
4989 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
4991 work.asyncproc = AsyncHttpEndRequestProc;
4992 work.hdr = WININET_AddRef( &request->hdr );
4994 work_endrequest = &work.u.HttpEndRequestW;
4995 work_endrequest->dwFlags = dwFlags;
4996 work_endrequest->dwContext = dwContext;
4998 INTERNET_AsyncCall(&work);
4999 res = ERROR_IO_PENDING;
5001 else
5002 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5004 WININET_Release( &request->hdr );
5005 TRACE("%u <--\n", res);
5006 if(res != ERROR_SUCCESS)
5007 SetLastError(res);
5008 return res == ERROR_SUCCESS;
5011 /***********************************************************************
5012 * HttpSendRequestExA (WININET.@)
5014 * Sends the specified request to the HTTP server and allows chunked
5015 * transfers.
5017 * RETURNS
5018 * Success: TRUE
5019 * Failure: FALSE, call GetLastError() for more information.
5021 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5022 LPINTERNET_BUFFERSA lpBuffersIn,
5023 LPINTERNET_BUFFERSA lpBuffersOut,
5024 DWORD dwFlags, DWORD_PTR dwContext)
5026 INTERNET_BUFFERSW BuffersInW;
5027 BOOL rc = FALSE;
5028 DWORD headerlen;
5029 LPWSTR header = NULL;
5031 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5032 lpBuffersOut, dwFlags, dwContext);
5034 if (lpBuffersIn)
5036 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5037 if (lpBuffersIn->lpcszHeader)
5039 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5040 lpBuffersIn->dwHeadersLength,0,0);
5041 header = heap_alloc(headerlen*sizeof(WCHAR));
5042 if (!(BuffersInW.lpcszHeader = header))
5044 SetLastError(ERROR_OUTOFMEMORY);
5045 return FALSE;
5047 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5048 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5049 header, headerlen);
5051 else
5052 BuffersInW.lpcszHeader = NULL;
5053 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5054 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5055 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5056 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5057 BuffersInW.Next = NULL;
5060 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5062 heap_free(header);
5063 return rc;
5066 /***********************************************************************
5067 * HttpSendRequestExW (WININET.@)
5069 * Sends the specified request to the HTTP server and allows chunked
5070 * transfers
5072 * RETURNS
5073 * Success: TRUE
5074 * Failure: FALSE, call GetLastError() for more information.
5076 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5077 LPINTERNET_BUFFERSW lpBuffersIn,
5078 LPINTERNET_BUFFERSW lpBuffersOut,
5079 DWORD dwFlags, DWORD_PTR dwContext)
5081 http_request_t *request;
5082 http_session_t *session;
5083 appinfo_t *hIC;
5084 DWORD res;
5086 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5087 lpBuffersOut, dwFlags, dwContext);
5089 request = (http_request_t*) get_handle_object( hRequest );
5091 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5093 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5094 goto lend;
5097 session = request->session;
5098 assert(session->hdr.htype == WH_HHTTPSESSION);
5099 hIC = session->appInfo;
5100 assert(hIC->hdr.htype == WH_HINIT);
5102 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5104 WORKREQUEST workRequest;
5105 struct WORKREQ_HTTPSENDREQUESTW *req;
5107 workRequest.asyncproc = AsyncHttpSendRequestProc;
5108 workRequest.hdr = WININET_AddRef( &request->hdr );
5109 req = &workRequest.u.HttpSendRequestW;
5110 if (lpBuffersIn)
5112 DWORD size = 0;
5114 if (lpBuffersIn->lpcszHeader)
5116 if (lpBuffersIn->dwHeadersLength == ~0u)
5117 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5118 else
5119 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5121 req->lpszHeader = heap_alloc(size);
5122 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5124 else req->lpszHeader = NULL;
5126 req->dwHeaderLength = size / sizeof(WCHAR);
5127 req->lpOptional = lpBuffersIn->lpvBuffer;
5128 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5129 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5131 else
5133 req->lpszHeader = NULL;
5134 req->dwHeaderLength = 0;
5135 req->lpOptional = NULL;
5136 req->dwOptionalLength = 0;
5137 req->dwContentLength = 0;
5140 req->bEndRequest = FALSE;
5142 INTERNET_AsyncCall(&workRequest);
5144 * This is from windows.
5146 res = ERROR_IO_PENDING;
5148 else
5150 if (lpBuffersIn)
5151 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5152 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5153 lpBuffersIn->dwBufferTotal, FALSE);
5154 else
5155 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5158 lend:
5159 if ( request )
5160 WININET_Release( &request->hdr );
5162 TRACE("<---\n");
5163 SetLastError(res);
5164 return res == ERROR_SUCCESS;
5167 /***********************************************************************
5168 * HttpSendRequestW (WININET.@)
5170 * Sends the specified request to the HTTP server
5172 * RETURNS
5173 * TRUE on success
5174 * FALSE on failure
5177 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5178 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5180 http_request_t *request;
5181 http_session_t *session = NULL;
5182 appinfo_t *hIC = NULL;
5183 DWORD res = ERROR_SUCCESS;
5185 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5186 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5188 request = (http_request_t*) get_handle_object( hHttpRequest );
5189 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5191 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5192 goto lend;
5195 session = request->session;
5196 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5198 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5199 goto lend;
5202 hIC = session->appInfo;
5203 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5205 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5206 goto lend;
5209 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5211 WORKREQUEST workRequest;
5212 struct WORKREQ_HTTPSENDREQUESTW *req;
5214 workRequest.asyncproc = AsyncHttpSendRequestProc;
5215 workRequest.hdr = WININET_AddRef( &request->hdr );
5216 req = &workRequest.u.HttpSendRequestW;
5217 if (lpszHeaders)
5219 DWORD size;
5221 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5222 else size = dwHeaderLength * sizeof(WCHAR);
5224 req->lpszHeader = heap_alloc(size);
5225 memcpy(req->lpszHeader, lpszHeaders, size);
5227 else
5228 req->lpszHeader = 0;
5229 req->dwHeaderLength = dwHeaderLength;
5230 req->lpOptional = lpOptional;
5231 req->dwOptionalLength = dwOptionalLength;
5232 req->dwContentLength = dwOptionalLength;
5233 req->bEndRequest = TRUE;
5235 INTERNET_AsyncCall(&workRequest);
5237 * This is from windows.
5239 res = ERROR_IO_PENDING;
5241 else
5243 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5244 dwHeaderLength, lpOptional, dwOptionalLength,
5245 dwOptionalLength, TRUE);
5247 lend:
5248 if( request )
5249 WININET_Release( &request->hdr );
5251 SetLastError(res);
5252 return res == ERROR_SUCCESS;
5255 /***********************************************************************
5256 * HttpSendRequestA (WININET.@)
5258 * Sends the specified request to the HTTP server
5260 * RETURNS
5261 * TRUE on success
5262 * FALSE on failure
5265 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5266 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5268 BOOL result;
5269 LPWSTR szHeaders=NULL;
5270 DWORD nLen=dwHeaderLength;
5271 if(lpszHeaders!=NULL)
5273 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5274 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5275 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5277 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5278 heap_free(szHeaders);
5279 return result;
5282 /***********************************************************************
5283 * HTTPSESSION_Destroy (internal)
5285 * Deallocate session handle
5288 static void HTTPSESSION_Destroy(object_header_t *hdr)
5290 http_session_t *session = (http_session_t*) hdr;
5292 TRACE("%p\n", session);
5294 WININET_Release(&session->appInfo->hdr);
5296 heap_free(session->hostName);
5297 heap_free(session->serverName);
5298 heap_free(session->password);
5299 heap_free(session->userName);
5302 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5304 switch(option) {
5305 case INTERNET_OPTION_HANDLE_TYPE:
5306 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5308 if (*size < sizeof(ULONG))
5309 return ERROR_INSUFFICIENT_BUFFER;
5311 *size = sizeof(DWORD);
5312 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5313 return ERROR_SUCCESS;
5316 return INET_QueryOption(hdr, option, buffer, size, unicode);
5319 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5321 http_session_t *ses = (http_session_t*)hdr;
5323 switch(option) {
5324 case INTERNET_OPTION_USERNAME:
5326 heap_free(ses->userName);
5327 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5328 return ERROR_SUCCESS;
5330 case INTERNET_OPTION_PASSWORD:
5332 heap_free(ses->password);
5333 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5334 return ERROR_SUCCESS;
5336 default: break;
5339 return ERROR_INTERNET_INVALID_OPTION;
5342 static const object_vtbl_t HTTPSESSIONVtbl = {
5343 HTTPSESSION_Destroy,
5344 NULL,
5345 HTTPSESSION_QueryOption,
5346 HTTPSESSION_SetOption,
5347 NULL,
5348 NULL,
5349 NULL,
5350 NULL,
5351 NULL
5355 /***********************************************************************
5356 * HTTP_Connect (internal)
5358 * Create http session handle
5360 * RETURNS
5361 * HINTERNET a session handle on success
5362 * NULL on failure
5365 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5366 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5367 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5368 DWORD dwInternalFlags, HINTERNET *ret)
5370 http_session_t *session = NULL;
5372 TRACE("-->\n");
5374 if (!lpszServerName || !lpszServerName[0])
5375 return ERROR_INVALID_PARAMETER;
5377 assert( hIC->hdr.htype == WH_HINIT );
5379 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5380 if (!session)
5381 return ERROR_OUTOFMEMORY;
5384 * According to my tests. The name is not resolved until a request is sent
5387 session->hdr.htype = WH_HHTTPSESSION;
5388 session->hdr.dwFlags = dwFlags;
5389 session->hdr.dwContext = dwContext;
5390 session->hdr.dwInternalFlags |= dwInternalFlags;
5392 WININET_AddRef( &hIC->hdr );
5393 session->appInfo = hIC;
5394 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5396 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5397 if(hIC->proxyBypass)
5398 FIXME("Proxy bypass is ignored.\n");
5400 session->serverName = heap_strdupW(lpszServerName);
5401 session->hostName = heap_strdupW(lpszServerName);
5402 if (lpszUserName && lpszUserName[0])
5403 session->userName = heap_strdupW(lpszUserName);
5404 if (lpszPassword && lpszPassword[0])
5405 session->password = heap_strdupW(lpszPassword);
5406 session->serverPort = serverPort;
5407 session->hostPort = serverPort;
5409 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5410 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5412 INTERNET_SendCallback(&hIC->hdr, dwContext,
5413 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5414 sizeof(HINTERNET));
5418 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5419 * windows
5422 TRACE("%p --> %p\n", hIC, session);
5424 *ret = session->hdr.hInternet;
5425 return ERROR_SUCCESS;
5428 /***********************************************************************
5429 * HTTP_clear_response_headers (internal)
5431 * clear out any old response headers
5433 static void HTTP_clear_response_headers( http_request_t *request )
5435 DWORD i;
5437 for( i=0; i<request->nCustHeaders; i++)
5439 if( !request->custHeaders[i].lpszField )
5440 continue;
5441 if( !request->custHeaders[i].lpszValue )
5442 continue;
5443 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5444 continue;
5445 HTTP_DeleteCustomHeader( request, i );
5446 i--;
5450 /***********************************************************************
5451 * HTTP_GetResponseHeaders (internal)
5453 * Read server response
5455 * RETURNS
5457 * TRUE on success
5458 * FALSE on error
5460 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5462 INT cbreaks = 0;
5463 WCHAR buffer[MAX_REPLY_LEN];
5464 DWORD buflen = MAX_REPLY_LEN;
5465 BOOL bSuccess = FALSE;
5466 INT rc = 0;
5467 char bufferA[MAX_REPLY_LEN];
5468 LPWSTR status_code = NULL, status_text = NULL;
5469 DWORD cchMaxRawHeaders = 1024;
5470 LPWSTR lpszRawHeaders = NULL;
5471 LPWSTR temp;
5472 DWORD cchRawHeaders = 0;
5473 BOOL codeHundred = FALSE;
5475 TRACE("-->\n");
5477 if(!request->netconn)
5478 goto lend;
5480 do {
5481 static const WCHAR szHundred[] = {'1','0','0',0};
5483 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5485 buflen = MAX_REPLY_LEN;
5486 if (!read_line(request, bufferA, &buflen))
5487 goto lend;
5489 /* clear old response headers (eg. from a redirect response) */
5490 if (clear) {
5491 HTTP_clear_response_headers( request );
5492 clear = FALSE;
5495 rc += buflen;
5496 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5497 /* check is this a status code line? */
5498 if (!strncmpW(buffer, g_szHttp1_0, 4))
5500 /* split the version from the status code */
5501 status_code = strchrW( buffer, ' ' );
5502 if( !status_code )
5503 goto lend;
5504 *status_code++=0;
5506 /* split the status code from the status text */
5507 status_text = strchrW( status_code, ' ' );
5508 if( !status_text )
5509 goto lend;
5510 *status_text++=0;
5512 TRACE("version [%s] status code [%s] status text [%s]\n",
5513 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5515 codeHundred = (!strcmpW(status_code, szHundred));
5517 else if (!codeHundred)
5519 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5521 heap_free(request->version);
5522 heap_free(request->statusText);
5524 request->version = heap_strdupW(g_szHttp1_0);
5525 request->statusText = heap_strdupW(szOK);
5527 heap_free(request->rawHeaders);
5528 request->rawHeaders = heap_strdupW(szDefaultHeader);
5530 bSuccess = TRUE;
5531 goto lend;
5533 } while (codeHundred);
5535 /* Add status code */
5536 HTTP_ProcessHeader(request, szStatus, status_code,
5537 HTTP_ADDHDR_FLAG_REPLACE);
5539 heap_free(request->version);
5540 heap_free(request->statusText);
5542 request->version = heap_strdupW(buffer);
5543 request->statusText = heap_strdupW(status_text);
5545 /* Restore the spaces */
5546 *(status_code-1) = ' ';
5547 *(status_text-1) = ' ';
5549 /* regenerate raw headers */
5550 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5551 if (!lpszRawHeaders) goto lend;
5553 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5554 cchMaxRawHeaders *= 2;
5555 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5556 if (temp == NULL) goto lend;
5557 lpszRawHeaders = temp;
5558 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5559 cchRawHeaders += (buflen-1);
5560 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5561 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5562 lpszRawHeaders[cchRawHeaders] = '\0';
5564 /* Parse each response line */
5567 buflen = MAX_REPLY_LEN;
5568 if (read_line(request, bufferA, &buflen))
5570 LPWSTR * pFieldAndValue;
5572 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5574 if (!bufferA[0]) break;
5575 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5577 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5578 if (pFieldAndValue)
5580 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5581 cchMaxRawHeaders *= 2;
5582 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5583 if (temp == NULL) goto lend;
5584 lpszRawHeaders = temp;
5585 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5586 cchRawHeaders += (buflen-1);
5587 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5588 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5589 lpszRawHeaders[cchRawHeaders] = '\0';
5591 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5592 HTTP_ADDREQ_FLAG_ADD );
5594 HTTP_FreeTokens(pFieldAndValue);
5597 else
5599 cbreaks++;
5600 if (cbreaks >= 2)
5601 break;
5603 }while(1);
5605 /* make sure the response header is terminated with an empty line. Some apps really
5606 truly care about that empty line being there for some reason. Just add it to the
5607 header. */
5608 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5610 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5611 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5612 if (temp == NULL) goto lend;
5613 lpszRawHeaders = temp;
5616 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5618 heap_free(request->rawHeaders);
5619 request->rawHeaders = lpszRawHeaders;
5620 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5621 bSuccess = TRUE;
5623 lend:
5625 TRACE("<--\n");
5626 if (bSuccess)
5627 return rc;
5628 else
5630 heap_free(lpszRawHeaders);
5631 return 0;
5635 /***********************************************************************
5636 * HTTP_InterpretHttpHeader (internal)
5638 * Parse server response
5640 * RETURNS
5642 * Pointer to array of field, value, NULL on success.
5643 * NULL on error.
5645 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5647 LPWSTR * pTokenPair;
5648 LPWSTR pszColon;
5649 INT len;
5651 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5653 pszColon = strchrW(buffer, ':');
5654 /* must have two tokens */
5655 if (!pszColon)
5657 HTTP_FreeTokens(pTokenPair);
5658 if (buffer[0])
5659 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5660 return NULL;
5663 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5664 if (!pTokenPair[0])
5666 HTTP_FreeTokens(pTokenPair);
5667 return NULL;
5669 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5670 pTokenPair[0][pszColon - buffer] = '\0';
5672 /* skip colon */
5673 pszColon++;
5674 len = strlenW(pszColon);
5675 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5676 if (!pTokenPair[1])
5678 HTTP_FreeTokens(pTokenPair);
5679 return NULL;
5681 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5683 strip_spaces(pTokenPair[0]);
5684 strip_spaces(pTokenPair[1]);
5686 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5687 return pTokenPair;
5690 /***********************************************************************
5691 * HTTP_ProcessHeader (internal)
5693 * Stuff header into header tables according to <dwModifier>
5697 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5699 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5701 LPHTTPHEADERW lphttpHdr = NULL;
5702 INT index = -1;
5703 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5704 DWORD res = ERROR_HTTP_INVALID_HEADER;
5706 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5708 /* REPLACE wins out over ADD */
5709 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5710 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5712 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5713 index = -1;
5714 else
5715 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5717 if (index >= 0)
5719 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5720 return ERROR_HTTP_INVALID_HEADER;
5721 lphttpHdr = &request->custHeaders[index];
5723 else if (value)
5725 HTTPHEADERW hdr;
5727 hdr.lpszField = (LPWSTR)field;
5728 hdr.lpszValue = (LPWSTR)value;
5729 hdr.wFlags = hdr.wCount = 0;
5731 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5732 hdr.wFlags |= HDR_ISREQUEST;
5734 return HTTP_InsertCustomHeader(request, &hdr);
5736 /* no value to delete */
5737 else return ERROR_SUCCESS;
5739 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5740 lphttpHdr->wFlags |= HDR_ISREQUEST;
5741 else
5742 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5744 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5746 HTTP_DeleteCustomHeader( request, index );
5748 if (value)
5750 HTTPHEADERW hdr;
5752 hdr.lpszField = (LPWSTR)field;
5753 hdr.lpszValue = (LPWSTR)value;
5754 hdr.wFlags = hdr.wCount = 0;
5756 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5757 hdr.wFlags |= HDR_ISREQUEST;
5759 return HTTP_InsertCustomHeader(request, &hdr);
5762 return ERROR_SUCCESS;
5764 else if (dwModifier & COALESCEFLAGS)
5766 LPWSTR lpsztmp;
5767 WCHAR ch = 0;
5768 INT len = 0;
5769 INT origlen = strlenW(lphttpHdr->lpszValue);
5770 INT valuelen = strlenW(value);
5772 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5774 ch = ',';
5775 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5777 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5779 ch = ';';
5780 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5783 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5785 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5786 if (lpsztmp)
5788 lphttpHdr->lpszValue = lpsztmp;
5789 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5790 if (ch > 0)
5792 lphttpHdr->lpszValue[origlen] = ch;
5793 origlen++;
5794 lphttpHdr->lpszValue[origlen] = ' ';
5795 origlen++;
5798 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5799 lphttpHdr->lpszValue[len] = '\0';
5800 res = ERROR_SUCCESS;
5802 else
5804 WARN("heap_realloc (%d bytes) failed\n",len+1);
5805 res = ERROR_OUTOFMEMORY;
5808 TRACE("<-- %d\n", res);
5809 return res;
5812 /***********************************************************************
5813 * HTTP_GetCustomHeaderIndex (internal)
5815 * Return index of custom header from header array
5818 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5819 int requested_index, BOOL request_only)
5821 DWORD index;
5823 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5825 for (index = 0; index < request->nCustHeaders; index++)
5827 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5828 continue;
5830 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5831 continue;
5833 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5834 continue;
5836 if (requested_index == 0)
5837 break;
5838 requested_index --;
5841 if (index >= request->nCustHeaders)
5842 index = -1;
5844 TRACE("Return: %d\n", index);
5845 return index;
5849 /***********************************************************************
5850 * HTTP_InsertCustomHeader (internal)
5852 * Insert header into array
5855 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5857 INT count;
5858 LPHTTPHEADERW lph = NULL;
5860 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5861 count = request->nCustHeaders + 1;
5862 if (count > 1)
5863 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5864 else
5865 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5867 if (!lph)
5868 return ERROR_OUTOFMEMORY;
5870 request->custHeaders = lph;
5871 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5872 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5873 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5874 request->custHeaders[count-1].wCount= lpHdr->wCount;
5875 request->nCustHeaders++;
5877 return ERROR_SUCCESS;
5881 /***********************************************************************
5882 * HTTP_DeleteCustomHeader (internal)
5884 * Delete header from array
5885 * If this function is called, the indexs may change.
5887 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5889 if( request->nCustHeaders <= 0 )
5890 return FALSE;
5891 if( index >= request->nCustHeaders )
5892 return FALSE;
5893 request->nCustHeaders--;
5895 heap_free(request->custHeaders[index].lpszField);
5896 heap_free(request->custHeaders[index].lpszValue);
5898 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5899 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5900 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5902 return TRUE;
5906 /***********************************************************************
5907 * HTTP_VerifyValidHeader (internal)
5909 * Verify the given header is not invalid for the given http request
5912 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5914 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5915 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5916 return ERROR_HTTP_INVALID_HEADER;
5918 return ERROR_SUCCESS;
5921 /***********************************************************************
5922 * IsHostInProxyBypassList (@)
5924 * Undocumented
5927 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5929 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5930 return FALSE;
5933 /***********************************************************************
5934 * InternetShowSecurityInfoByURLA (@)
5936 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5938 FIXME("stub: %s %p\n", url, window);
5939 return FALSE;
5942 /***********************************************************************
5943 * InternetShowSecurityInfoByURLW (@)
5945 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5947 FIXME("stub: %s %p\n", debugstr_w(url), window);
5948 return FALSE;