user32/tests: Do not check the last error on succeeding hotkey calls.
[wine.git] / dlls / wininet / http.c
blob559323df32fc434bb543f72de4e5f35a0d2bab3b
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;
2351 netconn_stream->content_read += *read = len;
2352 TRACE("read %u bytes\n", len);
2353 return ERROR_SUCCESS;
2356 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2358 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2359 BYTE buf[1024];
2360 DWORD avail;
2361 int len;
2363 if(netconn_end_of_data(stream, req))
2364 return TRUE;
2366 do {
2367 avail = netconn_get_avail_data(stream, req);
2368 if(!avail)
2369 return FALSE;
2371 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2372 return FALSE;
2374 netconn_stream->content_read += len;
2375 }while(netconn_stream->content_read < netconn_stream->content_length);
2377 return TRUE;
2380 static void netconn_destroy(data_stream_t *stream)
2384 static const data_stream_vtbl_t netconn_stream_vtbl = {
2385 netconn_get_avail_data,
2386 netconn_end_of_data,
2387 netconn_read,
2388 netconn_drain_content,
2389 netconn_destroy
2392 /* read some more data into the read buffer (the read section must be held) */
2393 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2395 DWORD res;
2396 int len;
2398 if (stream->buf_pos)
2400 /* move existing data to the start of the buffer */
2401 if(stream->buf_size)
2402 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2403 stream->buf_pos = 0;
2406 if (maxlen == -1) maxlen = sizeof(stream->buf);
2408 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2409 maxlen - stream->buf_size, 0, &len );
2410 if(res == ERROR_SUCCESS)
2411 stream->buf_size += len;
2413 return res;
2416 /* remove some amount of data from the read buffer (the read section must be held) */
2417 static void remove_chunked_data(chunked_stream_t *stream, int count)
2419 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2420 else stream->buf_pos += count;
2423 /* discard data contents until we reach end of line (the read section must be held) */
2424 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2426 DWORD res;
2430 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2431 if (eol)
2433 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2434 break;
2436 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2437 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2438 } while (stream->buf_size);
2439 return ERROR_SUCCESS;
2442 /* read the size of the next chunk (the read section must be held) */
2443 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2445 /* TODOO */
2446 DWORD chunk_size = 0, res;
2448 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2449 return res;
2451 for (;;)
2453 while (stream->buf_size)
2455 char ch = stream->buf[stream->buf_pos];
2456 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2457 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2458 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2459 else if (ch == ';' || ch == '\r' || ch == '\n')
2461 TRACE( "reading %u byte chunk\n", chunk_size );
2462 stream->chunk_size = chunk_size;
2463 req->contentLength += chunk_size;
2464 return discard_chunked_eol(stream, req);
2466 remove_chunked_data(stream, 1);
2468 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2469 if (!stream->buf_size)
2471 stream->chunk_size = 0;
2472 return ERROR_SUCCESS;
2477 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2479 /* Allow reading only from read buffer */
2480 return 0;
2483 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2485 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2486 return !chunked_stream->chunk_size;
2489 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2490 DWORD *read, read_mode_t read_mode)
2492 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2493 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2495 if(chunked_stream->chunk_size == ~0u) {
2496 res = start_next_chunk(chunked_stream, req);
2497 if(res != ERROR_SUCCESS)
2498 return res;
2501 while(size && chunked_stream->chunk_size) {
2502 if(chunked_stream->buf_size) {
2503 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2505 /* this could block */
2506 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2507 break;
2509 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2510 remove_chunked_data(chunked_stream, read_bytes);
2511 }else {
2512 read_bytes = min(size, chunked_stream->chunk_size);
2514 if(read_mode == READMODE_NOBLOCK) {
2515 DWORD avail;
2517 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2518 break;
2519 if(read_bytes > avail)
2520 read_bytes = avail;
2522 /* this could block */
2523 if(read_bytes == chunked_stream->chunk_size)
2524 break;
2527 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2528 if(res != ERROR_SUCCESS)
2529 break;
2532 chunked_stream->chunk_size -= read_bytes;
2533 size -= read_bytes;
2534 ret_read += read_bytes;
2535 if(!chunked_stream->chunk_size) {
2536 assert(read_mode != READMODE_NOBLOCK);
2537 res = start_next_chunk(chunked_stream, req);
2538 if(res != ERROR_SUCCESS)
2539 break;
2542 if(read_mode == READMODE_ASYNC)
2543 read_mode = READMODE_NOBLOCK;
2546 TRACE("read %u bytes\n", ret_read);
2547 *read = ret_read;
2548 return res;
2551 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2553 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2555 /* FIXME: we can do better */
2556 return !chunked_stream->chunk_size;
2559 static void chunked_destroy(data_stream_t *stream)
2561 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2562 heap_free(chunked_stream);
2565 static const data_stream_vtbl_t chunked_stream_vtbl = {
2566 chunked_get_avail_data,
2567 chunked_end_of_data,
2568 chunked_read,
2569 chunked_drain_content,
2570 chunked_destroy
2573 /* set the request content length based on the headers */
2574 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2576 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2577 WCHAR encoding[20];
2578 DWORD size;
2580 if(status_code == HTTP_STATUS_NO_CONTENT) {
2581 request->contentLength = request->netconn_stream.content_length = 0;
2582 return ERROR_SUCCESS;
2585 size = sizeof(request->contentLength);
2586 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2587 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2588 request->contentLength = ~0u;
2589 request->netconn_stream.content_length = request->contentLength;
2590 request->netconn_stream.content_read = request->read_size;
2592 size = sizeof(encoding);
2593 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2594 !strcmpiW(encoding, szChunked))
2596 chunked_stream_t *chunked_stream;
2598 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2599 if(!chunked_stream)
2600 return ERROR_OUTOFMEMORY;
2602 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2603 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2604 chunked_stream->chunk_size = ~0u;
2606 if(request->read_size) {
2607 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2608 chunked_stream->buf_size = request->read_size;
2609 request->read_size = request->read_pos = 0;
2612 request->data_stream = &chunked_stream->data_stream;
2613 request->contentLength = ~0u;
2614 request->read_chunked = TRUE;
2617 if(request->decoding) {
2618 int encoding_idx;
2620 static const WCHAR gzipW[] = {'g','z','i','p',0};
2622 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2623 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2624 return init_gzip_stream(request);
2627 return ERROR_SUCCESS;
2630 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2632 INTERNET_ASYNC_RESULT iar;
2633 DWORD res, read = 0;
2634 read_mode_t mode;
2636 TRACE("%p\n", req);
2638 EnterCriticalSection( &req->read_section );
2640 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2641 res = refill_read_buffer(req, mode, &read);
2642 if(res == ERROR_SUCCESS) {
2643 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2644 iar.dwError = first_notif ? 0 : get_avail_data(req);
2645 }else {
2646 iar.dwResult = 0;
2647 iar.dwError = res;
2650 LeaveCriticalSection( &req->read_section );
2652 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2653 WARN("res %u read %u, closing connection\n", res, read);
2654 http_release_netconn(req, FALSE);
2657 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2658 sizeof(INTERNET_ASYNC_RESULT));
2661 /* read data from the http connection (the read section must be held) */
2662 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2664 DWORD current_read = 0, ret_read = 0;
2665 read_mode_t read_mode;
2666 DWORD res = ERROR_SUCCESS;
2668 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2670 EnterCriticalSection( &req->read_section );
2672 if(req->read_size) {
2673 ret_read = min(size, req->read_size);
2674 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2675 req->read_size -= ret_read;
2676 req->read_pos += ret_read;
2677 if(read_mode == READMODE_ASYNC)
2678 read_mode = READMODE_NOBLOCK;
2681 if(ret_read < size) {
2682 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2683 ret_read += current_read;
2686 LeaveCriticalSection( &req->read_section );
2688 *read = ret_read;
2689 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2691 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2692 BOOL res;
2693 DWORD written;
2695 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2696 if(!res)
2697 WARN("WriteFile failed: %u\n", GetLastError());
2700 if(size && !ret_read)
2701 http_release_netconn(req, res == ERROR_SUCCESS);
2703 return res;
2707 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2709 http_request_t *req = (http_request_t*)hdr;
2710 DWORD res;
2712 EnterCriticalSection( &req->read_section );
2713 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2714 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2716 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2717 if(res == ERROR_SUCCESS)
2718 res = hdr->dwError;
2719 LeaveCriticalSection( &req->read_section );
2721 return res;
2724 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2726 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2727 http_request_t *req = (http_request_t*)workRequest->hdr;
2728 INTERNET_ASYNC_RESULT iar;
2729 DWORD res;
2731 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2733 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2734 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2736 iar.dwResult = res == ERROR_SUCCESS;
2737 iar.dwError = res;
2739 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2740 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2741 sizeof(INTERNET_ASYNC_RESULT));
2744 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2745 DWORD flags, DWORD_PTR context)
2747 http_request_t *req = (http_request_t*)hdr;
2748 DWORD res, size, read, error = ERROR_SUCCESS;
2750 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2751 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2753 if (buffers->dwStructSize != sizeof(*buffers))
2754 return ERROR_INVALID_PARAMETER;
2756 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2758 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2760 WORKREQUEST workRequest;
2762 if (TryEnterCriticalSection( &req->read_section ))
2764 if (get_avail_data(req))
2766 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2767 &buffers->dwBufferLength, FALSE);
2768 size = buffers->dwBufferLength;
2769 LeaveCriticalSection( &req->read_section );
2770 goto done;
2772 LeaveCriticalSection( &req->read_section );
2775 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2776 workRequest.hdr = WININET_AddRef(&req->hdr);
2777 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2779 INTERNET_AsyncCall(&workRequest);
2781 return ERROR_IO_PENDING;
2784 read = 0;
2785 size = buffers->dwBufferLength;
2787 EnterCriticalSection( &req->read_section );
2788 if(hdr->dwError == ERROR_SUCCESS)
2789 hdr->dwError = INTERNET_HANDLE_IN_USE;
2790 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2791 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2793 while(1) {
2794 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2795 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2796 if(res != ERROR_SUCCESS)
2797 break;
2799 read += buffers->dwBufferLength;
2800 if(read == size || end_of_read_data(req))
2801 break;
2803 LeaveCriticalSection( &req->read_section );
2805 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2806 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2807 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2808 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2810 EnterCriticalSection( &req->read_section );
2813 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2814 hdr->dwError = ERROR_SUCCESS;
2815 else
2816 error = hdr->dwError;
2818 LeaveCriticalSection( &req->read_section );
2819 size = buffers->dwBufferLength;
2820 buffers->dwBufferLength = read;
2822 done:
2823 if (res == ERROR_SUCCESS) {
2824 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2825 &size, sizeof(size));
2828 return res==ERROR_SUCCESS ? error : res;
2831 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2833 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2834 http_request_t *req = (http_request_t*)workRequest->hdr;
2835 INTERNET_ASYNC_RESULT iar;
2836 DWORD res;
2838 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2840 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2841 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2843 iar.dwResult = res == ERROR_SUCCESS;
2844 iar.dwError = res;
2846 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2847 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2848 sizeof(INTERNET_ASYNC_RESULT));
2851 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2852 DWORD flags, DWORD_PTR context)
2855 http_request_t *req = (http_request_t*)hdr;
2856 DWORD res, size, read, error = ERROR_SUCCESS;
2858 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2859 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2861 if (buffers->dwStructSize != sizeof(*buffers))
2862 return ERROR_INVALID_PARAMETER;
2864 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2866 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2868 WORKREQUEST workRequest;
2870 if (TryEnterCriticalSection( &req->read_section ))
2872 if (get_avail_data(req))
2874 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2875 &buffers->dwBufferLength, FALSE);
2876 size = buffers->dwBufferLength;
2877 LeaveCriticalSection( &req->read_section );
2878 goto done;
2880 LeaveCriticalSection( &req->read_section );
2883 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2884 workRequest.hdr = WININET_AddRef(&req->hdr);
2885 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2887 INTERNET_AsyncCall(&workRequest);
2889 return ERROR_IO_PENDING;
2892 read = 0;
2893 size = buffers->dwBufferLength;
2895 EnterCriticalSection( &req->read_section );
2896 if(hdr->dwError == ERROR_SUCCESS)
2897 hdr->dwError = INTERNET_HANDLE_IN_USE;
2898 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2899 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2901 while(1) {
2902 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2903 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2904 if(res != ERROR_SUCCESS)
2905 break;
2907 read += buffers->dwBufferLength;
2908 if(read == size || end_of_read_data(req))
2909 break;
2911 LeaveCriticalSection( &req->read_section );
2913 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2914 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2915 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2916 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2918 EnterCriticalSection( &req->read_section );
2921 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2922 hdr->dwError = ERROR_SUCCESS;
2923 else
2924 error = hdr->dwError;
2926 LeaveCriticalSection( &req->read_section );
2927 size = buffers->dwBufferLength;
2928 buffers->dwBufferLength = read;
2930 done:
2931 if (res == ERROR_SUCCESS) {
2932 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2933 &size, sizeof(size));
2936 return res==ERROR_SUCCESS ? error : res;
2939 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2941 DWORD res;
2942 http_request_t *request = (http_request_t*)hdr;
2944 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2946 *written = 0;
2947 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2948 if (res == ERROR_SUCCESS)
2949 request->bytesWritten += *written;
2951 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2952 return res;
2955 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2957 http_request_t *req = (http_request_t*)workRequest->hdr;
2959 HTTP_ReceiveRequestData(req, FALSE);
2962 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2964 http_request_t *req = (http_request_t*)hdr;
2966 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2968 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2970 WORKREQUEST workRequest;
2972 /* never wait, if we can't enter the section we queue an async request right away */
2973 if (TryEnterCriticalSection( &req->read_section ))
2975 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2976 if ((*available = get_avail_data( req ))) goto done;
2977 if (end_of_read_data( req )) goto done;
2978 LeaveCriticalSection( &req->read_section );
2981 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2982 workRequest.hdr = WININET_AddRef( &req->hdr );
2984 INTERNET_AsyncCall(&workRequest);
2986 return ERROR_IO_PENDING;
2989 EnterCriticalSection( &req->read_section );
2991 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2993 refill_read_buffer( req, READMODE_ASYNC, NULL );
2994 *available = get_avail_data( req );
2997 done:
2998 LeaveCriticalSection( &req->read_section );
3000 TRACE( "returning %u\n", *available );
3001 return ERROR_SUCCESS;
3004 static const object_vtbl_t HTTPREQVtbl = {
3005 HTTPREQ_Destroy,
3006 HTTPREQ_CloseConnection,
3007 HTTPREQ_QueryOption,
3008 HTTPREQ_SetOption,
3009 HTTPREQ_ReadFile,
3010 HTTPREQ_ReadFileExA,
3011 HTTPREQ_ReadFileExW,
3012 HTTPREQ_WriteFile,
3013 HTTPREQ_QueryDataAvailable,
3014 NULL
3017 /***********************************************************************
3018 * HTTP_HttpOpenRequestW (internal)
3020 * Open a HTTP request handle
3022 * RETURNS
3023 * HINTERNET a HTTP request handle on success
3024 * NULL on failure
3027 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3028 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3029 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3030 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3032 appinfo_t *hIC = session->appInfo;
3033 http_request_t *request;
3034 DWORD len, res = ERROR_SUCCESS;
3036 TRACE("-->\n");
3038 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3039 if(!request)
3040 return ERROR_OUTOFMEMORY;
3042 request->hdr.htype = WH_HHTTPREQ;
3043 request->hdr.dwFlags = dwFlags;
3044 request->hdr.dwContext = dwContext;
3045 request->contentLength = ~0u;
3047 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3048 request->data_stream = &request->netconn_stream.data_stream;
3050 InitializeCriticalSection( &request->read_section );
3052 WININET_AddRef( &session->hdr );
3053 request->session = session;
3054 list_add_head( &session->hdr.children, &request->hdr.entry );
3056 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3057 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3058 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3059 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3061 if (lpszObjectName && *lpszObjectName) {
3062 HRESULT rc;
3064 len = 0;
3065 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3066 if (rc != E_POINTER)
3067 len = strlenW(lpszObjectName)+1;
3068 request->path = heap_alloc(len*sizeof(WCHAR));
3069 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3070 URL_ESCAPE_SPACES_ONLY);
3071 if (rc != S_OK)
3073 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3074 strcpyW(request->path,lpszObjectName);
3076 }else {
3077 static const WCHAR slashW[] = {'/',0};
3079 request->path = heap_strdupW(slashW);
3082 if (lpszReferrer && *lpszReferrer)
3083 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3085 if (lpszAcceptTypes)
3087 int i;
3088 for (i = 0; lpszAcceptTypes[i]; i++)
3090 if (!*lpszAcceptTypes[i]) continue;
3091 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3092 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3093 HTTP_ADDHDR_FLAG_REQ |
3094 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3098 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3099 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3101 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3102 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3103 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3105 WCHAR *host_name;
3107 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3109 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3110 if (!host_name) {
3111 res = ERROR_OUTOFMEMORY;
3112 goto lend;
3115 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3116 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3117 heap_free(host_name);
3119 else
3120 HTTP_ProcessHeader(request, hostW, session->hostName,
3121 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3123 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3124 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3125 INTERNET_DEFAULT_HTTPS_PORT :
3126 INTERNET_DEFAULT_HTTP_PORT);
3128 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3129 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3130 INTERNET_DEFAULT_HTTPS_PORT :
3131 INTERNET_DEFAULT_HTTP_PORT);
3133 if (hIC->proxy && hIC->proxy[0])
3134 HTTP_DealWithProxy( hIC, session, request );
3136 INTERNET_SendCallback(&session->hdr, dwContext,
3137 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3138 sizeof(HINTERNET));
3140 lend:
3141 TRACE("<-- %u (%p)\n", res, request);
3143 if(res != ERROR_SUCCESS) {
3144 WININET_Release( &request->hdr );
3145 *ret = NULL;
3146 return res;
3149 *ret = request->hdr.hInternet;
3150 return ERROR_SUCCESS;
3153 /***********************************************************************
3154 * HttpOpenRequestW (WININET.@)
3156 * Open a HTTP request handle
3158 * RETURNS
3159 * HINTERNET a HTTP request handle on success
3160 * NULL on failure
3163 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3164 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3165 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3166 DWORD dwFlags, DWORD_PTR dwContext)
3168 http_session_t *session;
3169 HINTERNET handle = NULL;
3170 DWORD res;
3172 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3173 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3174 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3175 dwFlags, dwContext);
3176 if(lpszAcceptTypes!=NULL)
3178 int i;
3179 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3180 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3183 session = (http_session_t*) get_handle_object( hHttpSession );
3184 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3186 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3187 goto lend;
3191 * My tests seem to show that the windows version does not
3192 * become asynchronous until after this point. And anyhow
3193 * if this call was asynchronous then how would you get the
3194 * necessary HINTERNET pointer returned by this function.
3197 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3198 lpszVersion, lpszReferrer, lpszAcceptTypes,
3199 dwFlags, dwContext, &handle);
3200 lend:
3201 if( session )
3202 WININET_Release( &session->hdr );
3203 TRACE("returning %p\n", handle);
3204 if(res != ERROR_SUCCESS)
3205 SetLastError(res);
3206 return handle;
3209 static const LPCWSTR header_lookup[] = {
3210 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3211 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3212 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3213 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3214 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3215 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3216 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3217 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3218 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3219 szDate, /* HTTP_QUERY_DATE = 9 */
3220 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3221 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3222 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3223 szURI, /* HTTP_QUERY_URI = 13 */
3224 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3225 NULL, /* HTTP_QUERY_COST = 15 */
3226 NULL, /* HTTP_QUERY_LINK = 16 */
3227 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3228 NULL, /* HTTP_QUERY_VERSION = 18 */
3229 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3230 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3231 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3232 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3233 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3234 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3235 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3236 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3237 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3238 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3239 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3240 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3241 NULL, /* HTTP_QUERY_FROM = 31 */
3242 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3243 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3244 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3245 szReferer, /* HTTP_QUERY_REFERER = 35 */
3246 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3247 szServer, /* HTTP_QUERY_SERVER = 37 */
3248 NULL, /* HTTP_TITLE = 38 */
3249 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3250 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3251 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3252 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3253 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3254 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3255 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3256 NULL, /* HTTP_QUERY_REFRESH = 46 */
3257 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3258 szAge, /* HTTP_QUERY_AGE = 48 */
3259 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3260 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3261 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3262 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3263 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3264 szETag, /* HTTP_QUERY_ETAG = 54 */
3265 hostW, /* HTTP_QUERY_HOST = 55 */
3266 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3267 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3268 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3269 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3270 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3271 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3272 szRange, /* HTTP_QUERY_RANGE = 62 */
3273 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3274 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3275 szVary, /* HTTP_QUERY_VARY = 65 */
3276 szVia, /* HTTP_QUERY_VIA = 66 */
3277 szWarning, /* HTTP_QUERY_WARNING = 67 */
3278 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3279 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3280 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3283 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3285 /***********************************************************************
3286 * HTTP_HttpQueryInfoW (internal)
3288 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3289 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3291 LPHTTPHEADERW lphttpHdr = NULL;
3292 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3293 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3294 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3295 INT index = -1;
3297 /* Find requested header structure */
3298 switch (level)
3300 case HTTP_QUERY_CUSTOM:
3301 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3302 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3303 break;
3304 case HTTP_QUERY_RAW_HEADERS_CRLF:
3306 LPWSTR headers;
3307 DWORD len = 0;
3308 DWORD res = ERROR_INVALID_PARAMETER;
3310 if (request_only)
3311 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3312 else
3313 headers = request->rawHeaders;
3315 if (headers)
3316 len = strlenW(headers) * sizeof(WCHAR);
3318 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3320 len += sizeof(WCHAR);
3321 res = ERROR_INSUFFICIENT_BUFFER;
3323 else if (lpBuffer)
3325 if (headers)
3326 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3327 else
3329 len = strlenW(szCrLf) * sizeof(WCHAR);
3330 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3332 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3333 res = ERROR_SUCCESS;
3335 *lpdwBufferLength = len;
3337 if (request_only) heap_free(headers);
3338 return res;
3340 case HTTP_QUERY_RAW_HEADERS:
3342 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3343 DWORD i, size = 0;
3344 LPWSTR pszString = lpBuffer;
3346 for (i = 0; ppszRawHeaderLines[i]; i++)
3347 size += strlenW(ppszRawHeaderLines[i]) + 1;
3349 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3351 HTTP_FreeTokens(ppszRawHeaderLines);
3352 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3353 return ERROR_INSUFFICIENT_BUFFER;
3355 if (pszString)
3357 for (i = 0; ppszRawHeaderLines[i]; i++)
3359 DWORD len = strlenW(ppszRawHeaderLines[i]);
3360 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3361 pszString += len+1;
3363 *pszString = '\0';
3364 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3366 *lpdwBufferLength = size * sizeof(WCHAR);
3367 HTTP_FreeTokens(ppszRawHeaderLines);
3369 return ERROR_SUCCESS;
3371 case HTTP_QUERY_STATUS_TEXT:
3372 if (request->statusText)
3374 DWORD len = strlenW(request->statusText);
3375 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3377 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3378 return ERROR_INSUFFICIENT_BUFFER;
3380 if (lpBuffer)
3382 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3383 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3385 *lpdwBufferLength = len * sizeof(WCHAR);
3386 return ERROR_SUCCESS;
3388 break;
3389 case HTTP_QUERY_VERSION:
3390 if (request->version)
3392 DWORD len = strlenW(request->version);
3393 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3395 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3396 return ERROR_INSUFFICIENT_BUFFER;
3398 if (lpBuffer)
3400 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3401 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3403 *lpdwBufferLength = len * sizeof(WCHAR);
3404 return ERROR_SUCCESS;
3406 break;
3407 case HTTP_QUERY_CONTENT_ENCODING:
3408 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3409 requested_index,request_only);
3410 break;
3411 default:
3412 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3414 if (level < LAST_TABLE_HEADER && header_lookup[level])
3415 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3416 requested_index,request_only);
3419 if (index >= 0)
3420 lphttpHdr = &request->custHeaders[index];
3422 /* Ensure header satisfies requested attributes */
3423 if (!lphttpHdr ||
3424 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3425 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3427 return ERROR_HTTP_HEADER_NOT_FOUND;
3430 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3432 /* coalesce value to requested type */
3433 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3435 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3436 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3438 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3440 time_t tmpTime;
3441 struct tm tmpTM;
3442 SYSTEMTIME *STHook;
3444 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3446 tmpTM = *gmtime(&tmpTime);
3447 STHook = (SYSTEMTIME *)lpBuffer;
3448 STHook->wDay = tmpTM.tm_mday;
3449 STHook->wHour = tmpTM.tm_hour;
3450 STHook->wMilliseconds = 0;
3451 STHook->wMinute = tmpTM.tm_min;
3452 STHook->wDayOfWeek = tmpTM.tm_wday;
3453 STHook->wMonth = tmpTM.tm_mon + 1;
3454 STHook->wSecond = tmpTM.tm_sec;
3455 STHook->wYear = tmpTM.tm_year;
3457 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3458 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3459 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3461 else if (lphttpHdr->lpszValue)
3463 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3465 if (len > *lpdwBufferLength)
3467 *lpdwBufferLength = len;
3468 return ERROR_INSUFFICIENT_BUFFER;
3470 if (lpBuffer)
3472 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3473 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3475 *lpdwBufferLength = len - sizeof(WCHAR);
3477 return ERROR_SUCCESS;
3480 /***********************************************************************
3481 * HttpQueryInfoW (WININET.@)
3483 * Queries for information about an HTTP request
3485 * RETURNS
3486 * TRUE on success
3487 * FALSE on failure
3490 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3491 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3493 http_request_t *request;
3494 DWORD res;
3496 if (TRACE_ON(wininet)) {
3497 #define FE(x) { x, #x }
3498 static const wininet_flag_info query_flags[] = {
3499 FE(HTTP_QUERY_MIME_VERSION),
3500 FE(HTTP_QUERY_CONTENT_TYPE),
3501 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3502 FE(HTTP_QUERY_CONTENT_ID),
3503 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3504 FE(HTTP_QUERY_CONTENT_LENGTH),
3505 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3506 FE(HTTP_QUERY_ALLOW),
3507 FE(HTTP_QUERY_PUBLIC),
3508 FE(HTTP_QUERY_DATE),
3509 FE(HTTP_QUERY_EXPIRES),
3510 FE(HTTP_QUERY_LAST_MODIFIED),
3511 FE(HTTP_QUERY_MESSAGE_ID),
3512 FE(HTTP_QUERY_URI),
3513 FE(HTTP_QUERY_DERIVED_FROM),
3514 FE(HTTP_QUERY_COST),
3515 FE(HTTP_QUERY_LINK),
3516 FE(HTTP_QUERY_PRAGMA),
3517 FE(HTTP_QUERY_VERSION),
3518 FE(HTTP_QUERY_STATUS_CODE),
3519 FE(HTTP_QUERY_STATUS_TEXT),
3520 FE(HTTP_QUERY_RAW_HEADERS),
3521 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3522 FE(HTTP_QUERY_CONNECTION),
3523 FE(HTTP_QUERY_ACCEPT),
3524 FE(HTTP_QUERY_ACCEPT_CHARSET),
3525 FE(HTTP_QUERY_ACCEPT_ENCODING),
3526 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3527 FE(HTTP_QUERY_AUTHORIZATION),
3528 FE(HTTP_QUERY_CONTENT_ENCODING),
3529 FE(HTTP_QUERY_FORWARDED),
3530 FE(HTTP_QUERY_FROM),
3531 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3532 FE(HTTP_QUERY_LOCATION),
3533 FE(HTTP_QUERY_ORIG_URI),
3534 FE(HTTP_QUERY_REFERER),
3535 FE(HTTP_QUERY_RETRY_AFTER),
3536 FE(HTTP_QUERY_SERVER),
3537 FE(HTTP_QUERY_TITLE),
3538 FE(HTTP_QUERY_USER_AGENT),
3539 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3540 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3541 FE(HTTP_QUERY_ACCEPT_RANGES),
3542 FE(HTTP_QUERY_SET_COOKIE),
3543 FE(HTTP_QUERY_COOKIE),
3544 FE(HTTP_QUERY_REQUEST_METHOD),
3545 FE(HTTP_QUERY_REFRESH),
3546 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3547 FE(HTTP_QUERY_AGE),
3548 FE(HTTP_QUERY_CACHE_CONTROL),
3549 FE(HTTP_QUERY_CONTENT_BASE),
3550 FE(HTTP_QUERY_CONTENT_LOCATION),
3551 FE(HTTP_QUERY_CONTENT_MD5),
3552 FE(HTTP_QUERY_CONTENT_RANGE),
3553 FE(HTTP_QUERY_ETAG),
3554 FE(HTTP_QUERY_HOST),
3555 FE(HTTP_QUERY_IF_MATCH),
3556 FE(HTTP_QUERY_IF_NONE_MATCH),
3557 FE(HTTP_QUERY_IF_RANGE),
3558 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3559 FE(HTTP_QUERY_MAX_FORWARDS),
3560 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3561 FE(HTTP_QUERY_RANGE),
3562 FE(HTTP_QUERY_TRANSFER_ENCODING),
3563 FE(HTTP_QUERY_UPGRADE),
3564 FE(HTTP_QUERY_VARY),
3565 FE(HTTP_QUERY_VIA),
3566 FE(HTTP_QUERY_WARNING),
3567 FE(HTTP_QUERY_CUSTOM)
3569 static const wininet_flag_info modifier_flags[] = {
3570 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3571 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3572 FE(HTTP_QUERY_FLAG_NUMBER),
3573 FE(HTTP_QUERY_FLAG_COALESCE)
3575 #undef FE
3576 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3577 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3578 DWORD i;
3580 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3581 TRACE(" Attribute:");
3582 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3583 if (query_flags[i].val == info) {
3584 TRACE(" %s", query_flags[i].name);
3585 break;
3588 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3589 TRACE(" Unknown (%08x)", info);
3592 TRACE(" Modifier:");
3593 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3594 if (modifier_flags[i].val & info_mod) {
3595 TRACE(" %s", modifier_flags[i].name);
3596 info_mod &= ~ modifier_flags[i].val;
3600 if (info_mod) {
3601 TRACE(" Unknown (%08x)", info_mod);
3603 TRACE("\n");
3606 request = (http_request_t*) get_handle_object( hHttpRequest );
3607 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3609 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3610 goto lend;
3613 if (lpBuffer == NULL)
3614 *lpdwBufferLength = 0;
3615 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3616 lpBuffer, lpdwBufferLength, lpdwIndex);
3618 lend:
3619 if( request )
3620 WININET_Release( &request->hdr );
3622 TRACE("%u <--\n", res);
3623 if(res != ERROR_SUCCESS)
3624 SetLastError(res);
3625 return res == ERROR_SUCCESS;
3628 /***********************************************************************
3629 * HttpQueryInfoA (WININET.@)
3631 * Queries for information about an HTTP request
3633 * RETURNS
3634 * TRUE on success
3635 * FALSE on failure
3638 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3639 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3641 BOOL result;
3642 DWORD len;
3643 WCHAR* bufferW;
3645 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3646 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3648 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3649 lpdwBufferLength, lpdwIndex );
3652 if (lpBuffer)
3654 DWORD alloclen;
3655 len = (*lpdwBufferLength)*sizeof(WCHAR);
3656 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3658 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3659 if (alloclen < len)
3660 alloclen = len;
3662 else
3663 alloclen = len;
3664 bufferW = heap_alloc(alloclen);
3665 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3666 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3667 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3668 } else
3670 bufferW = NULL;
3671 len = 0;
3674 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3675 &len, lpdwIndex );
3676 if( result )
3678 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3679 lpBuffer, *lpdwBufferLength, NULL, NULL );
3680 *lpdwBufferLength = len - 1;
3682 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3684 else
3685 /* since the strings being returned from HttpQueryInfoW should be
3686 * only ASCII characters, it is reasonable to assume that all of
3687 * the Unicode characters can be reduced to a single byte */
3688 *lpdwBufferLength = len / sizeof(WCHAR);
3690 heap_free( bufferW );
3691 return result;
3694 /***********************************************************************
3695 * HTTP_GetRedirectURL (internal)
3697 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3699 static WCHAR szHttp[] = {'h','t','t','p',0};
3700 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3701 http_session_t *session = request->session;
3702 URL_COMPONENTSW urlComponents;
3703 DWORD url_length = 0;
3704 LPWSTR orig_url;
3705 LPWSTR combined_url;
3707 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3708 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3709 urlComponents.dwSchemeLength = 0;
3710 urlComponents.lpszHostName = session->hostName;
3711 urlComponents.dwHostNameLength = 0;
3712 urlComponents.nPort = session->hostPort;
3713 urlComponents.lpszUserName = session->userName;
3714 urlComponents.dwUserNameLength = 0;
3715 urlComponents.lpszPassword = NULL;
3716 urlComponents.dwPasswordLength = 0;
3717 urlComponents.lpszUrlPath = request->path;
3718 urlComponents.dwUrlPathLength = 0;
3719 urlComponents.lpszExtraInfo = NULL;
3720 urlComponents.dwExtraInfoLength = 0;
3722 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3723 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3724 return NULL;
3726 orig_url = heap_alloc(url_length);
3728 /* convert from bytes to characters */
3729 url_length = url_length / sizeof(WCHAR) - 1;
3730 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3732 heap_free(orig_url);
3733 return NULL;
3736 url_length = 0;
3737 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3738 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3740 heap_free(orig_url);
3741 return NULL;
3743 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3745 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3747 heap_free(orig_url);
3748 heap_free(combined_url);
3749 return NULL;
3751 heap_free(orig_url);
3752 return combined_url;
3756 /***********************************************************************
3757 * HTTP_HandleRedirect (internal)
3759 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3761 http_session_t *session = request->session;
3762 appinfo_t *hIC = session->appInfo;
3763 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3764 WCHAR path[INTERNET_MAX_URL_LENGTH];
3765 int index;
3767 if(lpszUrl[0]=='/')
3769 /* if it's an absolute path, keep the same session info */
3770 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3772 else
3774 URL_COMPONENTSW urlComponents;
3775 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3776 static WCHAR szHttp[] = {'h','t','t','p',0};
3777 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3779 userName[0] = 0;
3780 hostName[0] = 0;
3781 protocol[0] = 0;
3783 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3784 urlComponents.lpszScheme = protocol;
3785 urlComponents.dwSchemeLength = 32;
3786 urlComponents.lpszHostName = hostName;
3787 urlComponents.dwHostNameLength = MAXHOSTNAME;
3788 urlComponents.lpszUserName = userName;
3789 urlComponents.dwUserNameLength = 1024;
3790 urlComponents.lpszPassword = NULL;
3791 urlComponents.dwPasswordLength = 0;
3792 urlComponents.lpszUrlPath = path;
3793 urlComponents.dwUrlPathLength = 2048;
3794 urlComponents.lpszExtraInfo = NULL;
3795 urlComponents.dwExtraInfoLength = 0;
3796 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3797 return INTERNET_GetLastError();
3799 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3800 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3802 TRACE("redirect from secure page to non-secure page\n");
3803 /* FIXME: warn about from secure redirect to non-secure page */
3804 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3806 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3807 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3809 TRACE("redirect from non-secure page to secure page\n");
3810 /* FIXME: notify about redirect to secure page */
3811 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3814 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3816 if (lstrlenW(protocol)>4) /*https*/
3817 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3818 else /*http*/
3819 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3822 #if 0
3824 * This upsets redirects to binary files on sourceforge.net
3825 * and gives an html page instead of the target file
3826 * Examination of the HTTP request sent by native wininet.dll
3827 * reveals that it doesn't send a referrer in that case.
3828 * Maybe there's a flag that enables this, or maybe a referrer
3829 * shouldn't be added in case of a redirect.
3832 /* consider the current host as the referrer */
3833 if (session->lpszServerName && *session->lpszServerName)
3834 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3835 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3836 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3837 #endif
3839 heap_free(session->hostName);
3840 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3841 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3843 int len;
3844 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3845 len = lstrlenW(hostName);
3846 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3847 session->hostName = heap_alloc(len*sizeof(WCHAR));
3848 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3850 else
3851 session->hostName = heap_strdupW(hostName);
3853 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3855 heap_free(session->userName);
3856 session->userName = NULL;
3857 if (userName[0])
3858 session->userName = heap_strdupW(userName);
3860 reset_data_stream(request);
3862 if(!using_proxy) {
3863 if(strcmpiW(session->serverName, hostName)) {
3864 heap_free(session->serverName);
3865 session->serverName = heap_strdupW(hostName);
3867 session->serverPort = urlComponents.nPort;
3870 heap_free(request->path);
3871 request->path=NULL;
3872 if (*path)
3874 DWORD needed = 0;
3875 HRESULT rc;
3877 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3878 if (rc != E_POINTER)
3879 needed = strlenW(path)+1;
3880 request->path = heap_alloc(needed*sizeof(WCHAR));
3881 rc = UrlEscapeW(path, request->path, &needed,
3882 URL_ESCAPE_SPACES_ONLY);
3883 if (rc != S_OK)
3885 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3886 strcpyW(request->path,path);
3890 /* Remove custom content-type/length headers on redirects. */
3891 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3892 if (0 <= index)
3893 HTTP_DeleteCustomHeader(request, index);
3894 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3895 if (0 <= index)
3896 HTTP_DeleteCustomHeader(request, index);
3898 return ERROR_SUCCESS;
3901 /***********************************************************************
3902 * HTTP_build_req (internal)
3904 * concatenate all the strings in the request together
3906 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3908 LPCWSTR *t;
3909 LPWSTR str;
3911 for( t = list; *t ; t++ )
3912 len += strlenW( *t );
3913 len++;
3915 str = heap_alloc(len*sizeof(WCHAR));
3916 *str = 0;
3918 for( t = list; *t ; t++ )
3919 strcatW( str, *t );
3921 return str;
3924 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3926 LPWSTR lpszPath;
3927 LPWSTR requestString;
3928 INT len;
3929 INT cnt;
3930 INT responseLen;
3931 char *ascii_req;
3932 DWORD res;
3933 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3934 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3935 http_session_t *session = request->session;
3937 TRACE("\n");
3939 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3940 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3941 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3942 heap_free( lpszPath );
3944 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3945 NULL, 0, NULL, NULL );
3946 len--; /* the nul terminator isn't needed */
3947 ascii_req = heap_alloc(len);
3948 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3949 heap_free( requestString );
3951 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3953 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3954 heap_free( ascii_req );
3955 if (res != ERROR_SUCCESS)
3956 return res;
3958 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3959 if (!responseLen)
3960 return ERROR_HTTP_INVALID_HEADER;
3962 return ERROR_SUCCESS;
3965 static void HTTP_InsertCookies(http_request_t *request)
3967 DWORD cookie_size, size, cnt = 0;
3968 HTTPHEADERW *host;
3969 WCHAR *cookies;
3971 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3973 host = HTTP_GetHeader(request, hostW);
3974 if(!host)
3975 return;
3977 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3978 return;
3980 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
3981 if(!(cookies = heap_alloc(size)))
3982 return;
3984 cnt += sprintfW(cookies, cookieW);
3985 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
3986 strcatW(cookies, szCrLf);
3988 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
3990 heap_free(cookies);
3993 static WORD HTTP_ParseDay(LPCWSTR day)
3995 static const WCHAR days[7][4] = {{ 's','u','n',0 },
3996 { 'm','o','n',0 },
3997 { 't','u','e',0 },
3998 { 'w','e','d',0 },
3999 { 't','h','u',0 },
4000 { 'f','r','i',0 },
4001 { 's','a','t',0 }};
4002 int i;
4003 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4004 if (!strcmpiW(day, days[i]))
4005 return i;
4007 /* Invalid */
4008 return 7;
4011 static WORD HTTP_ParseMonth(LPCWSTR month)
4013 static const WCHAR jan[] = { 'j','a','n',0 };
4014 static const WCHAR feb[] = { 'f','e','b',0 };
4015 static const WCHAR mar[] = { 'm','a','r',0 };
4016 static const WCHAR apr[] = { 'a','p','r',0 };
4017 static const WCHAR may[] = { 'm','a','y',0 };
4018 static const WCHAR jun[] = { 'j','u','n',0 };
4019 static const WCHAR jul[] = { 'j','u','l',0 };
4020 static const WCHAR aug[] = { 'a','u','g',0 };
4021 static const WCHAR sep[] = { 's','e','p',0 };
4022 static const WCHAR oct[] = { 'o','c','t',0 };
4023 static const WCHAR nov[] = { 'n','o','v',0 };
4024 static const WCHAR dec[] = { 'd','e','c',0 };
4026 if (!strcmpiW(month, jan)) return 1;
4027 if (!strcmpiW(month, feb)) return 2;
4028 if (!strcmpiW(month, mar)) return 3;
4029 if (!strcmpiW(month, apr)) return 4;
4030 if (!strcmpiW(month, may)) return 5;
4031 if (!strcmpiW(month, jun)) return 6;
4032 if (!strcmpiW(month, jul)) return 7;
4033 if (!strcmpiW(month, aug)) return 8;
4034 if (!strcmpiW(month, sep)) return 9;
4035 if (!strcmpiW(month, oct)) return 10;
4036 if (!strcmpiW(month, nov)) return 11;
4037 if (!strcmpiW(month, dec)) return 12;
4038 /* Invalid */
4039 return 0;
4042 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4043 * optionally preceded by whitespace.
4044 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4045 * st, and sets *str to the first character after the time format.
4047 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4049 LPCWSTR ptr = *str;
4050 WCHAR *nextPtr;
4051 unsigned long num;
4053 while (isspaceW(*ptr))
4054 ptr++;
4056 num = strtoulW(ptr, &nextPtr, 10);
4057 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4059 ERR("unexpected time format %s\n", debugstr_w(ptr));
4060 return FALSE;
4062 if (num > 23)
4064 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4065 return FALSE;
4067 ptr = nextPtr + 1;
4068 st->wHour = (WORD)num;
4069 num = strtoulW(ptr, &nextPtr, 10);
4070 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4072 ERR("unexpected time format %s\n", debugstr_w(ptr));
4073 return FALSE;
4075 if (num > 59)
4077 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4078 return FALSE;
4080 ptr = nextPtr + 1;
4081 st->wMinute = (WORD)num;
4082 num = strtoulW(ptr, &nextPtr, 10);
4083 if (!nextPtr || nextPtr <= ptr)
4085 ERR("unexpected time format %s\n", debugstr_w(ptr));
4086 return FALSE;
4088 if (num > 59)
4090 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4091 return FALSE;
4093 ptr = nextPtr + 1;
4094 *str = ptr;
4095 st->wSecond = (WORD)num;
4096 return TRUE;
4099 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4101 static const WCHAR gmt[]= { 'G','M','T',0 };
4102 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4103 LPCWSTR ptr;
4104 SYSTEMTIME st = { 0 };
4105 unsigned long num;
4107 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4108 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4109 *dayPtr = *ptr;
4110 *dayPtr = 0;
4111 st.wDayOfWeek = HTTP_ParseDay(day);
4112 if (st.wDayOfWeek >= 7)
4114 ERR("unexpected weekday %s\n", debugstr_w(day));
4115 return FALSE;
4118 while (isspaceW(*ptr))
4119 ptr++;
4121 for (monthPtr = month; !isspace(*ptr) &&
4122 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4123 monthPtr++, ptr++)
4124 *monthPtr = *ptr;
4125 *monthPtr = 0;
4126 st.wMonth = HTTP_ParseMonth(month);
4127 if (!st.wMonth || st.wMonth > 12)
4129 ERR("unexpected month %s\n", debugstr_w(month));
4130 return FALSE;
4133 while (isspaceW(*ptr))
4134 ptr++;
4136 num = strtoulW(ptr, &nextPtr, 10);
4137 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4139 ERR("unexpected day %s\n", debugstr_w(ptr));
4140 return FALSE;
4142 ptr = nextPtr;
4143 st.wDay = (WORD)num;
4145 while (isspaceW(*ptr))
4146 ptr++;
4148 if (!HTTP_ParseTime(&st, &ptr))
4149 return FALSE;
4151 while (isspaceW(*ptr))
4152 ptr++;
4154 num = strtoulW(ptr, &nextPtr, 10);
4155 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4157 ERR("unexpected year %s\n", debugstr_w(ptr));
4158 return FALSE;
4160 ptr = nextPtr;
4161 st.wYear = (WORD)num;
4163 while (isspaceW(*ptr))
4164 ptr++;
4166 /* asctime() doesn't report a timezone, but some web servers do, so accept
4167 * with or without GMT.
4169 if (*ptr && strcmpW(ptr, gmt))
4171 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4172 return FALSE;
4174 return SystemTimeToFileTime(&st, ft);
4177 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4179 static const WCHAR gmt[]= { 'G','M','T',0 };
4180 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4181 LPCWSTR ptr;
4182 unsigned long num;
4183 SYSTEMTIME st = { 0 };
4185 ptr = strchrW(value, ',');
4186 if (!ptr)
4187 return FALSE;
4188 if (ptr - value != 3)
4190 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4191 return FALSE;
4193 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4194 day[3] = 0;
4195 st.wDayOfWeek = HTTP_ParseDay(day);
4196 if (st.wDayOfWeek > 6)
4198 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4199 return FALSE;
4201 ptr++;
4203 while (isspaceW(*ptr))
4204 ptr++;
4206 num = strtoulW(ptr, &nextPtr, 10);
4207 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4209 ERR("unexpected day %s\n", debugstr_w(value));
4210 return FALSE;
4212 ptr = nextPtr;
4213 st.wDay = (WORD)num;
4215 while (isspaceW(*ptr))
4216 ptr++;
4218 for (monthPtr = month; !isspace(*ptr) &&
4219 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4220 monthPtr++, ptr++)
4221 *monthPtr = *ptr;
4222 *monthPtr = 0;
4223 st.wMonth = HTTP_ParseMonth(month);
4224 if (!st.wMonth || st.wMonth > 12)
4226 ERR("unexpected month %s\n", debugstr_w(month));
4227 return FALSE;
4230 while (isspaceW(*ptr))
4231 ptr++;
4233 num = strtoulW(ptr, &nextPtr, 10);
4234 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4236 ERR("unexpected year %s\n", debugstr_w(value));
4237 return FALSE;
4239 ptr = nextPtr;
4240 st.wYear = (WORD)num;
4242 if (!HTTP_ParseTime(&st, &ptr))
4243 return FALSE;
4245 while (isspaceW(*ptr))
4246 ptr++;
4248 if (strcmpW(ptr, gmt))
4250 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4251 return FALSE;
4253 return SystemTimeToFileTime(&st, ft);
4256 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4257 * which may not be the only formats actually seen in the wild.
4258 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4259 * should be accepted as well.
4261 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4263 static const WCHAR zero[] = { '0',0 };
4264 BOOL ret;
4266 if (!strcmpW(value, zero))
4268 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4269 ret = TRUE;
4271 else if (strchrW(value, ','))
4272 ret = HTTP_ParseRfc1123Date(value, ft);
4273 else
4275 ret = HTTP_ParseDateAsAsctime(value, ft);
4276 if (!ret)
4277 ERR("unexpected date format %s\n", debugstr_w(value));
4279 return ret;
4282 static void HTTP_ProcessExpires(http_request_t *request)
4284 BOOL expirationFound = FALSE;
4285 int headerIndex;
4287 /* Look for a Cache-Control header with a max-age directive, as it takes
4288 * precedence over the Expires header.
4290 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4291 if (headerIndex != -1)
4293 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4294 LPWSTR ptr;
4296 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4298 LPWSTR comma = strchrW(ptr, ','), end, equal;
4300 if (comma)
4301 end = comma;
4302 else
4303 end = ptr + strlenW(ptr);
4304 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4306 if (*equal == '=')
4308 static const WCHAR max_age[] = {
4309 'm','a','x','-','a','g','e',0 };
4311 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4313 LPWSTR nextPtr;
4314 unsigned long age;
4316 age = strtoulW(equal + 1, &nextPtr, 10);
4317 if (nextPtr > equal + 1)
4319 LARGE_INTEGER ft;
4321 NtQuerySystemTime( &ft );
4322 /* Age is in seconds, FILETIME resolution is in
4323 * 100 nanosecond intervals.
4325 ft.QuadPart += age * (ULONGLONG)1000000;
4326 request->expires.dwLowDateTime = ft.u.LowPart;
4327 request->expires.dwHighDateTime = ft.u.HighPart;
4328 expirationFound = TRUE;
4332 if (comma)
4334 ptr = comma + 1;
4335 while (isspaceW(*ptr))
4336 ptr++;
4338 else
4339 ptr = NULL;
4342 if (!expirationFound)
4344 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4345 if (headerIndex != -1)
4347 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4348 FILETIME ft;
4350 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4352 expirationFound = TRUE;
4353 request->expires = ft;
4357 if (!expirationFound)
4359 LARGE_INTEGER t;
4361 /* With no known age, default to 10 minutes until expiration. */
4362 NtQuerySystemTime( &t );
4363 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4364 request->expires.dwLowDateTime = t.u.LowPart;
4365 request->expires.dwHighDateTime = t.u.HighPart;
4369 static void HTTP_ProcessLastModified(http_request_t *request)
4371 int headerIndex;
4373 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4374 if (headerIndex != -1)
4376 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4377 FILETIME ft;
4379 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4380 request->last_modified = ft;
4384 static void http_process_keep_alive(http_request_t *req)
4386 int index;
4388 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4389 if(index != -1)
4390 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4391 else
4392 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4395 static void HTTP_CacheRequest(http_request_t *request)
4397 WCHAR url[INTERNET_MAX_URL_LENGTH];
4398 WCHAR cacheFileName[MAX_PATH+1];
4399 BOOL b;
4401 b = HTTP_GetRequestURL(request, url);
4402 if(!b) {
4403 WARN("Could not get URL\n");
4404 return;
4407 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4408 if(b) {
4409 heap_free(request->cacheFile);
4410 CloseHandle(request->hCacheFile);
4412 request->cacheFile = heap_strdupW(cacheFileName);
4413 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4414 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4415 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4416 WARN("Could not create file: %u\n", GetLastError());
4417 request->hCacheFile = NULL;
4419 }else {
4420 WARN("Could not create cache entry: %08x\n", GetLastError());
4424 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4426 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4427 http_session_t *session = request->session;
4428 netconn_t *netconn = NULL;
4429 server_t *server;
4430 DWORD res;
4432 assert(!request->netconn);
4433 reset_data_stream(request);
4435 server = get_server(session->serverName, session->serverPort);
4436 if(!server)
4437 return ERROR_OUTOFMEMORY;
4439 res = HTTP_ResolveName(request, server);
4440 if(res != ERROR_SUCCESS) {
4441 server_release(server);
4442 return res;
4445 EnterCriticalSection(&connection_pool_cs);
4447 while(!list_empty(&server->conn_pool)) {
4448 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4449 list_remove(&netconn->pool_entry);
4451 if(NETCON_is_alive(netconn))
4452 break;
4454 TRACE("connection %p closed during idle\n", netconn);
4455 free_netconn(netconn);
4456 netconn = NULL;
4459 LeaveCriticalSection(&connection_pool_cs);
4461 if(netconn) {
4462 TRACE("<-- reusing %p netconn\n", netconn);
4463 request->netconn = netconn;
4464 *reusing = TRUE;
4465 return ERROR_SUCCESS;
4468 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4469 INTERNET_STATUS_CONNECTING_TO_SERVER,
4470 server->addr_str,
4471 strlen(server->addr_str)+1);
4473 res = create_netconn(is_https, server, request->security_flags, &netconn);
4474 server_release(server);
4475 if(res != ERROR_SUCCESS) {
4476 ERR("create_netconn failed: %u\n", res);
4477 return res;
4480 request->netconn = netconn;
4482 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4483 INTERNET_STATUS_CONNECTED_TO_SERVER,
4484 server->addr_str, strlen(server->addr_str)+1);
4486 if(is_https) {
4487 /* Note: we differ from Microsoft's WinINet here. they seem to have
4488 * a bug that causes no status callbacks to be sent when starting
4489 * a tunnel to a proxy server using the CONNECT verb. i believe our
4490 * behaviour to be more correct and to not cause any incompatibilities
4491 * because using a secure connection through a proxy server is a rare
4492 * case that would be hard for anyone to depend on */
4493 if(session->appInfo->proxy)
4494 res = HTTP_SecureProxyConnect(request);
4495 if(res == ERROR_SUCCESS)
4496 res = NETCON_secure_connect(request->netconn, session->hostName);
4497 if(res != ERROR_SUCCESS)
4499 WARN("Couldn't connect securely to host\n");
4501 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4502 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4503 || res == ERROR_INTERNET_INVALID_CA
4504 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4505 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4506 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4507 || res == ERROR_INTERNET_SEC_INVALID_CERT
4508 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4509 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4513 if(res != ERROR_SUCCESS) {
4514 http_release_netconn(request, FALSE);
4515 return res;
4518 *reusing = FALSE;
4519 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4520 return ERROR_SUCCESS;
4523 /***********************************************************************
4524 * HTTP_HttpSendRequestW (internal)
4526 * Sends the specified request to the HTTP server
4528 * RETURNS
4529 * ERROR_SUCCESS on success
4530 * win32 error code on failure
4533 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4534 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4535 DWORD dwContentLength, BOOL bEndRequest)
4537 INT cnt;
4538 BOOL redirected = FALSE;
4539 LPWSTR requestString = NULL;
4540 INT responseLen;
4541 BOOL loop_next;
4542 INTERNET_ASYNC_RESULT iar;
4543 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4544 static const WCHAR szContentLength[] =
4545 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4546 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4547 DWORD res;
4549 TRACE("--> %p\n", request);
4551 assert(request->hdr.htype == WH_HHTTPREQ);
4553 /* if the verb is NULL default to GET */
4554 if (!request->verb)
4555 request->verb = heap_strdupW(szGET);
4557 if (dwContentLength || strcmpW(request->verb, szGET))
4559 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4560 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4561 request->bytesToWrite = dwContentLength;
4563 if (request->session->appInfo->agent)
4565 WCHAR *agent_header;
4566 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4567 int len;
4569 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4570 agent_header = heap_alloc(len * sizeof(WCHAR));
4571 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4573 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4574 heap_free(agent_header);
4576 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4578 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4579 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4581 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4583 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4584 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4585 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4590 DWORD len;
4591 BOOL reusing_connection;
4592 char *ascii_req;
4594 loop_next = FALSE;
4596 /* like native, just in case the caller forgot to call InternetReadFile
4597 * for all the data */
4598 drain_content(request);
4599 if(redirected) {
4600 request->contentLength = ~0u;
4601 request->bytesToWrite = 0;
4604 if (TRACE_ON(wininet))
4606 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4607 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4610 HTTP_FixURL(request);
4611 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4613 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4615 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4616 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4618 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4619 HTTP_InsertCookies(request);
4621 /* add the headers the caller supplied */
4622 if( lpszHeaders && dwHeaderLength )
4624 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4625 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4628 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4630 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4631 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4632 heap_free(url);
4634 else
4635 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4638 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4640 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4641 break;
4643 /* send the request as ASCII, tack on the optional data */
4644 if (!lpOptional || redirected)
4645 dwOptionalLength = 0;
4646 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4647 NULL, 0, NULL, NULL );
4648 ascii_req = heap_alloc(len + dwOptionalLength);
4649 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4650 ascii_req, len, NULL, NULL );
4651 if( lpOptional )
4652 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4653 len = (len + dwOptionalLength - 1);
4654 ascii_req[len] = 0;
4655 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4657 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4658 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4660 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4661 heap_free( ascii_req );
4662 if(res != ERROR_SUCCESS) {
4663 TRACE("send failed: %u\n", res);
4664 if(!reusing_connection)
4665 break;
4666 http_release_netconn(request, FALSE);
4667 loop_next = TRUE;
4668 continue;
4671 request->bytesWritten = dwOptionalLength;
4673 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4674 INTERNET_STATUS_REQUEST_SENT,
4675 &len, sizeof(DWORD));
4677 if (bEndRequest)
4679 DWORD dwBufferSize;
4680 DWORD dwStatusCode;
4682 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4683 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4685 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4686 /* FIXME: We should know that connection is closed before sending
4687 * headers. Otherwise wrong callbacks are executed */
4688 if(!responseLen && reusing_connection) {
4689 TRACE("Connection closed by server, reconnecting\n");
4690 http_release_netconn(request, FALSE);
4691 loop_next = TRUE;
4692 continue;
4695 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4696 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4697 sizeof(DWORD));
4699 http_process_keep_alive(request);
4700 HTTP_ProcessCookies(request);
4701 HTTP_ProcessExpires(request);
4702 HTTP_ProcessLastModified(request);
4704 dwBufferSize = sizeof(dwStatusCode);
4705 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4706 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4707 dwStatusCode = 0;
4709 res = set_content_length(request, dwStatusCode);
4710 if(res != ERROR_SUCCESS)
4711 goto lend;
4712 if(!request->contentLength)
4713 http_release_netconn(request, TRUE);
4715 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4717 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4718 dwBufferSize=sizeof(szNewLocation);
4719 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4720 dwStatusCode == HTTP_STATUS_MOVED ||
4721 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4722 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4723 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4725 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4726 dwStatusCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4728 heap_free(request->verb);
4729 request->verb = heap_strdupW(szGET);
4731 drain_content(request);
4732 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4734 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4735 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4736 res = HTTP_HandleRedirect(request, new_url);
4737 if (res == ERROR_SUCCESS)
4739 heap_free(requestString);
4740 loop_next = TRUE;
4742 heap_free( new_url );
4744 redirected = TRUE;
4747 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4749 WCHAR szAuthValue[2048];
4750 dwBufferSize=2048;
4751 if (dwStatusCode == HTTP_STATUS_DENIED)
4753 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4754 DWORD dwIndex = 0;
4755 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4757 if (HTTP_DoAuthorization(request, szAuthValue,
4758 &request->authInfo,
4759 request->session->userName,
4760 request->session->password,
4761 Host->lpszValue))
4763 heap_free(requestString);
4764 loop_next = TRUE;
4765 break;
4769 if(!loop_next) {
4770 TRACE("Cleaning wrong authorization data\n");
4771 destroy_authinfo(request->authInfo);
4772 request->authInfo = NULL;
4775 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4777 DWORD dwIndex = 0;
4778 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4780 if (HTTP_DoAuthorization(request, szAuthValue,
4781 &request->proxyAuthInfo,
4782 request->session->appInfo->proxyUsername,
4783 request->session->appInfo->proxyPassword,
4784 NULL))
4786 loop_next = TRUE;
4787 break;
4791 if(!loop_next) {
4792 TRACE("Cleaning wrong proxy authorization data\n");
4793 destroy_authinfo(request->proxyAuthInfo);
4794 request->proxyAuthInfo = NULL;
4799 else
4800 res = ERROR_SUCCESS;
4802 while (loop_next);
4804 if(res == ERROR_SUCCESS)
4805 HTTP_CacheRequest(request);
4807 lend:
4808 heap_free(requestString);
4810 /* TODO: send notification for P3P header */
4812 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4814 if (res == ERROR_SUCCESS && request->contentLength && request->bytesWritten == request->bytesToWrite)
4815 HTTP_ReceiveRequestData(request, TRUE);
4816 else
4818 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4819 iar.dwError = res;
4821 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4822 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4823 sizeof(INTERNET_ASYNC_RESULT));
4827 TRACE("<--\n");
4828 return res;
4831 /***********************************************************************
4833 * Helper functions for the HttpSendRequest(Ex) functions
4836 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4838 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4839 http_request_t *request = (http_request_t*) workRequest->hdr;
4841 TRACE("%p\n", request);
4843 HTTP_HttpSendRequestW(request, req->lpszHeader,
4844 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4845 req->dwContentLength, req->bEndRequest);
4847 heap_free(req->lpszHeader);
4851 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4853 INT responseLen;
4854 DWORD dwCode, dwCodeLength;
4855 DWORD dwBufferSize;
4856 DWORD res = ERROR_SUCCESS;
4858 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4859 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4861 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4862 if (!responseLen)
4863 res = ERROR_HTTP_HEADER_NOT_FOUND;
4865 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4866 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4868 /* process cookies here. Is this right? */
4869 http_process_keep_alive(request);
4870 HTTP_ProcessCookies(request);
4871 HTTP_ProcessExpires(request);
4872 HTTP_ProcessLastModified(request);
4874 dwCodeLength = sizeof(dwCode);
4875 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4876 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
4877 dwCode = 0;
4879 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
4880 if(!request->contentLength)
4881 http_release_netconn(request, TRUE);
4884 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4886 if (dwCode == HTTP_STATUS_REDIRECT ||
4887 dwCode == HTTP_STATUS_MOVED ||
4888 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
4889 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
4891 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4892 dwBufferSize=sizeof(szNewLocation);
4893 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4895 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4896 dwCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4898 heap_free(request->verb);
4899 request->verb = heap_strdupW(szGET);
4901 drain_content(request);
4902 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4904 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4905 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4906 res = HTTP_HandleRedirect(request, new_url);
4907 if (res == ERROR_SUCCESS)
4908 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4909 heap_free( new_url );
4915 if (res == ERROR_SUCCESS && request->contentLength) {
4916 HTTP_ReceiveRequestData(request, TRUE);
4917 }else {
4918 INTERNET_ASYNC_RESULT iar = {0, res};
4920 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4921 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4922 sizeof(INTERNET_ASYNC_RESULT));
4925 return res;
4928 /***********************************************************************
4929 * HttpEndRequestA (WININET.@)
4931 * Ends an HTTP request that was started by HttpSendRequestEx
4933 * RETURNS
4934 * TRUE if successful
4935 * FALSE on failure
4938 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4939 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4941 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4943 if (lpBuffersOut)
4945 SetLastError(ERROR_INVALID_PARAMETER);
4946 return FALSE;
4949 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4952 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4954 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4955 http_request_t *request = (http_request_t*)work->hdr;
4957 TRACE("%p\n", request);
4959 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4962 /***********************************************************************
4963 * HttpEndRequestW (WININET.@)
4965 * Ends an HTTP request that was started by HttpSendRequestEx
4967 * RETURNS
4968 * TRUE if successful
4969 * FALSE on failure
4972 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4973 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4975 http_request_t *request;
4976 DWORD res;
4978 TRACE("-->\n");
4980 if (lpBuffersOut)
4982 SetLastError(ERROR_INVALID_PARAMETER);
4983 return FALSE;
4986 request = (http_request_t*) get_handle_object( hRequest );
4988 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4990 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4991 if (request)
4992 WININET_Release( &request->hdr );
4993 return FALSE;
4995 request->hdr.dwFlags |= dwFlags;
4997 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4999 WORKREQUEST work;
5000 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5002 work.asyncproc = AsyncHttpEndRequestProc;
5003 work.hdr = WININET_AddRef( &request->hdr );
5005 work_endrequest = &work.u.HttpEndRequestW;
5006 work_endrequest->dwFlags = dwFlags;
5007 work_endrequest->dwContext = dwContext;
5009 INTERNET_AsyncCall(&work);
5010 res = ERROR_IO_PENDING;
5012 else
5013 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5015 WININET_Release( &request->hdr );
5016 TRACE("%u <--\n", res);
5017 if(res != ERROR_SUCCESS)
5018 SetLastError(res);
5019 return res == ERROR_SUCCESS;
5022 /***********************************************************************
5023 * HttpSendRequestExA (WININET.@)
5025 * Sends the specified request to the HTTP server and allows chunked
5026 * transfers.
5028 * RETURNS
5029 * Success: TRUE
5030 * Failure: FALSE, call GetLastError() for more information.
5032 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5033 LPINTERNET_BUFFERSA lpBuffersIn,
5034 LPINTERNET_BUFFERSA lpBuffersOut,
5035 DWORD dwFlags, DWORD_PTR dwContext)
5037 INTERNET_BUFFERSW BuffersInW;
5038 BOOL rc = FALSE;
5039 DWORD headerlen;
5040 LPWSTR header = NULL;
5042 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5043 lpBuffersOut, dwFlags, dwContext);
5045 if (lpBuffersIn)
5047 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5048 if (lpBuffersIn->lpcszHeader)
5050 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5051 lpBuffersIn->dwHeadersLength,0,0);
5052 header = heap_alloc(headerlen*sizeof(WCHAR));
5053 if (!(BuffersInW.lpcszHeader = header))
5055 SetLastError(ERROR_OUTOFMEMORY);
5056 return FALSE;
5058 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5059 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5060 header, headerlen);
5062 else
5063 BuffersInW.lpcszHeader = NULL;
5064 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5065 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5066 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5067 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5068 BuffersInW.Next = NULL;
5071 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5073 heap_free(header);
5074 return rc;
5077 /***********************************************************************
5078 * HttpSendRequestExW (WININET.@)
5080 * Sends the specified request to the HTTP server and allows chunked
5081 * transfers
5083 * RETURNS
5084 * Success: TRUE
5085 * Failure: FALSE, call GetLastError() for more information.
5087 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5088 LPINTERNET_BUFFERSW lpBuffersIn,
5089 LPINTERNET_BUFFERSW lpBuffersOut,
5090 DWORD dwFlags, DWORD_PTR dwContext)
5092 http_request_t *request;
5093 http_session_t *session;
5094 appinfo_t *hIC;
5095 DWORD res;
5097 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5098 lpBuffersOut, dwFlags, dwContext);
5100 request = (http_request_t*) get_handle_object( hRequest );
5102 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5104 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5105 goto lend;
5108 session = request->session;
5109 assert(session->hdr.htype == WH_HHTTPSESSION);
5110 hIC = session->appInfo;
5111 assert(hIC->hdr.htype == WH_HINIT);
5113 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5115 WORKREQUEST workRequest;
5116 struct WORKREQ_HTTPSENDREQUESTW *req;
5118 workRequest.asyncproc = AsyncHttpSendRequestProc;
5119 workRequest.hdr = WININET_AddRef( &request->hdr );
5120 req = &workRequest.u.HttpSendRequestW;
5121 if (lpBuffersIn)
5123 DWORD size = 0;
5125 if (lpBuffersIn->lpcszHeader)
5127 if (lpBuffersIn->dwHeadersLength == ~0u)
5128 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5129 else
5130 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5132 req->lpszHeader = heap_alloc(size);
5133 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5135 else req->lpszHeader = NULL;
5137 req->dwHeaderLength = size / sizeof(WCHAR);
5138 req->lpOptional = lpBuffersIn->lpvBuffer;
5139 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5140 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5142 else
5144 req->lpszHeader = NULL;
5145 req->dwHeaderLength = 0;
5146 req->lpOptional = NULL;
5147 req->dwOptionalLength = 0;
5148 req->dwContentLength = 0;
5151 req->bEndRequest = FALSE;
5153 INTERNET_AsyncCall(&workRequest);
5155 * This is from windows.
5157 res = ERROR_IO_PENDING;
5159 else
5161 if (lpBuffersIn)
5162 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5163 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5164 lpBuffersIn->dwBufferTotal, FALSE);
5165 else
5166 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5169 lend:
5170 if ( request )
5171 WININET_Release( &request->hdr );
5173 TRACE("<---\n");
5174 SetLastError(res);
5175 return res == ERROR_SUCCESS;
5178 /***********************************************************************
5179 * HttpSendRequestW (WININET.@)
5181 * Sends the specified request to the HTTP server
5183 * RETURNS
5184 * TRUE on success
5185 * FALSE on failure
5188 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5189 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5191 http_request_t *request;
5192 http_session_t *session = NULL;
5193 appinfo_t *hIC = NULL;
5194 DWORD res = ERROR_SUCCESS;
5196 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5197 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5199 request = (http_request_t*) get_handle_object( hHttpRequest );
5200 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5202 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5203 goto lend;
5206 session = request->session;
5207 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5209 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5210 goto lend;
5213 hIC = session->appInfo;
5214 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5216 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5217 goto lend;
5220 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5222 WORKREQUEST workRequest;
5223 struct WORKREQ_HTTPSENDREQUESTW *req;
5225 workRequest.asyncproc = AsyncHttpSendRequestProc;
5226 workRequest.hdr = WININET_AddRef( &request->hdr );
5227 req = &workRequest.u.HttpSendRequestW;
5228 if (lpszHeaders)
5230 DWORD size;
5232 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5233 else size = dwHeaderLength * sizeof(WCHAR);
5235 req->lpszHeader = heap_alloc(size);
5236 memcpy(req->lpszHeader, lpszHeaders, size);
5238 else
5239 req->lpszHeader = 0;
5240 req->dwHeaderLength = dwHeaderLength;
5241 req->lpOptional = lpOptional;
5242 req->dwOptionalLength = dwOptionalLength;
5243 req->dwContentLength = dwOptionalLength;
5244 req->bEndRequest = TRUE;
5246 INTERNET_AsyncCall(&workRequest);
5248 * This is from windows.
5250 res = ERROR_IO_PENDING;
5252 else
5254 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5255 dwHeaderLength, lpOptional, dwOptionalLength,
5256 dwOptionalLength, TRUE);
5258 lend:
5259 if( request )
5260 WININET_Release( &request->hdr );
5262 SetLastError(res);
5263 return res == ERROR_SUCCESS;
5266 /***********************************************************************
5267 * HttpSendRequestA (WININET.@)
5269 * Sends the specified request to the HTTP server
5271 * RETURNS
5272 * TRUE on success
5273 * FALSE on failure
5276 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5277 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5279 BOOL result;
5280 LPWSTR szHeaders=NULL;
5281 DWORD nLen=dwHeaderLength;
5282 if(lpszHeaders!=NULL)
5284 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5285 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5286 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5288 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5289 heap_free(szHeaders);
5290 return result;
5293 /***********************************************************************
5294 * HTTPSESSION_Destroy (internal)
5296 * Deallocate session handle
5299 static void HTTPSESSION_Destroy(object_header_t *hdr)
5301 http_session_t *session = (http_session_t*) hdr;
5303 TRACE("%p\n", session);
5305 WININET_Release(&session->appInfo->hdr);
5307 heap_free(session->hostName);
5308 heap_free(session->serverName);
5309 heap_free(session->password);
5310 heap_free(session->userName);
5313 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5315 switch(option) {
5316 case INTERNET_OPTION_HANDLE_TYPE:
5317 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5319 if (*size < sizeof(ULONG))
5320 return ERROR_INSUFFICIENT_BUFFER;
5322 *size = sizeof(DWORD);
5323 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5324 return ERROR_SUCCESS;
5327 return INET_QueryOption(hdr, option, buffer, size, unicode);
5330 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5332 http_session_t *ses = (http_session_t*)hdr;
5334 switch(option) {
5335 case INTERNET_OPTION_USERNAME:
5337 heap_free(ses->userName);
5338 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5339 return ERROR_SUCCESS;
5341 case INTERNET_OPTION_PASSWORD:
5343 heap_free(ses->password);
5344 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5345 return ERROR_SUCCESS;
5347 default: break;
5350 return ERROR_INTERNET_INVALID_OPTION;
5353 static const object_vtbl_t HTTPSESSIONVtbl = {
5354 HTTPSESSION_Destroy,
5355 NULL,
5356 HTTPSESSION_QueryOption,
5357 HTTPSESSION_SetOption,
5358 NULL,
5359 NULL,
5360 NULL,
5361 NULL,
5362 NULL
5366 /***********************************************************************
5367 * HTTP_Connect (internal)
5369 * Create http session handle
5371 * RETURNS
5372 * HINTERNET a session handle on success
5373 * NULL on failure
5376 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5377 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5378 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5379 DWORD dwInternalFlags, HINTERNET *ret)
5381 http_session_t *session = NULL;
5383 TRACE("-->\n");
5385 if (!lpszServerName || !lpszServerName[0])
5386 return ERROR_INVALID_PARAMETER;
5388 assert( hIC->hdr.htype == WH_HINIT );
5390 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5391 if (!session)
5392 return ERROR_OUTOFMEMORY;
5395 * According to my tests. The name is not resolved until a request is sent
5398 session->hdr.htype = WH_HHTTPSESSION;
5399 session->hdr.dwFlags = dwFlags;
5400 session->hdr.dwContext = dwContext;
5401 session->hdr.dwInternalFlags |= dwInternalFlags;
5403 WININET_AddRef( &hIC->hdr );
5404 session->appInfo = hIC;
5405 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5407 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5408 if(hIC->proxyBypass)
5409 FIXME("Proxy bypass is ignored.\n");
5411 session->serverName = heap_strdupW(lpszServerName);
5412 session->hostName = heap_strdupW(lpszServerName);
5413 if (lpszUserName && lpszUserName[0])
5414 session->userName = heap_strdupW(lpszUserName);
5415 if (lpszPassword && lpszPassword[0])
5416 session->password = heap_strdupW(lpszPassword);
5417 session->serverPort = serverPort;
5418 session->hostPort = serverPort;
5420 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5421 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5423 INTERNET_SendCallback(&hIC->hdr, dwContext,
5424 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5425 sizeof(HINTERNET));
5429 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5430 * windows
5433 TRACE("%p --> %p\n", hIC, session);
5435 *ret = session->hdr.hInternet;
5436 return ERROR_SUCCESS;
5439 /***********************************************************************
5440 * HTTP_clear_response_headers (internal)
5442 * clear out any old response headers
5444 static void HTTP_clear_response_headers( http_request_t *request )
5446 DWORD i;
5448 for( i=0; i<request->nCustHeaders; i++)
5450 if( !request->custHeaders[i].lpszField )
5451 continue;
5452 if( !request->custHeaders[i].lpszValue )
5453 continue;
5454 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5455 continue;
5456 HTTP_DeleteCustomHeader( request, i );
5457 i--;
5461 /***********************************************************************
5462 * HTTP_GetResponseHeaders (internal)
5464 * Read server response
5466 * RETURNS
5468 * TRUE on success
5469 * FALSE on error
5471 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5473 INT cbreaks = 0;
5474 WCHAR buffer[MAX_REPLY_LEN];
5475 DWORD buflen = MAX_REPLY_LEN;
5476 BOOL bSuccess = FALSE;
5477 INT rc = 0;
5478 char bufferA[MAX_REPLY_LEN];
5479 LPWSTR status_code = NULL, status_text = NULL;
5480 DWORD cchMaxRawHeaders = 1024;
5481 LPWSTR lpszRawHeaders = NULL;
5482 LPWSTR temp;
5483 DWORD cchRawHeaders = 0;
5484 BOOL codeHundred = FALSE;
5486 TRACE("-->\n");
5488 if(!request->netconn)
5489 goto lend;
5491 do {
5492 static const WCHAR szHundred[] = {'1','0','0',0};
5494 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5496 buflen = MAX_REPLY_LEN;
5497 if (!read_line(request, bufferA, &buflen))
5498 goto lend;
5500 /* clear old response headers (eg. from a redirect response) */
5501 if (clear) {
5502 HTTP_clear_response_headers( request );
5503 clear = FALSE;
5506 rc += buflen;
5507 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5508 /* check is this a status code line? */
5509 if (!strncmpW(buffer, g_szHttp1_0, 4))
5511 /* split the version from the status code */
5512 status_code = strchrW( buffer, ' ' );
5513 if( !status_code )
5514 goto lend;
5515 *status_code++=0;
5517 /* split the status code from the status text */
5518 status_text = strchrW( status_code, ' ' );
5519 if( !status_text )
5520 goto lend;
5521 *status_text++=0;
5523 TRACE("version [%s] status code [%s] status text [%s]\n",
5524 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5526 codeHundred = (!strcmpW(status_code, szHundred));
5528 else if (!codeHundred)
5530 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5532 heap_free(request->version);
5533 heap_free(request->statusText);
5535 request->version = heap_strdupW(g_szHttp1_0);
5536 request->statusText = heap_strdupW(szOK);
5538 heap_free(request->rawHeaders);
5539 request->rawHeaders = heap_strdupW(szDefaultHeader);
5541 bSuccess = TRUE;
5542 goto lend;
5544 } while (codeHundred);
5546 /* Add status code */
5547 HTTP_ProcessHeader(request, szStatus, status_code,
5548 HTTP_ADDHDR_FLAG_REPLACE);
5550 heap_free(request->version);
5551 heap_free(request->statusText);
5553 request->version = heap_strdupW(buffer);
5554 request->statusText = heap_strdupW(status_text);
5556 /* Restore the spaces */
5557 *(status_code-1) = ' ';
5558 *(status_text-1) = ' ';
5560 /* regenerate raw headers */
5561 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5562 if (!lpszRawHeaders) goto lend;
5564 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5565 cchMaxRawHeaders *= 2;
5566 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5567 if (temp == NULL) goto lend;
5568 lpszRawHeaders = temp;
5569 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5570 cchRawHeaders += (buflen-1);
5571 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5572 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5573 lpszRawHeaders[cchRawHeaders] = '\0';
5575 /* Parse each response line */
5578 buflen = MAX_REPLY_LEN;
5579 if (read_line(request, bufferA, &buflen))
5581 LPWSTR * pFieldAndValue;
5583 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5585 if (!bufferA[0]) break;
5586 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5588 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5589 if (pFieldAndValue)
5591 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5592 cchMaxRawHeaders *= 2;
5593 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5594 if (temp == NULL) goto lend;
5595 lpszRawHeaders = temp;
5596 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5597 cchRawHeaders += (buflen-1);
5598 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5599 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5600 lpszRawHeaders[cchRawHeaders] = '\0';
5602 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5603 HTTP_ADDREQ_FLAG_ADD );
5605 HTTP_FreeTokens(pFieldAndValue);
5608 else
5610 cbreaks++;
5611 if (cbreaks >= 2)
5612 break;
5614 }while(1);
5616 /* make sure the response header is terminated with an empty line. Some apps really
5617 truly care about that empty line being there for some reason. Just add it to the
5618 header. */
5619 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5621 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5622 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5623 if (temp == NULL) goto lend;
5624 lpszRawHeaders = temp;
5627 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5629 heap_free(request->rawHeaders);
5630 request->rawHeaders = lpszRawHeaders;
5631 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5632 bSuccess = TRUE;
5634 lend:
5636 TRACE("<--\n");
5637 if (bSuccess)
5638 return rc;
5639 else
5641 heap_free(lpszRawHeaders);
5642 return 0;
5646 /***********************************************************************
5647 * HTTP_InterpretHttpHeader (internal)
5649 * Parse server response
5651 * RETURNS
5653 * Pointer to array of field, value, NULL on success.
5654 * NULL on error.
5656 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5658 LPWSTR * pTokenPair;
5659 LPWSTR pszColon;
5660 INT len;
5662 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5664 pszColon = strchrW(buffer, ':');
5665 /* must have two tokens */
5666 if (!pszColon)
5668 HTTP_FreeTokens(pTokenPair);
5669 if (buffer[0])
5670 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5671 return NULL;
5674 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5675 if (!pTokenPair[0])
5677 HTTP_FreeTokens(pTokenPair);
5678 return NULL;
5680 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5681 pTokenPair[0][pszColon - buffer] = '\0';
5683 /* skip colon */
5684 pszColon++;
5685 len = strlenW(pszColon);
5686 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5687 if (!pTokenPair[1])
5689 HTTP_FreeTokens(pTokenPair);
5690 return NULL;
5692 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5694 strip_spaces(pTokenPair[0]);
5695 strip_spaces(pTokenPair[1]);
5697 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5698 return pTokenPair;
5701 /***********************************************************************
5702 * HTTP_ProcessHeader (internal)
5704 * Stuff header into header tables according to <dwModifier>
5708 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5710 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5712 LPHTTPHEADERW lphttpHdr = NULL;
5713 INT index = -1;
5714 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5715 DWORD res = ERROR_HTTP_INVALID_HEADER;
5717 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5719 /* REPLACE wins out over ADD */
5720 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5721 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5723 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5724 index = -1;
5725 else
5726 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5728 if (index >= 0)
5730 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5731 return ERROR_HTTP_INVALID_HEADER;
5732 lphttpHdr = &request->custHeaders[index];
5734 else if (value)
5736 HTTPHEADERW hdr;
5738 hdr.lpszField = (LPWSTR)field;
5739 hdr.lpszValue = (LPWSTR)value;
5740 hdr.wFlags = hdr.wCount = 0;
5742 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5743 hdr.wFlags |= HDR_ISREQUEST;
5745 return HTTP_InsertCustomHeader(request, &hdr);
5747 /* no value to delete */
5748 else return ERROR_SUCCESS;
5750 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5751 lphttpHdr->wFlags |= HDR_ISREQUEST;
5752 else
5753 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5755 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5757 HTTP_DeleteCustomHeader( request, index );
5759 if (value)
5761 HTTPHEADERW hdr;
5763 hdr.lpszField = (LPWSTR)field;
5764 hdr.lpszValue = (LPWSTR)value;
5765 hdr.wFlags = hdr.wCount = 0;
5767 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5768 hdr.wFlags |= HDR_ISREQUEST;
5770 return HTTP_InsertCustomHeader(request, &hdr);
5773 return ERROR_SUCCESS;
5775 else if (dwModifier & COALESCEFLAGS)
5777 LPWSTR lpsztmp;
5778 WCHAR ch = 0;
5779 INT len = 0;
5780 INT origlen = strlenW(lphttpHdr->lpszValue);
5781 INT valuelen = strlenW(value);
5783 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5785 ch = ',';
5786 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5788 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5790 ch = ';';
5791 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5794 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5796 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5797 if (lpsztmp)
5799 lphttpHdr->lpszValue = lpsztmp;
5800 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5801 if (ch > 0)
5803 lphttpHdr->lpszValue[origlen] = ch;
5804 origlen++;
5805 lphttpHdr->lpszValue[origlen] = ' ';
5806 origlen++;
5809 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5810 lphttpHdr->lpszValue[len] = '\0';
5811 res = ERROR_SUCCESS;
5813 else
5815 WARN("heap_realloc (%d bytes) failed\n",len+1);
5816 res = ERROR_OUTOFMEMORY;
5819 TRACE("<-- %d\n", res);
5820 return res;
5823 /***********************************************************************
5824 * HTTP_GetCustomHeaderIndex (internal)
5826 * Return index of custom header from header array
5829 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5830 int requested_index, BOOL request_only)
5832 DWORD index;
5834 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5836 for (index = 0; index < request->nCustHeaders; index++)
5838 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5839 continue;
5841 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5842 continue;
5844 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5845 continue;
5847 if (requested_index == 0)
5848 break;
5849 requested_index --;
5852 if (index >= request->nCustHeaders)
5853 index = -1;
5855 TRACE("Return: %d\n", index);
5856 return index;
5860 /***********************************************************************
5861 * HTTP_InsertCustomHeader (internal)
5863 * Insert header into array
5866 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5868 INT count;
5869 LPHTTPHEADERW lph = NULL;
5871 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5872 count = request->nCustHeaders + 1;
5873 if (count > 1)
5874 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5875 else
5876 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5878 if (!lph)
5879 return ERROR_OUTOFMEMORY;
5881 request->custHeaders = lph;
5882 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5883 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5884 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5885 request->custHeaders[count-1].wCount= lpHdr->wCount;
5886 request->nCustHeaders++;
5888 return ERROR_SUCCESS;
5892 /***********************************************************************
5893 * HTTP_DeleteCustomHeader (internal)
5895 * Delete header from array
5896 * If this function is called, the indexs may change.
5898 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5900 if( request->nCustHeaders <= 0 )
5901 return FALSE;
5902 if( index >= request->nCustHeaders )
5903 return FALSE;
5904 request->nCustHeaders--;
5906 heap_free(request->custHeaders[index].lpszField);
5907 heap_free(request->custHeaders[index].lpszValue);
5909 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5910 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5911 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5913 return TRUE;
5917 /***********************************************************************
5918 * HTTP_VerifyValidHeader (internal)
5920 * Verify the given header is not invalid for the given http request
5923 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5925 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5926 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5927 return ERROR_HTTP_INVALID_HEADER;
5929 return ERROR_SUCCESS;
5932 /***********************************************************************
5933 * IsHostInProxyBypassList (@)
5935 * Undocumented
5938 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5940 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5941 return FALSE;
5944 /***********************************************************************
5945 * InternetShowSecurityInfoByURLA (@)
5947 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5949 FIXME("stub: %s %p\n", url, window);
5950 return FALSE;
5953 /***********************************************************************
5954 * InternetShowSecurityInfoByURLW (@)
5956 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5958 FIXME("stub: %s %p\n", debugstr_w(url), window);
5959 return FALSE;