kernel32: Remove a no longer needed todo_wine.
[wine.git] / dlls / wininet / http.c
blob3439924d3f83a38602a534852e607aa567a6cbee
1 /*
2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 * Copyright 2011 Jacek Caban for CodeWeavers
12 * Ulrich Czekalla
13 * David Hammerton
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
40 #endif
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
43 #endif
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #ifdef HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
50 #include <time.h>
51 #include <assert.h>
52 #ifdef HAVE_ZLIB
53 # include <zlib.h>
54 #endif
56 #include "windef.h"
57 #include "winbase.h"
58 #include "wininet.h"
59 #include "winerror.h"
60 #include "winternl.h"
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
65 #include "shlwapi.h"
66 #include "sspi.h"
67 #include "wincrypt.h"
68 #include "winuser.h"
69 #include "cryptuiapi.h"
71 #include "internet.h"
72 #include "wine/debug.h"
73 #include "wine/exception.h"
74 #include "wine/unicode.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
78 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
79 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
80 static const WCHAR szOK[] = {'O','K',0};
81 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
82 static const WCHAR hostW[] = { 'H','o','s','t',0 };
83 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
85 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
86 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
87 static const WCHAR szGET[] = { 'G','E','T', 0 };
88 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
89 static const WCHAR szCrLf[] = {'\r','\n', 0};
91 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
92 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
93 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
94 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
95 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
96 static const WCHAR szAge[] = { 'A','g','e',0 };
97 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
98 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
99 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
100 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
101 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
102 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
103 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
104 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
105 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
106 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
107 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
108 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 };
109 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
110 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
111 static const WCHAR szDate[] = { 'D','a','t','e',0 };
112 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
113 static const WCHAR szETag[] = { 'E','T','a','g',0 };
114 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
115 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
116 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
118 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
119 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
120 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
121 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
122 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
123 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
124 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
125 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
126 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
127 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
128 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
129 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
130 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
131 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
132 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
133 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
134 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
135 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 };
136 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
137 static const WCHAR szURI[] = { 'U','R','I',0 };
138 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
139 static const WCHAR szVary[] = { 'V','a','r','y',0 };
140 static const WCHAR szVia[] = { 'V','i','a',0 };
141 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
142 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define COLLECT_TIME 60000
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
160 struct HttpAuthInfo
162 LPWSTR scheme;
163 CredHandle cred;
164 CtxtHandle ctx;
165 TimeStamp exp;
166 ULONG attr;
167 ULONG max_token;
168 void *auth_data;
169 unsigned int auth_data_len;
170 BOOL finished; /* finished authenticating */
174 typedef struct _basicAuthorizationData
176 struct list entry;
178 LPWSTR host;
179 LPWSTR realm;
180 LPSTR authorization;
181 UINT authorizationLen;
182 } basicAuthorizationData;
184 typedef struct _authorizationData
186 struct list entry;
188 LPWSTR host;
189 LPWSTR scheme;
190 LPWSTR domain;
191 UINT domain_len;
192 LPWSTR user;
193 UINT user_len;
194 LPWSTR password;
195 UINT password_len;
196 } authorizationData;
198 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
199 static struct list authorizationCache = LIST_INIT(authorizationCache);
201 static CRITICAL_SECTION authcache_cs;
202 static CRITICAL_SECTION_DEBUG critsect_debug =
204 0, 0, &authcache_cs,
205 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
206 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
208 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
210 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
211 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
212 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
213 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
214 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
215 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
216 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
217 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
218 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
219 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
220 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
222 static CRITICAL_SECTION connection_pool_cs;
223 static CRITICAL_SECTION_DEBUG connection_pool_debug =
225 0, 0, &connection_pool_cs,
226 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
227 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
229 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
231 static struct list connection_pool = LIST_INIT(connection_pool);
232 static BOOL collector_running;
234 void server_addref(server_t *server)
236 InterlockedIncrement(&server->ref);
239 void server_release(server_t *server)
241 if(InterlockedDecrement(&server->ref))
242 return;
244 if(!server->ref)
245 server->keep_until = GetTickCount64() + COLLECT_TIME;
248 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
250 server_t *iter, *server = NULL;
252 EnterCriticalSection(&connection_pool_cs);
254 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
255 if(iter->port == port && !strcmpW(iter->name, name)) {
256 server = iter;
257 server_addref(server);
258 break;
262 if(!server) {
263 server = heap_alloc(sizeof(*server));
264 if(server) {
265 server->addr_len = 0;
266 server->ref = 1;
267 server->port = port;
268 list_init(&server->conn_pool);
269 server->name = heap_strdupW(name);
270 if(server->name) {
271 list_add_head(&connection_pool, &server->entry);
272 }else {
273 heap_free(server);
274 server = NULL;
279 LeaveCriticalSection(&connection_pool_cs);
281 return server;
284 BOOL collect_connections(BOOL collect_all)
286 netconn_t *netconn, *netconn_safe;
287 server_t *server, *server_safe;
288 BOOL remaining = FALSE;
289 DWORD64 now;
291 now = GetTickCount64();
293 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
294 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
295 if(collect_all || netconn->keep_until < now) {
296 TRACE("freeing %p\n", netconn);
297 list_remove(&netconn->pool_entry);
298 free_netconn(netconn);
299 }else {
300 remaining = TRUE;
304 if(!server->ref) {
305 if(collect_all || server->keep_until < now) {
306 list_remove(&server->entry);
308 heap_free(server->name);
309 heap_free(server);
310 }else {
311 remaining = TRUE;
316 return remaining;
319 static DWORD WINAPI collect_connections_proc(void *arg)
321 BOOL remaining_conns;
323 do {
324 /* FIXME: Use more sophisticated method */
325 Sleep(5000);
327 EnterCriticalSection(&connection_pool_cs);
329 remaining_conns = collect_connections(FALSE);
330 if(!remaining_conns)
331 collector_running = FALSE;
333 LeaveCriticalSection(&connection_pool_cs);
334 }while(remaining_conns);
336 FreeLibraryAndExitThread(WININET_hModule, 0);
339 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
341 int HeaderIndex = 0;
342 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
343 if (HeaderIndex == -1)
344 return NULL;
345 else
346 return &req->custHeaders[HeaderIndex];
349 typedef enum {
350 READMODE_SYNC,
351 READMODE_ASYNC,
352 READMODE_NOBLOCK
353 } read_mode_t;
355 struct data_stream_vtbl_t {
356 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
357 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
358 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
359 BOOL (*drain_content)(data_stream_t*,http_request_t*);
360 void (*destroy)(data_stream_t*);
363 typedef struct {
364 data_stream_t data_stream;
366 BYTE buf[READ_BUFFER_SIZE];
367 DWORD buf_size;
368 DWORD buf_pos;
369 DWORD chunk_size;
370 } chunked_stream_t;
372 static inline void destroy_data_stream(data_stream_t *stream)
374 stream->vtbl->destroy(stream);
377 static void reset_data_stream(http_request_t *req)
379 destroy_data_stream(req->data_stream);
380 req->data_stream = &req->netconn_stream.data_stream;
381 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
382 req->read_chunked = req->read_gzip = FALSE;
385 #ifdef HAVE_ZLIB
387 typedef struct {
388 data_stream_t stream;
389 data_stream_t *parent_stream;
390 z_stream zstream;
391 BYTE buf[READ_BUFFER_SIZE];
392 DWORD buf_size;
393 DWORD buf_pos;
394 BOOL end_of_data;
395 } gzip_stream_t;
397 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
399 /* Allow reading only from read buffer */
400 return 0;
403 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
405 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
406 return gzip_stream->end_of_data;
409 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
410 DWORD *read, read_mode_t read_mode)
412 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
413 z_stream *zstream = &gzip_stream->zstream;
414 DWORD current_read, ret_read = 0;
415 BOOL end;
416 int zres;
417 DWORD res = ERROR_SUCCESS;
419 while(size && !gzip_stream->end_of_data) {
420 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
422 if(gzip_stream->buf_size <= 64 && !end) {
423 if(gzip_stream->buf_pos) {
424 if(gzip_stream->buf_size)
425 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
426 gzip_stream->buf_pos = 0;
428 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
429 sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
430 gzip_stream->buf_size += current_read;
431 if(res != ERROR_SUCCESS)
432 break;
433 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
434 if(!current_read && !end) {
435 if(read_mode != READMODE_NOBLOCK) {
436 WARN("unexpected end of data\n");
437 gzip_stream->end_of_data = TRUE;
439 break;
441 if(gzip_stream->buf_size <= 64 && !end)
442 continue;
445 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
446 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
447 zstream->next_out = buf+ret_read;
448 zstream->avail_out = size;
449 zres = inflate(&gzip_stream->zstream, 0);
450 current_read = size - zstream->avail_out;
451 size -= current_read;
452 ret_read += current_read;
453 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
454 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
455 if(zres == Z_STREAM_END) {
456 TRACE("end of data\n");
457 gzip_stream->end_of_data = TRUE;
458 inflateEnd(zstream);
459 }else if(zres != Z_OK) {
460 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
461 if(!ret_read)
462 res = ERROR_INTERNET_DECODING_FAILED;
463 break;
466 if(ret_read && read_mode == READMODE_ASYNC)
467 read_mode = READMODE_NOBLOCK;
470 TRACE("read %u bytes\n", ret_read);
471 *read = ret_read;
472 return res;
475 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
477 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
478 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
481 static void gzip_destroy(data_stream_t *stream)
483 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
485 destroy_data_stream(gzip_stream->parent_stream);
487 if(!gzip_stream->end_of_data)
488 inflateEnd(&gzip_stream->zstream);
489 heap_free(gzip_stream);
492 static const data_stream_vtbl_t gzip_stream_vtbl = {
493 gzip_get_avail_data,
494 gzip_end_of_data,
495 gzip_read,
496 gzip_drain_content,
497 gzip_destroy
500 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
502 return heap_alloc(items*size);
505 static void wininet_zfree(voidpf opaque, voidpf address)
507 heap_free(address);
510 static DWORD init_gzip_stream(http_request_t *req)
512 gzip_stream_t *gzip_stream;
513 int index, zres;
515 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
516 if(!gzip_stream)
517 return ERROR_OUTOFMEMORY;
519 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
520 gzip_stream->zstream.zalloc = wininet_zalloc;
521 gzip_stream->zstream.zfree = wininet_zfree;
523 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
524 if(zres != Z_OK) {
525 ERR("inflateInit failed: %d\n", zres);
526 heap_free(gzip_stream);
527 return ERROR_OUTOFMEMORY;
530 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
531 if(index != -1)
532 HTTP_DeleteCustomHeader(req, index);
534 if(req->read_size) {
535 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
536 gzip_stream->buf_size = req->read_size;
537 req->read_pos = req->read_size = 0;
540 req->read_gzip = TRUE;
541 gzip_stream->parent_stream = req->data_stream;
542 req->data_stream = &gzip_stream->stream;
543 return ERROR_SUCCESS;
546 #else
548 static DWORD init_gzip_stream(http_request_t *req)
550 ERR("gzip stream not supported, missing zlib.\n");
551 return ERROR_SUCCESS;
554 #endif
556 /***********************************************************************
557 * HTTP_Tokenize (internal)
559 * Tokenize a string, allocating memory for the tokens.
561 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
563 LPWSTR * token_array;
564 int tokens = 0;
565 int i;
566 LPCWSTR next_token;
568 if (string)
570 /* empty string has no tokens */
571 if (*string)
572 tokens++;
573 /* count tokens */
574 for (i = 0; string[i]; i++)
576 if (!strncmpW(string+i, token_string, strlenW(token_string)))
578 DWORD j;
579 tokens++;
580 /* we want to skip over separators, but not the null terminator */
581 for (j = 0; j < strlenW(token_string) - 1; j++)
582 if (!string[i+j])
583 break;
584 i += j;
589 /* add 1 for terminating NULL */
590 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
591 token_array[tokens] = NULL;
592 if (!tokens)
593 return token_array;
594 for (i = 0; i < tokens; i++)
596 int len;
597 next_token = strstrW(string, token_string);
598 if (!next_token) next_token = string+strlenW(string);
599 len = next_token - string;
600 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
601 memcpy(token_array[i], string, len*sizeof(WCHAR));
602 token_array[i][len] = '\0';
603 string = next_token+strlenW(token_string);
605 return token_array;
608 /***********************************************************************
609 * HTTP_FreeTokens (internal)
611 * Frees memory returned from HTTP_Tokenize.
613 static void HTTP_FreeTokens(LPWSTR * token_array)
615 int i;
616 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
617 heap_free(token_array);
620 static void HTTP_FixURL(http_request_t *request)
622 static const WCHAR szSlash[] = { '/',0 };
623 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
625 /* If we don't have a path we set it to root */
626 if (NULL == request->path)
627 request->path = heap_strdupW(szSlash);
628 else /* remove \r and \n*/
630 int nLen = strlenW(request->path);
631 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
633 nLen--;
634 request->path[nLen]='\0';
636 /* Replace '\' with '/' */
637 while (nLen>0) {
638 nLen--;
639 if (request->path[nLen] == '\\') request->path[nLen]='/';
643 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
644 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
645 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
647 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
648 *fixurl = '/';
649 strcpyW(fixurl + 1, request->path);
650 heap_free( request->path );
651 request->path = fixurl;
655 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
657 LPWSTR requestString;
658 DWORD len, n;
659 LPCWSTR *req;
660 UINT i;
661 LPWSTR p;
663 static const WCHAR szSpace[] = { ' ',0 };
664 static const WCHAR szColon[] = { ':',' ',0 };
665 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
667 /* allocate space for an array of all the string pointers to be added */
668 len = (request->nCustHeaders)*4 + 10;
669 req = heap_alloc(len*sizeof(LPCWSTR));
671 /* add the verb, path and HTTP version string */
672 n = 0;
673 req[n++] = verb;
674 req[n++] = szSpace;
675 req[n++] = path;
676 req[n++] = szSpace;
677 req[n++] = version;
679 /* Append custom request headers */
680 for (i = 0; i < request->nCustHeaders; i++)
682 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
684 req[n++] = szCrLf;
685 req[n++] = request->custHeaders[i].lpszField;
686 req[n++] = szColon;
687 req[n++] = request->custHeaders[i].lpszValue;
689 TRACE("Adding custom header %s (%s)\n",
690 debugstr_w(request->custHeaders[i].lpszField),
691 debugstr_w(request->custHeaders[i].lpszValue));
695 if( n >= len )
696 ERR("oops. buffer overrun\n");
698 req[n] = NULL;
699 requestString = HTTP_build_req( req, 4 );
700 heap_free( req );
703 * Set (header) termination string for request
704 * Make sure there's exactly two new lines at the end of the request
706 p = &requestString[strlenW(requestString)-1];
707 while ( (*p == '\n') || (*p == '\r') )
708 p--;
709 strcpyW( p+1, sztwocrlf );
711 return requestString;
714 static void HTTP_ProcessCookies( http_request_t *request )
716 int HeaderIndex;
717 int numCookies = 0;
718 LPHTTPHEADERW setCookieHeader;
720 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
721 return;
723 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
725 HTTPHEADERW *host;
726 const WCHAR *data;
727 WCHAR *name;
729 setCookieHeader = &request->custHeaders[HeaderIndex];
731 if (!setCookieHeader->lpszValue)
732 continue;
734 host = HTTP_GetHeader(request, hostW);
735 if(!host)
736 continue;
738 data = strchrW(setCookieHeader->lpszValue, '=');
739 if(!data)
740 continue;
742 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
743 if(!name)
744 continue;
746 data++;
747 set_cookie(host->lpszValue, request->path, name, data);
748 heap_free(name);
752 static void strip_spaces(LPWSTR start)
754 LPWSTR str = start;
755 LPWSTR end;
757 while (*str == ' ' && *str != '\0')
758 str++;
760 if (str != start)
761 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
763 end = start + strlenW(start) - 1;
764 while (end >= start && *end == ' ')
766 *end = '\0';
767 end--;
771 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
773 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
774 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
775 BOOL is_basic;
776 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
777 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
778 if (is_basic && pszRealm)
780 LPCWSTR token;
781 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
782 LPCWSTR realm;
783 ptr++;
784 *pszRealm=NULL;
785 token = strchrW(ptr,'=');
786 if (!token)
787 return TRUE;
788 realm = ptr;
789 while (*realm == ' ' && *realm != '\0')
790 realm++;
791 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
792 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
794 token++;
795 while (*token == ' ' && *token != '\0')
796 token++;
797 if (*token == '\0')
798 return TRUE;
799 *pszRealm = heap_strdupW(token);
800 strip_spaces(*pszRealm);
804 return is_basic;
807 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
809 if (!authinfo) return;
811 if (SecIsValidHandle(&authinfo->ctx))
812 DeleteSecurityContext(&authinfo->ctx);
813 if (SecIsValidHandle(&authinfo->cred))
814 FreeCredentialsHandle(&authinfo->cred);
816 heap_free(authinfo->auth_data);
817 heap_free(authinfo->scheme);
818 heap_free(authinfo);
821 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
823 basicAuthorizationData *ad;
824 UINT rc = 0;
826 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
828 EnterCriticalSection(&authcache_cs);
829 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
831 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
833 TRACE("Authorization found in cache\n");
834 *auth_data = heap_alloc(ad->authorizationLen);
835 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
836 rc = ad->authorizationLen;
837 break;
840 LeaveCriticalSection(&authcache_cs);
841 return rc;
844 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
846 struct list *cursor;
847 basicAuthorizationData* ad = NULL;
849 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
851 EnterCriticalSection(&authcache_cs);
852 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
854 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
855 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
857 ad = check;
858 break;
862 if (ad)
864 TRACE("Found match in cache, replacing\n");
865 heap_free(ad->authorization);
866 ad->authorization = heap_alloc(auth_data_len);
867 memcpy(ad->authorization, auth_data, auth_data_len);
868 ad->authorizationLen = auth_data_len;
870 else
872 ad = heap_alloc(sizeof(basicAuthorizationData));
873 ad->host = heap_strdupW(host);
874 ad->realm = heap_strdupW(realm);
875 ad->authorization = heap_alloc(auth_data_len);
876 memcpy(ad->authorization, auth_data, auth_data_len);
877 ad->authorizationLen = auth_data_len;
878 list_add_head(&basicAuthorizationCache,&ad->entry);
879 TRACE("authorization cached\n");
881 LeaveCriticalSection(&authcache_cs);
884 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
885 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
887 authorizationData *ad;
889 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
891 EnterCriticalSection(&authcache_cs);
892 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
893 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
894 TRACE("Authorization found in cache\n");
896 nt_auth_identity->User = heap_strdupW(ad->user);
897 nt_auth_identity->Password = heap_strdupW(ad->password);
898 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
899 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
900 (!nt_auth_identity->Domain && ad->domain_len)) {
901 heap_free(nt_auth_identity->User);
902 heap_free(nt_auth_identity->Password);
903 heap_free(nt_auth_identity->Domain);
904 break;
907 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
908 nt_auth_identity->UserLength = ad->user_len;
909 nt_auth_identity->PasswordLength = ad->password_len;
910 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
911 nt_auth_identity->DomainLength = ad->domain_len;
912 LeaveCriticalSection(&authcache_cs);
913 return TRUE;
916 LeaveCriticalSection(&authcache_cs);
918 return FALSE;
921 static void cache_authorization(LPWSTR host, LPWSTR scheme,
922 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
924 authorizationData *ad;
925 BOOL found = FALSE;
927 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
929 EnterCriticalSection(&authcache_cs);
930 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
931 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
932 found = TRUE;
933 break;
936 if(found) {
937 heap_free(ad->user);
938 heap_free(ad->password);
939 heap_free(ad->domain);
940 } else {
941 ad = heap_alloc(sizeof(authorizationData));
942 if(!ad) {
943 LeaveCriticalSection(&authcache_cs);
944 return;
947 ad->host = heap_strdupW(host);
948 ad->scheme = heap_strdupW(scheme);
949 list_add_head(&authorizationCache, &ad->entry);
952 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
953 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
954 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
955 ad->user_len = nt_auth_identity->UserLength;
956 ad->password_len = nt_auth_identity->PasswordLength;
957 ad->domain_len = nt_auth_identity->DomainLength;
959 if(!ad->host || !ad->scheme || !ad->user || !ad->password
960 || (nt_auth_identity->Domain && !ad->domain)) {
961 heap_free(ad->host);
962 heap_free(ad->scheme);
963 heap_free(ad->user);
964 heap_free(ad->password);
965 heap_free(ad->domain);
966 list_remove(&ad->entry);
967 heap_free(ad);
970 LeaveCriticalSection(&authcache_cs);
973 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
974 struct HttpAuthInfo **ppAuthInfo,
975 LPWSTR domain_and_username, LPWSTR password,
976 LPWSTR host )
978 SECURITY_STATUS sec_status;
979 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
980 BOOL first = FALSE;
981 LPWSTR szRealm = NULL;
983 TRACE("%s\n", debugstr_w(pszAuthValue));
985 if (!pAuthInfo)
987 TimeStamp exp;
989 first = TRUE;
990 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
991 if (!pAuthInfo)
992 return FALSE;
994 SecInvalidateHandle(&pAuthInfo->cred);
995 SecInvalidateHandle(&pAuthInfo->ctx);
996 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
997 pAuthInfo->attr = 0;
998 pAuthInfo->auth_data = NULL;
999 pAuthInfo->auth_data_len = 0;
1000 pAuthInfo->finished = FALSE;
1002 if (is_basic_auth_value(pszAuthValue,NULL))
1004 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1005 pAuthInfo->scheme = heap_strdupW(szBasic);
1006 if (!pAuthInfo->scheme)
1008 heap_free(pAuthInfo);
1009 return FALSE;
1012 else
1014 PVOID pAuthData;
1015 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1017 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1018 if (!pAuthInfo->scheme)
1020 heap_free(pAuthInfo);
1021 return FALSE;
1024 if (domain_and_username)
1026 WCHAR *user = strchrW(domain_and_username, '\\');
1027 WCHAR *domain = domain_and_username;
1029 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1031 pAuthData = &nt_auth_identity;
1033 if (user) user++;
1034 else
1036 user = domain_and_username;
1037 domain = NULL;
1040 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1041 nt_auth_identity.User = user;
1042 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1043 nt_auth_identity.Domain = domain;
1044 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1045 nt_auth_identity.Password = password;
1046 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1048 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1050 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1051 pAuthData = &nt_auth_identity;
1052 else
1053 /* use default credentials */
1054 pAuthData = NULL;
1056 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1057 SECPKG_CRED_OUTBOUND, NULL,
1058 pAuthData, NULL,
1059 NULL, &pAuthInfo->cred,
1060 &exp);
1062 if(pAuthData && !domain_and_username) {
1063 heap_free(nt_auth_identity.User);
1064 heap_free(nt_auth_identity.Domain);
1065 heap_free(nt_auth_identity.Password);
1068 if (sec_status == SEC_E_OK)
1070 PSecPkgInfoW sec_pkg_info;
1071 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1072 if (sec_status == SEC_E_OK)
1074 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1075 FreeContextBuffer(sec_pkg_info);
1078 if (sec_status != SEC_E_OK)
1080 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1081 debugstr_w(pAuthInfo->scheme), sec_status);
1082 heap_free(pAuthInfo->scheme);
1083 heap_free(pAuthInfo);
1084 return FALSE;
1087 *ppAuthInfo = pAuthInfo;
1089 else if (pAuthInfo->finished)
1090 return FALSE;
1092 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1093 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1095 ERR("authentication scheme changed from %s to %s\n",
1096 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1097 return FALSE;
1100 if (is_basic_auth_value(pszAuthValue,&szRealm))
1102 int userlen;
1103 int passlen;
1104 char *auth_data = NULL;
1105 UINT auth_data_len = 0;
1107 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1109 if (!domain_and_username)
1111 if (host && szRealm)
1112 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1113 if (auth_data_len == 0)
1115 heap_free(szRealm);
1116 return FALSE;
1119 else
1121 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1122 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1124 /* length includes a nul terminator, which will be re-used for the ':' */
1125 auth_data = heap_alloc(userlen + 1 + passlen);
1126 if (!auth_data)
1128 heap_free(szRealm);
1129 return FALSE;
1132 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1133 auth_data[userlen] = ':';
1134 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1135 auth_data_len = userlen + 1 + passlen;
1136 if (host && szRealm)
1137 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1140 pAuthInfo->auth_data = auth_data;
1141 pAuthInfo->auth_data_len = auth_data_len;
1142 pAuthInfo->finished = TRUE;
1143 heap_free(szRealm);
1144 return TRUE;
1146 else
1148 LPCWSTR pszAuthData;
1149 SecBufferDesc out_desc, in_desc;
1150 SecBuffer out, in;
1151 unsigned char *buffer;
1152 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1153 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1155 in.BufferType = SECBUFFER_TOKEN;
1156 in.cbBuffer = 0;
1157 in.pvBuffer = NULL;
1159 in_desc.ulVersion = 0;
1160 in_desc.cBuffers = 1;
1161 in_desc.pBuffers = &in;
1163 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1164 if (*pszAuthData == ' ')
1166 pszAuthData++;
1167 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1168 in.pvBuffer = heap_alloc(in.cbBuffer);
1169 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1172 buffer = heap_alloc(pAuthInfo->max_token);
1174 out.BufferType = SECBUFFER_TOKEN;
1175 out.cbBuffer = pAuthInfo->max_token;
1176 out.pvBuffer = buffer;
1178 out_desc.ulVersion = 0;
1179 out_desc.cBuffers = 1;
1180 out_desc.pBuffers = &out;
1182 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1183 first ? NULL : &pAuthInfo->ctx,
1184 first ? request->session->serverName : NULL,
1185 context_req, 0, SECURITY_NETWORK_DREP,
1186 in.pvBuffer ? &in_desc : NULL,
1187 0, &pAuthInfo->ctx, &out_desc,
1188 &pAuthInfo->attr, &pAuthInfo->exp);
1189 if (sec_status == SEC_E_OK)
1191 pAuthInfo->finished = TRUE;
1192 pAuthInfo->auth_data = out.pvBuffer;
1193 pAuthInfo->auth_data_len = out.cbBuffer;
1194 TRACE("sending last auth packet\n");
1196 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1198 pAuthInfo->auth_data = out.pvBuffer;
1199 pAuthInfo->auth_data_len = out.cbBuffer;
1200 TRACE("sending next auth packet\n");
1202 else
1204 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1205 heap_free(out.pvBuffer);
1206 destroy_authinfo(pAuthInfo);
1207 *ppAuthInfo = NULL;
1208 return FALSE;
1212 return TRUE;
1215 /***********************************************************************
1216 * HTTP_HttpAddRequestHeadersW (internal)
1218 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1219 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1221 LPWSTR lpszStart;
1222 LPWSTR lpszEnd;
1223 LPWSTR buffer;
1224 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1226 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1228 if( dwHeaderLength == ~0U )
1229 len = strlenW(lpszHeader);
1230 else
1231 len = dwHeaderLength;
1232 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1233 lstrcpynW( buffer, lpszHeader, len + 1);
1235 lpszStart = buffer;
1239 LPWSTR * pFieldAndValue;
1241 lpszEnd = lpszStart;
1243 while (*lpszEnd != '\0')
1245 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1246 break;
1247 lpszEnd++;
1250 if (*lpszStart == '\0')
1251 break;
1253 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1255 *lpszEnd = '\0';
1256 lpszEnd++; /* Jump over newline */
1258 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1259 if (*lpszStart == '\0')
1261 /* Skip 0-length headers */
1262 lpszStart = lpszEnd;
1263 res = ERROR_SUCCESS;
1264 continue;
1266 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1267 if (pFieldAndValue)
1269 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1270 if (res == ERROR_SUCCESS)
1271 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1272 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1273 HTTP_FreeTokens(pFieldAndValue);
1276 lpszStart = lpszEnd;
1277 } while (res == ERROR_SUCCESS);
1279 heap_free(buffer);
1280 return res;
1283 /***********************************************************************
1284 * HttpAddRequestHeadersW (WININET.@)
1286 * Adds one or more HTTP header to the request handler
1288 * NOTE
1289 * On Windows if dwHeaderLength includes the trailing '\0', then
1290 * HttpAddRequestHeadersW() adds it too. However this results in an
1291 * invalid Http header which is rejected by some servers so we probably
1292 * don't need to match Windows on that point.
1294 * RETURNS
1295 * TRUE on success
1296 * FALSE on failure
1299 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1300 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1302 http_request_t *request;
1303 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1305 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1307 if (!lpszHeader)
1308 return TRUE;
1310 request = (http_request_t*) get_handle_object( hHttpRequest );
1311 if (request && request->hdr.htype == WH_HHTTPREQ)
1312 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1313 if( request )
1314 WININET_Release( &request->hdr );
1316 if(res != ERROR_SUCCESS)
1317 SetLastError(res);
1318 return res == ERROR_SUCCESS;
1321 /***********************************************************************
1322 * HttpAddRequestHeadersA (WININET.@)
1324 * Adds one or more HTTP header to the request handler
1326 * RETURNS
1327 * TRUE on success
1328 * FALSE on failure
1331 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1332 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1334 DWORD len;
1335 LPWSTR hdr;
1336 BOOL r;
1338 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1340 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1341 hdr = heap_alloc(len*sizeof(WCHAR));
1342 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1343 if( dwHeaderLength != ~0U )
1344 dwHeaderLength = len;
1346 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1348 heap_free( hdr );
1349 return r;
1352 static void free_accept_types( WCHAR **accept_types )
1354 WCHAR *ptr, **types = accept_types;
1356 if (!types) return;
1357 while ((ptr = *types))
1359 heap_free( ptr );
1360 types++;
1362 heap_free( accept_types );
1365 static WCHAR **convert_accept_types( const char **accept_types )
1367 unsigned int count;
1368 const char **types = accept_types;
1369 WCHAR **typesW;
1370 BOOL invalid_pointer = FALSE;
1372 if (!types) return NULL;
1373 count = 0;
1374 while (*types)
1376 __TRY
1378 /* find out how many there are */
1379 if (*types && **types)
1381 TRACE("accept type: %s\n", debugstr_a(*types));
1382 count++;
1385 __EXCEPT_PAGE_FAULT
1387 WARN("invalid accept type pointer\n");
1388 invalid_pointer = TRUE;
1390 __ENDTRY;
1391 types++;
1393 if (invalid_pointer) return NULL;
1394 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1395 count = 0;
1396 types = accept_types;
1397 while (*types)
1399 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1400 types++;
1402 typesW[count] = NULL;
1403 return typesW;
1406 /***********************************************************************
1407 * HttpOpenRequestA (WININET.@)
1409 * Open a HTTP request handle
1411 * RETURNS
1412 * HINTERNET a HTTP request handle on success
1413 * NULL on failure
1416 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1417 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1418 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1419 DWORD dwFlags, DWORD_PTR dwContext)
1421 LPWSTR szVerb = NULL, szObjectName = NULL;
1422 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1423 HINTERNET rc = FALSE;
1425 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1426 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1427 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1428 dwFlags, dwContext);
1430 if (lpszVerb)
1432 szVerb = heap_strdupAtoW(lpszVerb);
1433 if ( !szVerb )
1434 goto end;
1437 if (lpszObjectName)
1439 szObjectName = heap_strdupAtoW(lpszObjectName);
1440 if ( !szObjectName )
1441 goto end;
1444 if (lpszVersion)
1446 szVersion = heap_strdupAtoW(lpszVersion);
1447 if ( !szVersion )
1448 goto end;
1451 if (lpszReferrer)
1453 szReferrer = heap_strdupAtoW(lpszReferrer);
1454 if ( !szReferrer )
1455 goto end;
1458 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1459 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1460 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1462 end:
1463 free_accept_types(szAcceptTypes);
1464 heap_free(szReferrer);
1465 heap_free(szVersion);
1466 heap_free(szObjectName);
1467 heap_free(szVerb);
1468 return rc;
1471 /***********************************************************************
1472 * HTTP_EncodeBase64
1474 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1476 UINT n = 0, x;
1477 static const CHAR HTTP_Base64Enc[] =
1478 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1480 while( len > 0 )
1482 /* first 6 bits, all from bin[0] */
1483 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1484 x = (bin[0] & 3) << 4;
1486 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1487 if( len == 1 )
1489 base64[n++] = HTTP_Base64Enc[x];
1490 base64[n++] = '=';
1491 base64[n++] = '=';
1492 break;
1494 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1495 x = ( bin[1] & 0x0f ) << 2;
1497 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1498 if( len == 2 )
1500 base64[n++] = HTTP_Base64Enc[x];
1501 base64[n++] = '=';
1502 break;
1504 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1506 /* last 6 bits, all from bin [2] */
1507 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1508 bin += 3;
1509 len -= 3;
1511 base64[n] = 0;
1512 return n;
1515 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1516 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1517 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1518 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1519 static const signed char HTTP_Base64Dec[256] =
1521 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1522 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1523 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1524 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1525 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1526 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1527 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1528 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1529 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1530 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1531 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1532 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1533 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1534 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1535 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1536 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1537 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1538 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1539 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1540 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1541 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1542 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1543 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1544 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1545 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1546 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1548 #undef CH
1550 /***********************************************************************
1551 * HTTP_DecodeBase64
1553 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1555 unsigned int n = 0;
1557 while(*base64)
1559 signed char in[4];
1561 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1562 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1563 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1564 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1566 WARN("invalid base64: %s\n", debugstr_w(base64));
1567 return 0;
1569 if (bin)
1570 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1571 n++;
1573 if ((base64[2] == '=') && (base64[3] == '='))
1574 break;
1575 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1576 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1578 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1579 return 0;
1581 if (bin)
1582 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1583 n++;
1585 if (base64[3] == '=')
1586 break;
1587 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1588 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1590 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1591 return 0;
1593 if (bin)
1594 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1595 n++;
1597 base64 += 4;
1600 return n;
1603 /***********************************************************************
1604 * HTTP_InsertAuthorization
1606 * Insert or delete the authorization field in the request header.
1608 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1610 if (pAuthInfo)
1612 static const WCHAR wszSpace[] = {' ',0};
1613 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1614 unsigned int len;
1615 WCHAR *authorization = NULL;
1617 if (pAuthInfo->auth_data_len)
1619 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1620 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1621 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1622 if (!authorization)
1623 return FALSE;
1625 strcpyW(authorization, pAuthInfo->scheme);
1626 strcatW(authorization, wszSpace);
1627 HTTP_EncodeBase64(pAuthInfo->auth_data,
1628 pAuthInfo->auth_data_len,
1629 authorization+strlenW(authorization));
1631 /* clear the data as it isn't valid now that it has been sent to the
1632 * server, unless it's Basic authentication which doesn't do
1633 * connection tracking */
1634 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1636 heap_free(pAuthInfo->auth_data);
1637 pAuthInfo->auth_data = NULL;
1638 pAuthInfo->auth_data_len = 0;
1642 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1644 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1645 heap_free(authorization);
1647 return TRUE;
1650 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1652 static const WCHAR slash[] = { '/',0 };
1653 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1654 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1655 http_session_t *session = req->session;
1656 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1657 DWORD size;
1659 size = sizeof(new_location);
1660 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1662 URL_COMPONENTSW UrlComponents;
1664 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1665 strcpyW( url, new_location );
1667 ZeroMemory(&UrlComponents,sizeof(URL_COMPONENTSW));
1668 if(InternetCrackUrlW(url, 0, 0, &UrlComponents)) goto done;
1669 heap_free(url);
1672 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1673 size += strlenW( session->hostName ) + strlenW( req->path );
1675 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1677 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1678 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1679 else
1680 sprintfW( url, format, session->hostName, session->hostPort );
1681 if (req->path[0] != '/') strcatW( url, slash );
1682 strcatW( url, req->path );
1684 done:
1685 TRACE("url=%s\n", debugstr_w(url));
1686 return url;
1689 /***********************************************************************
1690 * HTTP_DealWithProxy
1692 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1694 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1695 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1696 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1697 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1698 static WCHAR szNul[] = { 0 };
1699 URL_COMPONENTSW UrlComponents;
1700 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1701 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1702 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1704 memset( &UrlComponents, 0, sizeof UrlComponents );
1705 UrlComponents.dwStructSize = sizeof UrlComponents;
1706 UrlComponents.lpszHostName = buf;
1707 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1709 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1710 return FALSE;
1711 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1712 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1713 sprintfW(proxy, szFormat, protoProxy);
1714 else
1715 strcpyW(proxy, protoProxy);
1716 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1717 return FALSE;
1718 if( UrlComponents.dwHostNameLength == 0 )
1719 return FALSE;
1721 if( !request->path )
1722 request->path = szNul;
1724 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1725 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1727 heap_free(session->serverName);
1728 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1729 session->serverPort = UrlComponents.nPort;
1731 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1732 return TRUE;
1735 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
1737 socklen_t addr_len;
1738 const void *addr;
1740 if(server->addr_len)
1741 return ERROR_SUCCESS;
1743 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1744 INTERNET_STATUS_RESOLVING_NAME,
1745 server->name,
1746 (strlenW(server->name)+1) * sizeof(WCHAR));
1748 addr_len = sizeof(server->addr);
1749 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1750 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1752 switch(server->addr.ss_family) {
1753 case AF_INET:
1754 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1755 break;
1756 case AF_INET6:
1757 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1758 break;
1759 default:
1760 WARN("unsupported family %d\n", server->addr.ss_family);
1761 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1764 server->addr_len = addr_len;
1765 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1766 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1767 INTERNET_STATUS_NAME_RESOLVED,
1768 server->addr_str, strlen(server->addr_str)+1);
1770 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1771 return ERROR_SUCCESS;
1774 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1776 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1777 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1778 static const WCHAR slash[] = { '/',0 };
1779 LPHTTPHEADERW host_header;
1780 LPCWSTR scheme;
1782 host_header = HTTP_GetHeader(req, hostW);
1783 if(!host_header)
1784 return FALSE;
1786 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1787 scheme = https;
1788 else
1789 scheme = http;
1790 strcpyW(buf, scheme);
1791 strcatW(buf, host_header->lpszValue);
1792 if (req->path[0] != '/')
1793 strcatW(buf, slash);
1794 strcatW(buf, req->path);
1795 return TRUE;
1799 /***********************************************************************
1800 * HTTPREQ_Destroy (internal)
1802 * Deallocate request handle
1805 static void HTTPREQ_Destroy(object_header_t *hdr)
1807 http_request_t *request = (http_request_t*) hdr;
1808 DWORD i;
1810 TRACE("\n");
1812 if(request->hCacheFile) {
1813 WCHAR url[INTERNET_MAX_URL_LENGTH];
1815 CloseHandle(request->hCacheFile);
1817 if(HTTP_GetRequestURL(request, url)) {
1818 DWORD headersLen;
1820 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1821 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1822 request->last_modified, NORMAL_CACHE_ENTRY,
1823 request->rawHeaders, headersLen, NULL, 0);
1826 heap_free(request->cacheFile);
1828 request->read_section.DebugInfo->Spare[0] = 0;
1829 DeleteCriticalSection( &request->read_section );
1830 WININET_Release(&request->session->hdr);
1832 destroy_authinfo(request->authInfo);
1833 destroy_authinfo(request->proxyAuthInfo);
1835 heap_free(request->path);
1836 heap_free(request->verb);
1837 heap_free(request->rawHeaders);
1838 heap_free(request->version);
1839 heap_free(request->statusText);
1841 for (i = 0; i < request->nCustHeaders; i++)
1843 heap_free(request->custHeaders[i].lpszField);
1844 heap_free(request->custHeaders[i].lpszValue);
1846 destroy_data_stream(request->data_stream);
1847 heap_free(request->custHeaders);
1850 static void http_release_netconn(http_request_t *req, BOOL reuse)
1852 TRACE("%p %p\n",req, req->netconn);
1854 if(!req->netconn)
1855 return;
1857 if(reuse && req->netconn->keep_alive) {
1858 BOOL run_collector;
1860 EnterCriticalSection(&connection_pool_cs);
1862 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1863 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1864 req->netconn = NULL;
1866 run_collector = !collector_running;
1867 collector_running = TRUE;
1869 LeaveCriticalSection(&connection_pool_cs);
1871 if(run_collector) {
1872 HANDLE thread = NULL;
1873 HMODULE module;
1875 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1876 if(module)
1877 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1878 if(!thread) {
1879 EnterCriticalSection(&connection_pool_cs);
1880 collector_running = FALSE;
1881 LeaveCriticalSection(&connection_pool_cs);
1883 if(module)
1884 FreeLibrary(module);
1886 else
1887 CloseHandle(thread);
1889 return;
1892 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1893 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1895 free_netconn(req->netconn);
1896 req->netconn = NULL;
1898 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1899 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1902 static void drain_content(http_request_t *req)
1904 BOOL try_reuse;
1906 if (!req->netconn) return;
1908 if (req->contentLength == -1)
1909 try_reuse = FALSE;
1910 else if(!strcmpW(req->verb, szHEAD))
1911 try_reuse = TRUE;
1912 else
1913 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1915 http_release_netconn(req, try_reuse);
1918 static BOOL HTTP_KeepAlive(http_request_t *request)
1920 WCHAR szVersion[10];
1921 WCHAR szConnectionResponse[20];
1922 DWORD dwBufferSize = sizeof(szVersion);
1923 BOOL keepalive = FALSE;
1925 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1926 * the connection is keep-alive by default */
1927 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1928 && !strcmpiW(szVersion, g_szHttp1_1))
1930 keepalive = TRUE;
1933 dwBufferSize = sizeof(szConnectionResponse);
1934 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1935 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1937 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1940 return keepalive;
1943 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1945 http_request_t *req = (http_request_t*)hdr;
1947 drain_content(req);
1950 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1952 http_request_t *req = (http_request_t*)hdr;
1954 switch(option) {
1955 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1957 http_session_t *session = req->session;
1958 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1960 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1962 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1963 return ERROR_INSUFFICIENT_BUFFER;
1964 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1965 /* FIXME: can't get a SOCKET from our connection since we don't use
1966 * winsock
1968 info->Socket = 0;
1969 /* FIXME: get source port from req->netConnection */
1970 info->SourcePort = 0;
1971 info->DestPort = session->hostPort;
1972 info->Flags = 0;
1973 if (HTTP_KeepAlive(req))
1974 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1975 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1976 info->Flags |= IDSI_FLAG_PROXY;
1977 if (req->netconn->useSSL)
1978 info->Flags |= IDSI_FLAG_SECURE;
1980 return ERROR_SUCCESS;
1983 case INTERNET_OPTION_SECURITY_FLAGS:
1985 DWORD flags;
1987 if (*size < sizeof(ULONG))
1988 return ERROR_INSUFFICIENT_BUFFER;
1990 *size = sizeof(DWORD);
1991 flags = 0;
1992 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1993 flags |= SECURITY_FLAG_SECURE;
1994 flags |= req->security_flags;
1995 if(req->netconn) {
1996 int bits = NETCON_GetCipherStrength(req->netconn);
1997 if (bits >= 128)
1998 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1999 else if (bits >= 56)
2000 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
2001 else
2002 flags |= SECURITY_FLAG_STRENGTH_WEAK;
2004 *(DWORD *)buffer = flags;
2005 return ERROR_SUCCESS;
2008 case INTERNET_OPTION_HANDLE_TYPE:
2009 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2011 if (*size < sizeof(ULONG))
2012 return ERROR_INSUFFICIENT_BUFFER;
2014 *size = sizeof(DWORD);
2015 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2016 return ERROR_SUCCESS;
2018 case INTERNET_OPTION_URL: {
2019 WCHAR url[INTERNET_MAX_URL_LENGTH];
2020 HTTPHEADERW *host;
2021 DWORD len;
2022 WCHAR *pch;
2024 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2026 TRACE("INTERNET_OPTION_URL\n");
2028 host = HTTP_GetHeader(req, hostW);
2029 strcpyW(url, httpW);
2030 strcatW(url, host->lpszValue);
2031 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2032 *pch = 0;
2033 strcatW(url, req->path);
2035 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2037 if(unicode) {
2038 len = (strlenW(url)+1) * sizeof(WCHAR);
2039 if(*size < len)
2040 return ERROR_INSUFFICIENT_BUFFER;
2042 *size = len;
2043 strcpyW(buffer, url);
2044 return ERROR_SUCCESS;
2045 }else {
2046 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2047 if(len > *size)
2048 return ERROR_INSUFFICIENT_BUFFER;
2050 *size = len;
2051 return ERROR_SUCCESS;
2055 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2056 INTERNET_CACHE_ENTRY_INFOW *info;
2057 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2058 WCHAR url[INTERNET_MAX_URL_LENGTH];
2059 DWORD nbytes, error;
2060 BOOL ret;
2062 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2064 if (*size < sizeof(*ts))
2066 *size = sizeof(*ts);
2067 return ERROR_INSUFFICIENT_BUFFER;
2069 nbytes = 0;
2070 HTTP_GetRequestURL(req, url);
2071 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2072 error = GetLastError();
2073 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2075 if (!(info = heap_alloc(nbytes)))
2076 return ERROR_OUTOFMEMORY;
2078 GetUrlCacheEntryInfoW(url, info, &nbytes);
2080 ts->ftExpires = info->ExpireTime;
2081 ts->ftLastModified = info->LastModifiedTime;
2083 heap_free(info);
2084 *size = sizeof(*ts);
2085 return ERROR_SUCCESS;
2087 return error;
2090 case INTERNET_OPTION_DATAFILE_NAME: {
2091 DWORD req_size;
2093 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2095 if(!req->cacheFile) {
2096 *size = 0;
2097 return ERROR_INTERNET_ITEM_NOT_FOUND;
2100 if(unicode) {
2101 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2102 if(*size < req_size)
2103 return ERROR_INSUFFICIENT_BUFFER;
2105 *size = req_size;
2106 memcpy(buffer, req->cacheFile, *size);
2107 return ERROR_SUCCESS;
2108 }else {
2109 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2110 if (req_size > *size)
2111 return ERROR_INSUFFICIENT_BUFFER;
2113 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2114 -1, buffer, *size, NULL, NULL);
2115 return ERROR_SUCCESS;
2119 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2120 PCCERT_CONTEXT context;
2122 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2123 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2124 return ERROR_INSUFFICIENT_BUFFER;
2127 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2128 if(context) {
2129 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2130 DWORD len;
2132 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2133 info->ftExpiry = context->pCertInfo->NotAfter;
2134 info->ftStart = context->pCertInfo->NotBefore;
2135 len = CertNameToStrA(context->dwCertEncodingType,
2136 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2137 info->lpszSubjectInfo = LocalAlloc(0, len);
2138 if(info->lpszSubjectInfo)
2139 CertNameToStrA(context->dwCertEncodingType,
2140 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2141 info->lpszSubjectInfo, len);
2142 len = CertNameToStrA(context->dwCertEncodingType,
2143 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2144 info->lpszIssuerInfo = LocalAlloc(0, len);
2145 if(info->lpszIssuerInfo)
2146 CertNameToStrA(context->dwCertEncodingType,
2147 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2148 info->lpszIssuerInfo, len);
2149 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2150 CertFreeCertificateContext(context);
2151 return ERROR_SUCCESS;
2154 case INTERNET_OPTION_CONNECT_TIMEOUT:
2155 if (*size < sizeof(DWORD))
2156 return ERROR_INSUFFICIENT_BUFFER;
2158 *size = sizeof(DWORD);
2159 *(DWORD *)buffer = req->connect_timeout;
2160 return ERROR_SUCCESS;
2163 return INET_QueryOption(hdr, option, buffer, size, unicode);
2166 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2168 http_request_t *req = (http_request_t*)hdr;
2170 switch(option) {
2171 case INTERNET_OPTION_SECURITY_FLAGS:
2173 DWORD flags;
2175 if (!buffer || size != sizeof(DWORD))
2176 return ERROR_INVALID_PARAMETER;
2177 flags = *(DWORD *)buffer;
2178 TRACE("%08x\n", flags);
2179 req->security_flags = flags;
2180 if(req->netconn)
2181 req->netconn->security_flags = flags;
2182 return ERROR_SUCCESS;
2184 case INTERNET_OPTION_CONNECT_TIMEOUT:
2185 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2186 req->connect_timeout = *(DWORD *)buffer;
2187 return ERROR_SUCCESS;
2189 case INTERNET_OPTION_SEND_TIMEOUT:
2190 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2191 req->send_timeout = *(DWORD *)buffer;
2192 return ERROR_SUCCESS;
2194 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2195 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2196 req->receive_timeout = *(DWORD *)buffer;
2197 return ERROR_SUCCESS;
2199 case INTERNET_OPTION_USERNAME:
2200 heap_free(req->session->userName);
2201 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2202 return ERROR_SUCCESS;
2204 case INTERNET_OPTION_PASSWORD:
2205 heap_free(req->session->password);
2206 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2207 return ERROR_SUCCESS;
2208 case INTERNET_OPTION_HTTP_DECODING:
2209 if(size != sizeof(BOOL))
2210 return ERROR_INVALID_PARAMETER;
2211 req->decoding = *(BOOL*)buffer;
2212 return ERROR_SUCCESS;
2215 return ERROR_INTERNET_INVALID_OPTION;
2218 /* read some more data into the read buffer (the read section must be held) */
2219 static DWORD read_more_data( http_request_t *req, int maxlen )
2221 DWORD res;
2222 int len;
2224 if (req->read_pos)
2226 /* move existing data to the start of the buffer */
2227 if(req->read_size)
2228 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2229 req->read_pos = 0;
2232 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2234 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2235 maxlen - req->read_size, 0, &len );
2236 if(res == ERROR_SUCCESS)
2237 req->read_size += len;
2239 return res;
2242 /* remove some amount of data from the read buffer (the read section must be held) */
2243 static void remove_data( http_request_t *req, int count )
2245 if (!(req->read_size -= count)) req->read_pos = 0;
2246 else req->read_pos += count;
2249 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2251 int count, bytes_read, pos = 0;
2252 DWORD res;
2254 EnterCriticalSection( &req->read_section );
2255 for (;;)
2257 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2259 if (eol)
2261 count = eol - (req->read_buf + req->read_pos);
2262 bytes_read = count + 1;
2264 else count = bytes_read = req->read_size;
2266 count = min( count, *len - pos );
2267 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2268 pos += count;
2269 remove_data( req, bytes_read );
2270 if (eol) break;
2272 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2274 *len = 0;
2275 TRACE( "returning empty string %u\n", res);
2276 LeaveCriticalSection( &req->read_section );
2277 INTERNET_SetLastError(res);
2278 return FALSE;
2281 LeaveCriticalSection( &req->read_section );
2283 if (pos < *len)
2285 if (pos && buffer[pos - 1] == '\r') pos--;
2286 *len = pos + 1;
2288 buffer[*len - 1] = 0;
2289 TRACE( "returning %s\n", debugstr_a(buffer));
2290 return TRUE;
2293 /* check if we have reached the end of the data to read (the read section must be held) */
2294 static BOOL end_of_read_data( http_request_t *req )
2296 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2299 /* fetch some more data into the read buffer (the read section must be held) */
2300 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2302 DWORD res, read=0;
2304 if(req->read_size == sizeof(req->read_buf))
2305 return ERROR_SUCCESS;
2307 if(req->read_pos) {
2308 if(req->read_size)
2309 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2310 req->read_pos = 0;
2313 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2314 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2315 req->read_size += read;
2317 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2318 if(read_bytes)
2319 *read_bytes = read;
2320 return res;
2323 /* return the size of data available to be read immediately (the read section must be held) */
2324 static DWORD get_avail_data( http_request_t *req )
2326 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2329 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2331 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2332 DWORD avail = 0;
2334 if(req->netconn)
2335 NETCON_query_data_available(req->netconn, &avail);
2336 return netconn_stream->content_length == ~0u
2337 ? avail
2338 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2341 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2343 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2344 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2347 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2348 DWORD *read, read_mode_t read_mode)
2350 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2351 int len = 0;
2353 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2355 if(read_mode == READMODE_NOBLOCK)
2356 size = min(size, netconn_get_avail_data(stream, req));
2358 if(size && req->netconn) {
2359 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2360 len = 0;
2361 if(!len)
2362 netconn_stream->content_length = netconn_stream->content_read;
2365 netconn_stream->content_read += *read = len;
2366 TRACE("read %u bytes\n", len);
2367 return ERROR_SUCCESS;
2370 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2372 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2373 BYTE buf[1024];
2374 DWORD avail;
2375 int len;
2377 if(netconn_end_of_data(stream, req))
2378 return TRUE;
2380 do {
2381 avail = netconn_get_avail_data(stream, req);
2382 if(!avail)
2383 return FALSE;
2385 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2386 return FALSE;
2388 netconn_stream->content_read += len;
2389 }while(netconn_stream->content_read < netconn_stream->content_length);
2391 return TRUE;
2394 static void netconn_destroy(data_stream_t *stream)
2398 static const data_stream_vtbl_t netconn_stream_vtbl = {
2399 netconn_get_avail_data,
2400 netconn_end_of_data,
2401 netconn_read,
2402 netconn_drain_content,
2403 netconn_destroy
2406 /* read some more data into the read buffer (the read section must be held) */
2407 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2409 DWORD res;
2410 int len;
2412 if (stream->buf_pos)
2414 /* move existing data to the start of the buffer */
2415 if(stream->buf_size)
2416 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2417 stream->buf_pos = 0;
2420 if (maxlen == -1) maxlen = sizeof(stream->buf);
2422 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2423 maxlen - stream->buf_size, 0, &len );
2424 if(res == ERROR_SUCCESS)
2425 stream->buf_size += len;
2427 return res;
2430 /* remove some amount of data from the read buffer (the read section must be held) */
2431 static void remove_chunked_data(chunked_stream_t *stream, int count)
2433 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2434 else stream->buf_pos += count;
2437 /* discard data contents until we reach end of line (the read section must be held) */
2438 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2440 DWORD res;
2444 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2445 if (eol)
2447 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2448 break;
2450 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2451 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2452 } while (stream->buf_size);
2453 return ERROR_SUCCESS;
2456 /* read the size of the next chunk (the read section must be held) */
2457 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2459 /* TODOO */
2460 DWORD chunk_size = 0, res;
2462 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2463 return res;
2465 for (;;)
2467 while (stream->buf_size)
2469 char ch = stream->buf[stream->buf_pos];
2470 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2471 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2472 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2473 else if (ch == ';' || ch == '\r' || ch == '\n')
2475 TRACE( "reading %u byte chunk\n", chunk_size );
2476 stream->chunk_size = chunk_size;
2477 req->contentLength += chunk_size;
2478 return discard_chunked_eol(stream, req);
2480 remove_chunked_data(stream, 1);
2482 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2483 if (!stream->buf_size)
2485 stream->chunk_size = 0;
2486 return ERROR_SUCCESS;
2491 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2493 /* Allow reading only from read buffer */
2494 return 0;
2497 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2499 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2500 return !chunked_stream->chunk_size;
2503 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2504 DWORD *read, read_mode_t read_mode)
2506 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2507 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2509 if(chunked_stream->chunk_size == ~0u) {
2510 res = start_next_chunk(chunked_stream, req);
2511 if(res != ERROR_SUCCESS)
2512 return res;
2515 while(size && chunked_stream->chunk_size) {
2516 if(chunked_stream->buf_size) {
2517 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2519 /* this could block */
2520 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2521 break;
2523 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2524 remove_chunked_data(chunked_stream, read_bytes);
2525 }else {
2526 read_bytes = min(size, chunked_stream->chunk_size);
2528 if(read_mode == READMODE_NOBLOCK) {
2529 DWORD avail;
2531 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2532 break;
2533 if(read_bytes > avail)
2534 read_bytes = avail;
2536 /* this could block */
2537 if(read_bytes == chunked_stream->chunk_size)
2538 break;
2541 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2542 if(res != ERROR_SUCCESS)
2543 break;
2546 chunked_stream->chunk_size -= read_bytes;
2547 size -= read_bytes;
2548 ret_read += read_bytes;
2549 if(!chunked_stream->chunk_size) {
2550 assert(read_mode != READMODE_NOBLOCK);
2551 res = start_next_chunk(chunked_stream, req);
2552 if(res != ERROR_SUCCESS)
2553 break;
2556 if(read_mode == READMODE_ASYNC)
2557 read_mode = READMODE_NOBLOCK;
2560 TRACE("read %u bytes\n", ret_read);
2561 *read = ret_read;
2562 return res;
2565 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2567 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2569 /* FIXME: we can do better */
2570 return !chunked_stream->chunk_size;
2573 static void chunked_destroy(data_stream_t *stream)
2575 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2576 heap_free(chunked_stream);
2579 static const data_stream_vtbl_t chunked_stream_vtbl = {
2580 chunked_get_avail_data,
2581 chunked_end_of_data,
2582 chunked_read,
2583 chunked_drain_content,
2584 chunked_destroy
2587 /* set the request content length based on the headers */
2588 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2590 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2591 WCHAR encoding[20];
2592 DWORD size;
2594 if(status_code == HTTP_STATUS_NO_CONTENT) {
2595 request->contentLength = request->netconn_stream.content_length = 0;
2596 return ERROR_SUCCESS;
2599 size = sizeof(request->contentLength);
2600 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2601 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2602 request->contentLength = ~0u;
2603 request->netconn_stream.content_length = request->contentLength;
2604 request->netconn_stream.content_read = request->read_size;
2606 size = sizeof(encoding);
2607 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2608 !strcmpiW(encoding, szChunked))
2610 chunked_stream_t *chunked_stream;
2612 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2613 if(!chunked_stream)
2614 return ERROR_OUTOFMEMORY;
2616 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2617 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2618 chunked_stream->chunk_size = ~0u;
2620 if(request->read_size) {
2621 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2622 chunked_stream->buf_size = request->read_size;
2623 request->read_size = request->read_pos = 0;
2626 request->data_stream = &chunked_stream->data_stream;
2627 request->contentLength = ~0u;
2628 request->read_chunked = TRUE;
2631 if(request->decoding) {
2632 int encoding_idx;
2634 static const WCHAR gzipW[] = {'g','z','i','p',0};
2636 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2637 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2638 return init_gzip_stream(request);
2641 return ERROR_SUCCESS;
2644 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2646 INTERNET_ASYNC_RESULT iar;
2648 iar.dwResult = result;
2649 iar.dwError = error;
2651 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2652 sizeof(INTERNET_ASYNC_RESULT));
2655 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2657 DWORD res, read = 0, avail = 0;
2658 read_mode_t mode;
2660 TRACE("%p\n", req);
2662 EnterCriticalSection( &req->read_section );
2664 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2665 res = refill_read_buffer(req, mode, &read);
2666 if(res == ERROR_SUCCESS && !first_notif)
2667 avail = get_avail_data(req);
2669 LeaveCriticalSection( &req->read_section );
2671 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2672 WARN("res %u read %u, closing connection\n", res, read);
2673 http_release_netconn(req, FALSE);
2676 if(res == ERROR_SUCCESS)
2677 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2678 else
2679 send_request_complete(req, 0, res);
2682 /* read data from the http connection (the read section must be held) */
2683 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2685 DWORD current_read = 0, ret_read = 0;
2686 read_mode_t read_mode;
2687 DWORD res = ERROR_SUCCESS;
2689 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2691 EnterCriticalSection( &req->read_section );
2693 if(req->read_size) {
2694 ret_read = min(size, req->read_size);
2695 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2696 req->read_size -= ret_read;
2697 req->read_pos += ret_read;
2698 if(read_mode == READMODE_ASYNC)
2699 read_mode = READMODE_NOBLOCK;
2702 if(ret_read < size) {
2703 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2704 ret_read += current_read;
2707 LeaveCriticalSection( &req->read_section );
2709 *read = ret_read;
2710 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2712 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2713 BOOL res;
2714 DWORD written;
2716 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2717 if(!res)
2718 WARN("WriteFile failed: %u\n", GetLastError());
2721 if(size && !ret_read)
2722 http_release_netconn(req, res == ERROR_SUCCESS);
2724 return res;
2728 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2730 http_request_t *req = (http_request_t*)hdr;
2731 DWORD res;
2733 EnterCriticalSection( &req->read_section );
2734 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2735 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2737 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2738 if(res == ERROR_SUCCESS)
2739 res = hdr->dwError;
2740 LeaveCriticalSection( &req->read_section );
2742 return res;
2745 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2747 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2748 http_request_t *req = (http_request_t*)workRequest->hdr;
2749 DWORD res;
2751 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2753 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2754 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2756 send_request_complete(req, res == ERROR_SUCCESS, res);
2759 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2760 DWORD flags, DWORD_PTR context)
2762 http_request_t *req = (http_request_t*)hdr;
2763 DWORD res, size, read, error = ERROR_SUCCESS;
2765 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2766 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2768 if (buffers->dwStructSize != sizeof(*buffers))
2769 return ERROR_INVALID_PARAMETER;
2771 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2773 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2775 WORKREQUEST workRequest;
2777 if (TryEnterCriticalSection( &req->read_section ))
2779 if (get_avail_data(req))
2781 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2782 &buffers->dwBufferLength, FALSE);
2783 size = buffers->dwBufferLength;
2784 LeaveCriticalSection( &req->read_section );
2785 goto done;
2787 LeaveCriticalSection( &req->read_section );
2790 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2791 workRequest.hdr = WININET_AddRef(&req->hdr);
2792 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2794 INTERNET_AsyncCall(&workRequest);
2796 return ERROR_IO_PENDING;
2799 read = 0;
2800 size = buffers->dwBufferLength;
2802 EnterCriticalSection( &req->read_section );
2803 if(hdr->dwError == ERROR_SUCCESS)
2804 hdr->dwError = INTERNET_HANDLE_IN_USE;
2805 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2806 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2808 while(1) {
2809 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2810 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2811 if(res != ERROR_SUCCESS)
2812 break;
2814 read += buffers->dwBufferLength;
2815 if(read == size || end_of_read_data(req))
2816 break;
2818 LeaveCriticalSection( &req->read_section );
2820 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2821 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2822 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2823 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2825 EnterCriticalSection( &req->read_section );
2828 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2829 hdr->dwError = ERROR_SUCCESS;
2830 else
2831 error = hdr->dwError;
2833 LeaveCriticalSection( &req->read_section );
2834 size = buffers->dwBufferLength;
2835 buffers->dwBufferLength = read;
2837 done:
2838 if (res == ERROR_SUCCESS) {
2839 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2840 &size, sizeof(size));
2843 return res==ERROR_SUCCESS ? error : res;
2846 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2848 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2849 http_request_t *req = (http_request_t*)workRequest->hdr;
2850 DWORD res;
2852 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2854 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2855 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2857 send_request_complete(req, res == ERROR_SUCCESS, res);
2860 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2861 DWORD flags, DWORD_PTR context)
2864 http_request_t *req = (http_request_t*)hdr;
2865 DWORD res, size, read, error = ERROR_SUCCESS;
2867 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2868 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2870 if (buffers->dwStructSize != sizeof(*buffers))
2871 return ERROR_INVALID_PARAMETER;
2873 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2875 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2877 WORKREQUEST workRequest;
2879 if (TryEnterCriticalSection( &req->read_section ))
2881 if (get_avail_data(req))
2883 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2884 &buffers->dwBufferLength, FALSE);
2885 size = buffers->dwBufferLength;
2886 LeaveCriticalSection( &req->read_section );
2887 goto done;
2889 LeaveCriticalSection( &req->read_section );
2892 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2893 workRequest.hdr = WININET_AddRef(&req->hdr);
2894 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2896 INTERNET_AsyncCall(&workRequest);
2898 return ERROR_IO_PENDING;
2901 read = 0;
2902 size = buffers->dwBufferLength;
2904 EnterCriticalSection( &req->read_section );
2905 if(hdr->dwError == ERROR_SUCCESS)
2906 hdr->dwError = INTERNET_HANDLE_IN_USE;
2907 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2908 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2910 while(1) {
2911 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2912 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2913 if(res != ERROR_SUCCESS)
2914 break;
2916 read += buffers->dwBufferLength;
2917 if(read == size || end_of_read_data(req))
2918 break;
2920 LeaveCriticalSection( &req->read_section );
2922 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2923 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2924 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2925 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2927 EnterCriticalSection( &req->read_section );
2930 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2931 hdr->dwError = ERROR_SUCCESS;
2932 else
2933 error = hdr->dwError;
2935 LeaveCriticalSection( &req->read_section );
2936 size = buffers->dwBufferLength;
2937 buffers->dwBufferLength = read;
2939 done:
2940 if (res == ERROR_SUCCESS) {
2941 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2942 &size, sizeof(size));
2945 return res==ERROR_SUCCESS ? error : res;
2948 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2950 DWORD res;
2951 http_request_t *request = (http_request_t*)hdr;
2953 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2955 *written = 0;
2956 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2957 if (res == ERROR_SUCCESS)
2958 request->bytesWritten += *written;
2960 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2961 return res;
2964 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2966 http_request_t *req = (http_request_t*)workRequest->hdr;
2968 HTTP_ReceiveRequestData(req, FALSE);
2971 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2973 http_request_t *req = (http_request_t*)hdr;
2975 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2977 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2979 WORKREQUEST workRequest;
2981 /* never wait, if we can't enter the section we queue an async request right away */
2982 if (TryEnterCriticalSection( &req->read_section ))
2984 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2985 if ((*available = get_avail_data( req ))) goto done;
2986 if (end_of_read_data( req )) goto done;
2987 LeaveCriticalSection( &req->read_section );
2990 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2991 workRequest.hdr = WININET_AddRef( &req->hdr );
2993 INTERNET_AsyncCall(&workRequest);
2995 return ERROR_IO_PENDING;
2998 EnterCriticalSection( &req->read_section );
3000 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3002 refill_read_buffer( req, READMODE_ASYNC, NULL );
3003 *available = get_avail_data( req );
3006 done:
3007 LeaveCriticalSection( &req->read_section );
3009 TRACE( "returning %u\n", *available );
3010 return ERROR_SUCCESS;
3013 static const object_vtbl_t HTTPREQVtbl = {
3014 HTTPREQ_Destroy,
3015 HTTPREQ_CloseConnection,
3016 HTTPREQ_QueryOption,
3017 HTTPREQ_SetOption,
3018 HTTPREQ_ReadFile,
3019 HTTPREQ_ReadFileExA,
3020 HTTPREQ_ReadFileExW,
3021 HTTPREQ_WriteFile,
3022 HTTPREQ_QueryDataAvailable,
3023 NULL
3026 /***********************************************************************
3027 * HTTP_HttpOpenRequestW (internal)
3029 * Open a HTTP request handle
3031 * RETURNS
3032 * HINTERNET a HTTP request handle on success
3033 * NULL on failure
3036 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3037 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3038 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3039 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3041 appinfo_t *hIC = session->appInfo;
3042 http_request_t *request;
3043 DWORD len, res = ERROR_SUCCESS;
3045 TRACE("-->\n");
3047 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3048 if(!request)
3049 return ERROR_OUTOFMEMORY;
3051 request->hdr.htype = WH_HHTTPREQ;
3052 request->hdr.dwFlags = dwFlags;
3053 request->hdr.dwContext = dwContext;
3054 request->contentLength = ~0u;
3056 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3057 request->data_stream = &request->netconn_stream.data_stream;
3058 request->connect_timeout = session->connect_timeout;
3059 request->send_timeout = session->send_timeout;
3060 request->receive_timeout = session->receive_timeout;
3062 InitializeCriticalSection( &request->read_section );
3063 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3065 WININET_AddRef( &session->hdr );
3066 request->session = session;
3067 list_add_head( &session->hdr.children, &request->hdr.entry );
3069 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3070 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3071 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3072 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3074 if (lpszObjectName && *lpszObjectName) {
3075 HRESULT rc;
3077 len = 0;
3078 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3079 if (rc != E_POINTER)
3080 len = strlenW(lpszObjectName)+1;
3081 request->path = heap_alloc(len*sizeof(WCHAR));
3082 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3083 URL_ESCAPE_SPACES_ONLY);
3084 if (rc != S_OK)
3086 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3087 strcpyW(request->path,lpszObjectName);
3089 }else {
3090 static const WCHAR slashW[] = {'/',0};
3092 request->path = heap_strdupW(slashW);
3095 if (lpszReferrer && *lpszReferrer)
3096 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3098 if (lpszAcceptTypes)
3100 int i;
3101 for (i = 0; lpszAcceptTypes[i]; i++)
3103 if (!*lpszAcceptTypes[i]) continue;
3104 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3105 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3106 HTTP_ADDHDR_FLAG_REQ |
3107 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3111 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3112 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3114 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3115 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3116 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3118 WCHAR *host_name;
3120 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3122 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3123 if (!host_name) {
3124 res = ERROR_OUTOFMEMORY;
3125 goto lend;
3128 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3129 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3130 heap_free(host_name);
3132 else
3133 HTTP_ProcessHeader(request, hostW, session->hostName,
3134 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3136 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3137 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3138 INTERNET_DEFAULT_HTTPS_PORT :
3139 INTERNET_DEFAULT_HTTP_PORT);
3141 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3142 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3143 INTERNET_DEFAULT_HTTPS_PORT :
3144 INTERNET_DEFAULT_HTTP_PORT);
3146 if (hIC->proxy && hIC->proxy[0])
3147 HTTP_DealWithProxy( hIC, session, request );
3149 INTERNET_SendCallback(&session->hdr, dwContext,
3150 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3151 sizeof(HINTERNET));
3153 lend:
3154 TRACE("<-- %u (%p)\n", res, request);
3156 if(res != ERROR_SUCCESS) {
3157 WININET_Release( &request->hdr );
3158 *ret = NULL;
3159 return res;
3162 *ret = request->hdr.hInternet;
3163 return ERROR_SUCCESS;
3166 /***********************************************************************
3167 * HttpOpenRequestW (WININET.@)
3169 * Open a HTTP request handle
3171 * RETURNS
3172 * HINTERNET a HTTP request handle on success
3173 * NULL on failure
3176 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3177 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3178 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3179 DWORD dwFlags, DWORD_PTR dwContext)
3181 http_session_t *session;
3182 HINTERNET handle = NULL;
3183 DWORD res;
3185 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3186 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3187 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3188 dwFlags, dwContext);
3189 if(lpszAcceptTypes!=NULL)
3191 int i;
3192 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3193 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3196 session = (http_session_t*) get_handle_object( hHttpSession );
3197 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3199 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3200 goto lend;
3204 * My tests seem to show that the windows version does not
3205 * become asynchronous until after this point. And anyhow
3206 * if this call was asynchronous then how would you get the
3207 * necessary HINTERNET pointer returned by this function.
3210 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3211 lpszVersion, lpszReferrer, lpszAcceptTypes,
3212 dwFlags, dwContext, &handle);
3213 lend:
3214 if( session )
3215 WININET_Release( &session->hdr );
3216 TRACE("returning %p\n", handle);
3217 if(res != ERROR_SUCCESS)
3218 SetLastError(res);
3219 return handle;
3222 static const LPCWSTR header_lookup[] = {
3223 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3224 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3225 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3226 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3227 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3228 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3229 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3230 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3231 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3232 szDate, /* HTTP_QUERY_DATE = 9 */
3233 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3234 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3235 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3236 szURI, /* HTTP_QUERY_URI = 13 */
3237 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3238 NULL, /* HTTP_QUERY_COST = 15 */
3239 NULL, /* HTTP_QUERY_LINK = 16 */
3240 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3241 NULL, /* HTTP_QUERY_VERSION = 18 */
3242 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3243 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3244 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3245 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3246 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3247 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3248 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3249 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3250 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3251 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3252 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3253 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3254 NULL, /* HTTP_QUERY_FROM = 31 */
3255 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3256 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3257 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3258 szReferer, /* HTTP_QUERY_REFERER = 35 */
3259 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3260 szServer, /* HTTP_QUERY_SERVER = 37 */
3261 NULL, /* HTTP_TITLE = 38 */
3262 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3263 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3264 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3265 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3266 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3267 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3268 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3269 NULL, /* HTTP_QUERY_REFRESH = 46 */
3270 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3271 szAge, /* HTTP_QUERY_AGE = 48 */
3272 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3273 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3274 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3275 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3276 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3277 szETag, /* HTTP_QUERY_ETAG = 54 */
3278 hostW, /* HTTP_QUERY_HOST = 55 */
3279 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3280 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3281 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3282 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3283 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3284 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3285 szRange, /* HTTP_QUERY_RANGE = 62 */
3286 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3287 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3288 szVary, /* HTTP_QUERY_VARY = 65 */
3289 szVia, /* HTTP_QUERY_VIA = 66 */
3290 szWarning, /* HTTP_QUERY_WARNING = 67 */
3291 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3292 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3293 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3296 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3298 /***********************************************************************
3299 * HTTP_HttpQueryInfoW (internal)
3301 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3302 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3304 LPHTTPHEADERW lphttpHdr = NULL;
3305 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3306 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3307 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3308 INT index = -1;
3310 /* Find requested header structure */
3311 switch (level)
3313 case HTTP_QUERY_CUSTOM:
3314 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3315 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3316 break;
3317 case HTTP_QUERY_RAW_HEADERS_CRLF:
3319 LPWSTR headers;
3320 DWORD len = 0;
3321 DWORD res = ERROR_INVALID_PARAMETER;
3323 if (request_only)
3324 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3325 else
3326 headers = request->rawHeaders;
3328 if (headers)
3329 len = strlenW(headers) * sizeof(WCHAR);
3331 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3333 len += sizeof(WCHAR);
3334 res = ERROR_INSUFFICIENT_BUFFER;
3336 else if (lpBuffer)
3338 if (headers)
3339 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3340 else
3342 len = strlenW(szCrLf) * sizeof(WCHAR);
3343 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3345 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3346 res = ERROR_SUCCESS;
3348 *lpdwBufferLength = len;
3350 if (request_only) heap_free(headers);
3351 return res;
3353 case HTTP_QUERY_RAW_HEADERS:
3355 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3356 DWORD i, size = 0;
3357 LPWSTR pszString = lpBuffer;
3359 for (i = 0; ppszRawHeaderLines[i]; i++)
3360 size += strlenW(ppszRawHeaderLines[i]) + 1;
3362 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3364 HTTP_FreeTokens(ppszRawHeaderLines);
3365 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3366 return ERROR_INSUFFICIENT_BUFFER;
3368 if (pszString)
3370 for (i = 0; ppszRawHeaderLines[i]; i++)
3372 DWORD len = strlenW(ppszRawHeaderLines[i]);
3373 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3374 pszString += len+1;
3376 *pszString = '\0';
3377 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3379 *lpdwBufferLength = size * sizeof(WCHAR);
3380 HTTP_FreeTokens(ppszRawHeaderLines);
3382 return ERROR_SUCCESS;
3384 case HTTP_QUERY_STATUS_TEXT:
3385 if (request->statusText)
3387 DWORD len = strlenW(request->statusText);
3388 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3390 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3391 return ERROR_INSUFFICIENT_BUFFER;
3393 if (lpBuffer)
3395 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3396 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3398 *lpdwBufferLength = len * sizeof(WCHAR);
3399 return ERROR_SUCCESS;
3401 break;
3402 case HTTP_QUERY_VERSION:
3403 if (request->version)
3405 DWORD len = strlenW(request->version);
3406 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3408 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3409 return ERROR_INSUFFICIENT_BUFFER;
3411 if (lpBuffer)
3413 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3414 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3416 *lpdwBufferLength = len * sizeof(WCHAR);
3417 return ERROR_SUCCESS;
3419 break;
3420 case HTTP_QUERY_CONTENT_ENCODING:
3421 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3422 requested_index,request_only);
3423 break;
3424 default:
3425 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3427 if (level < LAST_TABLE_HEADER && header_lookup[level])
3428 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3429 requested_index,request_only);
3432 if (index >= 0)
3433 lphttpHdr = &request->custHeaders[index];
3435 /* Ensure header satisfies requested attributes */
3436 if (!lphttpHdr ||
3437 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3438 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3440 return ERROR_HTTP_HEADER_NOT_FOUND;
3443 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3445 /* coalesce value to requested type */
3446 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3448 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3449 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3451 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3453 time_t tmpTime;
3454 struct tm tmpTM;
3455 SYSTEMTIME *STHook;
3457 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3459 tmpTM = *gmtime(&tmpTime);
3460 STHook = (SYSTEMTIME *)lpBuffer;
3461 STHook->wDay = tmpTM.tm_mday;
3462 STHook->wHour = tmpTM.tm_hour;
3463 STHook->wMilliseconds = 0;
3464 STHook->wMinute = tmpTM.tm_min;
3465 STHook->wDayOfWeek = tmpTM.tm_wday;
3466 STHook->wMonth = tmpTM.tm_mon + 1;
3467 STHook->wSecond = tmpTM.tm_sec;
3468 STHook->wYear = tmpTM.tm_year;
3470 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3471 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3472 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3474 else if (lphttpHdr->lpszValue)
3476 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3478 if (len > *lpdwBufferLength)
3480 *lpdwBufferLength = len;
3481 return ERROR_INSUFFICIENT_BUFFER;
3483 if (lpBuffer)
3485 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3486 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3488 *lpdwBufferLength = len - sizeof(WCHAR);
3490 return ERROR_SUCCESS;
3493 /***********************************************************************
3494 * HttpQueryInfoW (WININET.@)
3496 * Queries for information about an HTTP request
3498 * RETURNS
3499 * TRUE on success
3500 * FALSE on failure
3503 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3504 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3506 http_request_t *request;
3507 DWORD res;
3509 if (TRACE_ON(wininet)) {
3510 #define FE(x) { x, #x }
3511 static const wininet_flag_info query_flags[] = {
3512 FE(HTTP_QUERY_MIME_VERSION),
3513 FE(HTTP_QUERY_CONTENT_TYPE),
3514 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3515 FE(HTTP_QUERY_CONTENT_ID),
3516 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3517 FE(HTTP_QUERY_CONTENT_LENGTH),
3518 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3519 FE(HTTP_QUERY_ALLOW),
3520 FE(HTTP_QUERY_PUBLIC),
3521 FE(HTTP_QUERY_DATE),
3522 FE(HTTP_QUERY_EXPIRES),
3523 FE(HTTP_QUERY_LAST_MODIFIED),
3524 FE(HTTP_QUERY_MESSAGE_ID),
3525 FE(HTTP_QUERY_URI),
3526 FE(HTTP_QUERY_DERIVED_FROM),
3527 FE(HTTP_QUERY_COST),
3528 FE(HTTP_QUERY_LINK),
3529 FE(HTTP_QUERY_PRAGMA),
3530 FE(HTTP_QUERY_VERSION),
3531 FE(HTTP_QUERY_STATUS_CODE),
3532 FE(HTTP_QUERY_STATUS_TEXT),
3533 FE(HTTP_QUERY_RAW_HEADERS),
3534 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3535 FE(HTTP_QUERY_CONNECTION),
3536 FE(HTTP_QUERY_ACCEPT),
3537 FE(HTTP_QUERY_ACCEPT_CHARSET),
3538 FE(HTTP_QUERY_ACCEPT_ENCODING),
3539 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3540 FE(HTTP_QUERY_AUTHORIZATION),
3541 FE(HTTP_QUERY_CONTENT_ENCODING),
3542 FE(HTTP_QUERY_FORWARDED),
3543 FE(HTTP_QUERY_FROM),
3544 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3545 FE(HTTP_QUERY_LOCATION),
3546 FE(HTTP_QUERY_ORIG_URI),
3547 FE(HTTP_QUERY_REFERER),
3548 FE(HTTP_QUERY_RETRY_AFTER),
3549 FE(HTTP_QUERY_SERVER),
3550 FE(HTTP_QUERY_TITLE),
3551 FE(HTTP_QUERY_USER_AGENT),
3552 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3553 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3554 FE(HTTP_QUERY_ACCEPT_RANGES),
3555 FE(HTTP_QUERY_SET_COOKIE),
3556 FE(HTTP_QUERY_COOKIE),
3557 FE(HTTP_QUERY_REQUEST_METHOD),
3558 FE(HTTP_QUERY_REFRESH),
3559 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3560 FE(HTTP_QUERY_AGE),
3561 FE(HTTP_QUERY_CACHE_CONTROL),
3562 FE(HTTP_QUERY_CONTENT_BASE),
3563 FE(HTTP_QUERY_CONTENT_LOCATION),
3564 FE(HTTP_QUERY_CONTENT_MD5),
3565 FE(HTTP_QUERY_CONTENT_RANGE),
3566 FE(HTTP_QUERY_ETAG),
3567 FE(HTTP_QUERY_HOST),
3568 FE(HTTP_QUERY_IF_MATCH),
3569 FE(HTTP_QUERY_IF_NONE_MATCH),
3570 FE(HTTP_QUERY_IF_RANGE),
3571 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3572 FE(HTTP_QUERY_MAX_FORWARDS),
3573 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3574 FE(HTTP_QUERY_RANGE),
3575 FE(HTTP_QUERY_TRANSFER_ENCODING),
3576 FE(HTTP_QUERY_UPGRADE),
3577 FE(HTTP_QUERY_VARY),
3578 FE(HTTP_QUERY_VIA),
3579 FE(HTTP_QUERY_WARNING),
3580 FE(HTTP_QUERY_CUSTOM)
3582 static const wininet_flag_info modifier_flags[] = {
3583 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3584 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3585 FE(HTTP_QUERY_FLAG_NUMBER),
3586 FE(HTTP_QUERY_FLAG_COALESCE)
3588 #undef FE
3589 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3590 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3591 DWORD i;
3593 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3594 TRACE(" Attribute:");
3595 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3596 if (query_flags[i].val == info) {
3597 TRACE(" %s", query_flags[i].name);
3598 break;
3601 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3602 TRACE(" Unknown (%08x)", info);
3605 TRACE(" Modifier:");
3606 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3607 if (modifier_flags[i].val & info_mod) {
3608 TRACE(" %s", modifier_flags[i].name);
3609 info_mod &= ~ modifier_flags[i].val;
3613 if (info_mod) {
3614 TRACE(" Unknown (%08x)", info_mod);
3616 TRACE("\n");
3619 request = (http_request_t*) get_handle_object( hHttpRequest );
3620 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3622 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3623 goto lend;
3626 if (lpBuffer == NULL)
3627 *lpdwBufferLength = 0;
3628 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3629 lpBuffer, lpdwBufferLength, lpdwIndex);
3631 lend:
3632 if( request )
3633 WININET_Release( &request->hdr );
3635 TRACE("%u <--\n", res);
3636 if(res != ERROR_SUCCESS)
3637 SetLastError(res);
3638 return res == ERROR_SUCCESS;
3641 /***********************************************************************
3642 * HttpQueryInfoA (WININET.@)
3644 * Queries for information about an HTTP request
3646 * RETURNS
3647 * TRUE on success
3648 * FALSE on failure
3651 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3652 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3654 BOOL result;
3655 DWORD len;
3656 WCHAR* bufferW;
3658 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3659 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3661 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3662 lpdwBufferLength, lpdwIndex );
3665 if (lpBuffer)
3667 DWORD alloclen;
3668 len = (*lpdwBufferLength)*sizeof(WCHAR);
3669 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3671 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3672 if (alloclen < len)
3673 alloclen = len;
3675 else
3676 alloclen = len;
3677 bufferW = heap_alloc(alloclen);
3678 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3679 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3680 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3681 } else
3683 bufferW = NULL;
3684 len = 0;
3687 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3688 &len, lpdwIndex );
3689 if( result )
3691 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3692 lpBuffer, *lpdwBufferLength, NULL, NULL );
3693 *lpdwBufferLength = len - 1;
3695 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3697 else
3698 /* since the strings being returned from HttpQueryInfoW should be
3699 * only ASCII characters, it is reasonable to assume that all of
3700 * the Unicode characters can be reduced to a single byte */
3701 *lpdwBufferLength = len / sizeof(WCHAR);
3703 heap_free( bufferW );
3704 return result;
3707 /***********************************************************************
3708 * HTTP_GetRedirectURL (internal)
3710 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3712 static WCHAR szHttp[] = {'h','t','t','p',0};
3713 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3714 http_session_t *session = request->session;
3715 URL_COMPONENTSW urlComponents;
3716 DWORD url_length = 0;
3717 LPWSTR orig_url;
3718 LPWSTR combined_url;
3720 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3721 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3722 urlComponents.dwSchemeLength = 0;
3723 urlComponents.lpszHostName = session->hostName;
3724 urlComponents.dwHostNameLength = 0;
3725 urlComponents.nPort = session->hostPort;
3726 urlComponents.lpszUserName = session->userName;
3727 urlComponents.dwUserNameLength = 0;
3728 urlComponents.lpszPassword = NULL;
3729 urlComponents.dwPasswordLength = 0;
3730 urlComponents.lpszUrlPath = request->path;
3731 urlComponents.dwUrlPathLength = 0;
3732 urlComponents.lpszExtraInfo = NULL;
3733 urlComponents.dwExtraInfoLength = 0;
3735 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3736 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3737 return NULL;
3739 orig_url = heap_alloc(url_length);
3741 /* convert from bytes to characters */
3742 url_length = url_length / sizeof(WCHAR) - 1;
3743 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3745 heap_free(orig_url);
3746 return NULL;
3749 url_length = 0;
3750 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3751 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3753 heap_free(orig_url);
3754 return NULL;
3756 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3758 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3760 heap_free(orig_url);
3761 heap_free(combined_url);
3762 return NULL;
3764 heap_free(orig_url);
3765 return combined_url;
3769 /***********************************************************************
3770 * HTTP_HandleRedirect (internal)
3772 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3774 http_session_t *session = request->session;
3775 appinfo_t *hIC = session->appInfo;
3776 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3777 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3778 int index;
3780 if(lpszUrl[0]=='/')
3782 /* if it's an absolute path, keep the same session info */
3783 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3785 else
3787 URL_COMPONENTSW urlComponents;
3788 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3789 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3790 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3791 static WCHAR szHttp[] = {'h','t','t','p',0};
3792 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3794 userName[0] = 0;
3795 hostName[0] = 0;
3796 protocol[0] = 0;
3798 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3799 urlComponents.lpszScheme = protocol;
3800 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3801 urlComponents.lpszHostName = hostName;
3802 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3803 urlComponents.lpszUserName = userName;
3804 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3805 urlComponents.lpszPassword = NULL;
3806 urlComponents.dwPasswordLength = 0;
3807 urlComponents.lpszUrlPath = path;
3808 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3809 urlComponents.lpszExtraInfo = NULL;
3810 urlComponents.dwExtraInfoLength = 0;
3811 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3812 return INTERNET_GetLastError();
3814 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3815 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3817 TRACE("redirect from secure page to non-secure page\n");
3818 /* FIXME: warn about from secure redirect to non-secure page */
3819 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3821 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3822 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3824 TRACE("redirect from non-secure page to secure page\n");
3825 /* FIXME: notify about redirect to secure page */
3826 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3829 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3831 if (lstrlenW(protocol)>4) /*https*/
3832 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3833 else /*http*/
3834 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3837 #if 0
3839 * This upsets redirects to binary files on sourceforge.net
3840 * and gives an html page instead of the target file
3841 * Examination of the HTTP request sent by native wininet.dll
3842 * reveals that it doesn't send a referrer in that case.
3843 * Maybe there's a flag that enables this, or maybe a referrer
3844 * shouldn't be added in case of a redirect.
3847 /* consider the current host as the referrer */
3848 if (session->lpszServerName && *session->lpszServerName)
3849 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3850 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3851 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3852 #endif
3854 heap_free(session->hostName);
3855 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3856 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3858 int len;
3859 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3860 len = lstrlenW(hostName);
3861 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3862 session->hostName = heap_alloc(len*sizeof(WCHAR));
3863 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3865 else
3866 session->hostName = heap_strdupW(hostName);
3868 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3870 heap_free(session->userName);
3871 session->userName = NULL;
3872 if (userName[0])
3873 session->userName = heap_strdupW(userName);
3875 reset_data_stream(request);
3877 if(!using_proxy) {
3878 if(strcmpiW(session->serverName, hostName)) {
3879 heap_free(session->serverName);
3880 session->serverName = heap_strdupW(hostName);
3882 session->serverPort = urlComponents.nPort;
3885 heap_free(request->path);
3886 request->path=NULL;
3887 if (*path)
3889 DWORD needed = 0;
3890 HRESULT rc;
3892 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3893 if (rc != E_POINTER)
3894 needed = strlenW(path)+1;
3895 request->path = heap_alloc(needed*sizeof(WCHAR));
3896 rc = UrlEscapeW(path, request->path, &needed,
3897 URL_ESCAPE_SPACES_ONLY);
3898 if (rc != S_OK)
3900 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3901 strcpyW(request->path,path);
3905 /* Remove custom content-type/length headers on redirects. */
3906 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3907 if (0 <= index)
3908 HTTP_DeleteCustomHeader(request, index);
3909 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3910 if (0 <= index)
3911 HTTP_DeleteCustomHeader(request, index);
3913 return ERROR_SUCCESS;
3916 /***********************************************************************
3917 * HTTP_build_req (internal)
3919 * concatenate all the strings in the request together
3921 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3923 LPCWSTR *t;
3924 LPWSTR str;
3926 for( t = list; *t ; t++ )
3927 len += strlenW( *t );
3928 len++;
3930 str = heap_alloc(len*sizeof(WCHAR));
3931 *str = 0;
3933 for( t = list; *t ; t++ )
3934 strcatW( str, *t );
3936 return str;
3939 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3941 LPWSTR lpszPath;
3942 LPWSTR requestString;
3943 INT len;
3944 INT cnt;
3945 INT responseLen;
3946 char *ascii_req;
3947 DWORD res;
3948 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3949 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3950 http_session_t *session = request->session;
3952 TRACE("\n");
3954 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3955 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3956 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3957 heap_free( lpszPath );
3959 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3960 NULL, 0, NULL, NULL );
3961 len--; /* the nul terminator isn't needed */
3962 ascii_req = heap_alloc(len);
3963 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3964 heap_free( requestString );
3966 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3968 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
3969 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3970 heap_free( ascii_req );
3971 if (res != ERROR_SUCCESS)
3972 return res;
3974 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3975 if (!responseLen)
3976 return ERROR_HTTP_INVALID_HEADER;
3978 return ERROR_SUCCESS;
3981 static void HTTP_InsertCookies(http_request_t *request)
3983 DWORD cookie_size, size, cnt = 0;
3984 HTTPHEADERW *host;
3985 WCHAR *cookies;
3987 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3989 host = HTTP_GetHeader(request, hostW);
3990 if(!host)
3991 return;
3993 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3994 return;
3996 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
3997 if(!(cookies = heap_alloc(size)))
3998 return;
4000 cnt += sprintfW(cookies, cookieW);
4001 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4002 strcatW(cookies, szCrLf);
4004 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4006 heap_free(cookies);
4009 static WORD HTTP_ParseWkday(LPCWSTR day)
4011 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4012 { 'm','o','n',0 },
4013 { 't','u','e',0 },
4014 { 'w','e','d',0 },
4015 { 't','h','u',0 },
4016 { 'f','r','i',0 },
4017 { 's','a','t',0 }};
4018 int i;
4019 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4020 if (!strcmpiW(day, days[i]))
4021 return i;
4023 /* Invalid */
4024 return 7;
4027 static WORD HTTP_ParseMonth(LPCWSTR month)
4029 static const WCHAR jan[] = { 'j','a','n',0 };
4030 static const WCHAR feb[] = { 'f','e','b',0 };
4031 static const WCHAR mar[] = { 'm','a','r',0 };
4032 static const WCHAR apr[] = { 'a','p','r',0 };
4033 static const WCHAR may[] = { 'm','a','y',0 };
4034 static const WCHAR jun[] = { 'j','u','n',0 };
4035 static const WCHAR jul[] = { 'j','u','l',0 };
4036 static const WCHAR aug[] = { 'a','u','g',0 };
4037 static const WCHAR sep[] = { 's','e','p',0 };
4038 static const WCHAR oct[] = { 'o','c','t',0 };
4039 static const WCHAR nov[] = { 'n','o','v',0 };
4040 static const WCHAR dec[] = { 'd','e','c',0 };
4042 if (!strcmpiW(month, jan)) return 1;
4043 if (!strcmpiW(month, feb)) return 2;
4044 if (!strcmpiW(month, mar)) return 3;
4045 if (!strcmpiW(month, apr)) return 4;
4046 if (!strcmpiW(month, may)) return 5;
4047 if (!strcmpiW(month, jun)) return 6;
4048 if (!strcmpiW(month, jul)) return 7;
4049 if (!strcmpiW(month, aug)) return 8;
4050 if (!strcmpiW(month, sep)) return 9;
4051 if (!strcmpiW(month, oct)) return 10;
4052 if (!strcmpiW(month, nov)) return 11;
4053 if (!strcmpiW(month, dec)) return 12;
4054 /* Invalid */
4055 return 0;
4058 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4059 * optionally preceded by whitespace.
4060 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4061 * st, and sets *str to the first character after the time format.
4063 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4065 LPCWSTR ptr = *str;
4066 WCHAR *nextPtr;
4067 unsigned long num;
4069 while (isspaceW(*ptr))
4070 ptr++;
4072 num = strtoulW(ptr, &nextPtr, 10);
4073 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4075 ERR("unexpected time format %s\n", debugstr_w(ptr));
4076 return FALSE;
4078 if (num > 23)
4080 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4081 return FALSE;
4083 ptr = nextPtr + 1;
4084 st->wHour = (WORD)num;
4085 num = strtoulW(ptr, &nextPtr, 10);
4086 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4088 ERR("unexpected time format %s\n", debugstr_w(ptr));
4089 return FALSE;
4091 if (num > 59)
4093 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4094 return FALSE;
4096 ptr = nextPtr + 1;
4097 st->wMinute = (WORD)num;
4098 num = strtoulW(ptr, &nextPtr, 10);
4099 if (!nextPtr || nextPtr <= ptr)
4101 ERR("unexpected time format %s\n", debugstr_w(ptr));
4102 return FALSE;
4104 if (num > 59)
4106 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4107 return FALSE;
4109 ptr = nextPtr + 1;
4110 *str = ptr;
4111 st->wSecond = (WORD)num;
4112 return TRUE;
4115 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4117 static const WCHAR gmt[]= { 'G','M','T',0 };
4118 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4119 LPCWSTR ptr;
4120 SYSTEMTIME st = { 0 };
4121 unsigned long num;
4123 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4124 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4125 *dayPtr = *ptr;
4126 *dayPtr = 0;
4127 st.wDayOfWeek = HTTP_ParseWkday(day);
4128 if (st.wDayOfWeek >= 7)
4130 ERR("unexpected weekday %s\n", debugstr_w(day));
4131 return FALSE;
4134 while (isspaceW(*ptr))
4135 ptr++;
4137 for (monthPtr = month; !isspace(*ptr) &&
4138 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4139 monthPtr++, ptr++)
4140 *monthPtr = *ptr;
4141 *monthPtr = 0;
4142 st.wMonth = HTTP_ParseMonth(month);
4143 if (!st.wMonth || st.wMonth > 12)
4145 ERR("unexpected month %s\n", debugstr_w(month));
4146 return FALSE;
4149 while (isspaceW(*ptr))
4150 ptr++;
4152 num = strtoulW(ptr, &nextPtr, 10);
4153 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4155 ERR("unexpected day %s\n", debugstr_w(ptr));
4156 return FALSE;
4158 ptr = nextPtr;
4159 st.wDay = (WORD)num;
4161 while (isspaceW(*ptr))
4162 ptr++;
4164 if (!HTTP_ParseTime(&st, &ptr))
4165 return FALSE;
4167 while (isspaceW(*ptr))
4168 ptr++;
4170 num = strtoulW(ptr, &nextPtr, 10);
4171 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4173 ERR("unexpected year %s\n", debugstr_w(ptr));
4174 return FALSE;
4176 ptr = nextPtr;
4177 st.wYear = (WORD)num;
4179 while (isspaceW(*ptr))
4180 ptr++;
4182 /* asctime() doesn't report a timezone, but some web servers do, so accept
4183 * with or without GMT.
4185 if (*ptr && strcmpW(ptr, gmt))
4187 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4188 return FALSE;
4190 return SystemTimeToFileTime(&st, ft);
4193 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4195 static const WCHAR gmt[]= { 'G','M','T',0 };
4196 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4197 LPCWSTR ptr;
4198 unsigned long num;
4199 SYSTEMTIME st = { 0 };
4201 ptr = strchrW(value, ',');
4202 if (!ptr)
4203 return FALSE;
4204 if (ptr - value != 3)
4206 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4207 return FALSE;
4209 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4210 day[3] = 0;
4211 st.wDayOfWeek = HTTP_ParseWkday(day);
4212 if (st.wDayOfWeek > 6)
4214 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4215 return FALSE;
4217 ptr++;
4219 while (isspaceW(*ptr))
4220 ptr++;
4222 num = strtoulW(ptr, &nextPtr, 10);
4223 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4225 WARN("unexpected day %s\n", debugstr_w(value));
4226 return FALSE;
4228 ptr = nextPtr;
4229 st.wDay = (WORD)num;
4231 while (isspaceW(*ptr))
4232 ptr++;
4234 for (monthPtr = month; !isspace(*ptr) &&
4235 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4236 monthPtr++, ptr++)
4237 *monthPtr = *ptr;
4238 *monthPtr = 0;
4239 st.wMonth = HTTP_ParseMonth(month);
4240 if (!st.wMonth || st.wMonth > 12)
4242 WARN("unexpected month %s\n", debugstr_w(month));
4243 return FALSE;
4246 while (isspaceW(*ptr))
4247 ptr++;
4249 num = strtoulW(ptr, &nextPtr, 10);
4250 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4252 ERR("unexpected year %s\n", debugstr_w(value));
4253 return FALSE;
4255 ptr = nextPtr;
4256 st.wYear = (WORD)num;
4258 if (!HTTP_ParseTime(&st, &ptr))
4259 return FALSE;
4261 while (isspaceW(*ptr))
4262 ptr++;
4264 if (strcmpW(ptr, gmt))
4266 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4267 return FALSE;
4269 return SystemTimeToFileTime(&st, ft);
4272 static WORD HTTP_ParseWeekday(LPCWSTR day)
4274 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4275 { 'm','o','n','d','a','y',0 },
4276 { 't','u','e','s','d','a','y',0 },
4277 { 'w','e','d','n','e','s','d','a','y',0 },
4278 { 't','h','u','r','s','d','a','y',0 },
4279 { 'f','r','i','d','a','y',0 },
4280 { 's','a','t','u','r','d','a','y',0 }};
4281 int i;
4282 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4283 if (!strcmpiW(day, days[i]))
4284 return i;
4286 /* Invalid */
4287 return 7;
4290 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4292 static const WCHAR gmt[]= { 'G','M','T',0 };
4293 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4294 LPCWSTR ptr;
4295 unsigned long num;
4296 SYSTEMTIME st = { 0 };
4298 ptr = strchrW(value, ',');
4299 if (!ptr)
4300 return FALSE;
4301 if (ptr - value == 3)
4303 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4304 day[3] = 0;
4305 st.wDayOfWeek = HTTP_ParseWkday(day);
4306 if (st.wDayOfWeek > 6)
4308 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4309 return FALSE;
4312 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4314 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4315 day[ptr - value + 1] = 0;
4316 st.wDayOfWeek = HTTP_ParseWeekday(day);
4317 if (st.wDayOfWeek > 6)
4319 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4320 return FALSE;
4323 else
4325 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4326 return FALSE;
4328 ptr++;
4330 while (isspaceW(*ptr))
4331 ptr++;
4333 num = strtoulW(ptr, &nextPtr, 10);
4334 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4336 ERR("unexpected day %s\n", debugstr_w(value));
4337 return FALSE;
4339 ptr = nextPtr;
4340 st.wDay = (WORD)num;
4342 if (*ptr != '-')
4344 ERR("unexpected month format %s\n", debugstr_w(ptr));
4345 return FALSE;
4347 ptr++;
4349 for (monthPtr = month; *ptr != '-' &&
4350 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4351 monthPtr++, ptr++)
4352 *monthPtr = *ptr;
4353 *monthPtr = 0;
4354 st.wMonth = HTTP_ParseMonth(month);
4355 if (!st.wMonth || st.wMonth > 12)
4357 ERR("unexpected month %s\n", debugstr_w(month));
4358 return FALSE;
4361 if (*ptr != '-')
4363 ERR("unexpected year format %s\n", debugstr_w(ptr));
4364 return FALSE;
4366 ptr++;
4368 num = strtoulW(ptr, &nextPtr, 10);
4369 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4371 ERR("unexpected year %s\n", debugstr_w(value));
4372 return FALSE;
4374 ptr = nextPtr;
4375 st.wYear = (WORD)num;
4377 if (!HTTP_ParseTime(&st, &ptr))
4378 return FALSE;
4380 while (isspaceW(*ptr))
4381 ptr++;
4383 if (strcmpW(ptr, gmt))
4385 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4386 return FALSE;
4388 return SystemTimeToFileTime(&st, ft);
4391 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4393 static const WCHAR zero[] = { '0',0 };
4394 BOOL ret;
4396 if (!strcmpW(value, zero))
4398 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4399 ret = TRUE;
4401 else if (strchrW(value, ','))
4403 ret = HTTP_ParseRfc1123Date(value, ft);
4404 if (!ret)
4406 ret = HTTP_ParseRfc850Date(value, ft);
4407 if (!ret)
4408 ERR("unexpected date format %s\n", debugstr_w(value));
4411 else
4413 ret = HTTP_ParseDateAsAsctime(value, ft);
4414 if (!ret)
4415 ERR("unexpected date format %s\n", debugstr_w(value));
4417 return ret;
4420 static void HTTP_ProcessExpires(http_request_t *request)
4422 BOOL expirationFound = FALSE;
4423 int headerIndex;
4425 /* Look for a Cache-Control header with a max-age directive, as it takes
4426 * precedence over the Expires header.
4428 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4429 if (headerIndex != -1)
4431 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4432 LPWSTR ptr;
4434 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4436 LPWSTR comma = strchrW(ptr, ','), end, equal;
4438 if (comma)
4439 end = comma;
4440 else
4441 end = ptr + strlenW(ptr);
4442 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4444 if (*equal == '=')
4446 static const WCHAR max_age[] = {
4447 'm','a','x','-','a','g','e',0 };
4449 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4451 LPWSTR nextPtr;
4452 unsigned long age;
4454 age = strtoulW(equal + 1, &nextPtr, 10);
4455 if (nextPtr > equal + 1)
4457 LARGE_INTEGER ft;
4459 NtQuerySystemTime( &ft );
4460 /* Age is in seconds, FILETIME resolution is in
4461 * 100 nanosecond intervals.
4463 ft.QuadPart += age * (ULONGLONG)1000000;
4464 request->expires.dwLowDateTime = ft.u.LowPart;
4465 request->expires.dwHighDateTime = ft.u.HighPart;
4466 expirationFound = TRUE;
4470 if (comma)
4472 ptr = comma + 1;
4473 while (isspaceW(*ptr))
4474 ptr++;
4476 else
4477 ptr = NULL;
4480 if (!expirationFound)
4482 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4483 if (headerIndex != -1)
4485 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4486 FILETIME ft;
4488 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4490 expirationFound = TRUE;
4491 request->expires = ft;
4495 if (!expirationFound)
4497 LARGE_INTEGER t;
4499 /* With no known age, default to 10 minutes until expiration. */
4500 NtQuerySystemTime( &t );
4501 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4502 request->expires.dwLowDateTime = t.u.LowPart;
4503 request->expires.dwHighDateTime = t.u.HighPart;
4507 static void HTTP_ProcessLastModified(http_request_t *request)
4509 int headerIndex;
4511 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4512 if (headerIndex != -1)
4514 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4515 FILETIME ft;
4517 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4518 request->last_modified = ft;
4522 static void http_process_keep_alive(http_request_t *req)
4524 int index;
4526 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4527 if(index != -1)
4528 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4529 else
4530 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4533 static void HTTP_CacheRequest(http_request_t *request)
4535 WCHAR url[INTERNET_MAX_URL_LENGTH];
4536 WCHAR cacheFileName[MAX_PATH+1];
4537 BOOL b;
4539 b = HTTP_GetRequestURL(request, url);
4540 if(!b) {
4541 WARN("Could not get URL\n");
4542 return;
4545 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4546 if(b) {
4547 heap_free(request->cacheFile);
4548 CloseHandle(request->hCacheFile);
4550 request->cacheFile = heap_strdupW(cacheFileName);
4551 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4552 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4553 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4554 WARN("Could not create file: %u\n", GetLastError());
4555 request->hCacheFile = NULL;
4557 }else {
4558 WARN("Could not create cache entry: %08x\n", GetLastError());
4562 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4564 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4565 http_session_t *session = request->session;
4566 netconn_t *netconn = NULL;
4567 server_t *server;
4568 DWORD res;
4570 assert(!request->netconn);
4571 reset_data_stream(request);
4573 server = get_server(session->serverName, session->serverPort);
4574 if(!server)
4575 return ERROR_OUTOFMEMORY;
4577 res = HTTP_ResolveName(request, server);
4578 if(res != ERROR_SUCCESS) {
4579 server_release(server);
4580 return res;
4583 EnterCriticalSection(&connection_pool_cs);
4585 while(!list_empty(&server->conn_pool)) {
4586 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4587 list_remove(&netconn->pool_entry);
4589 if(NETCON_is_alive(netconn))
4590 break;
4592 TRACE("connection %p closed during idle\n", netconn);
4593 free_netconn(netconn);
4594 netconn = NULL;
4597 LeaveCriticalSection(&connection_pool_cs);
4599 if(netconn) {
4600 TRACE("<-- reusing %p netconn\n", netconn);
4601 request->netconn = netconn;
4602 *reusing = TRUE;
4603 return ERROR_SUCCESS;
4606 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4607 INTERNET_STATUS_CONNECTING_TO_SERVER,
4608 server->addr_str,
4609 strlen(server->addr_str)+1);
4611 res = create_netconn(is_https, server, request->security_flags, request->connect_timeout, &netconn);
4612 server_release(server);
4613 if(res != ERROR_SUCCESS) {
4614 ERR("create_netconn failed: %u\n", res);
4615 return res;
4618 request->netconn = netconn;
4620 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4621 INTERNET_STATUS_CONNECTED_TO_SERVER,
4622 server->addr_str, strlen(server->addr_str)+1);
4624 if(is_https) {
4625 /* Note: we differ from Microsoft's WinINet here. they seem to have
4626 * a bug that causes no status callbacks to be sent when starting
4627 * a tunnel to a proxy server using the CONNECT verb. i believe our
4628 * behaviour to be more correct and to not cause any incompatibilities
4629 * because using a secure connection through a proxy server is a rare
4630 * case that would be hard for anyone to depend on */
4631 if(session->appInfo->proxy)
4632 res = HTTP_SecureProxyConnect(request);
4633 if(res == ERROR_SUCCESS)
4634 res = NETCON_secure_connect(request->netconn, session->hostName);
4635 if(res != ERROR_SUCCESS)
4637 WARN("Couldn't connect securely to host\n");
4639 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4640 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4641 || res == ERROR_INTERNET_INVALID_CA
4642 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4643 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4644 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4645 || res == ERROR_INTERNET_SEC_INVALID_CERT
4646 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4647 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4651 if(res != ERROR_SUCCESS) {
4652 http_release_netconn(request, FALSE);
4653 return res;
4656 *reusing = FALSE;
4657 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4658 return ERROR_SUCCESS;
4661 /***********************************************************************
4662 * HTTP_HttpSendRequestW (internal)
4664 * Sends the specified request to the HTTP server
4666 * RETURNS
4667 * ERROR_SUCCESS on success
4668 * win32 error code on failure
4671 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4672 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4673 DWORD dwContentLength, BOOL bEndRequest)
4675 INT cnt;
4676 BOOL redirected = FALSE;
4677 LPWSTR requestString = NULL;
4678 INT responseLen;
4679 BOOL loop_next;
4680 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4681 static const WCHAR szContentLength[] =
4682 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4683 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4684 DWORD res;
4686 TRACE("--> %p\n", request);
4688 assert(request->hdr.htype == WH_HHTTPREQ);
4690 /* if the verb is NULL default to GET */
4691 if (!request->verb)
4692 request->verb = heap_strdupW(szGET);
4694 if (dwContentLength || strcmpW(request->verb, szGET))
4696 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4697 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4698 request->bytesToWrite = dwContentLength;
4700 if (request->session->appInfo->agent)
4702 WCHAR *agent_header;
4703 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4704 int len;
4706 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4707 agent_header = heap_alloc(len * sizeof(WCHAR));
4708 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4710 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4711 heap_free(agent_header);
4713 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4715 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4716 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4718 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4720 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4721 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4722 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4727 DWORD len;
4728 BOOL reusing_connection;
4729 char *ascii_req;
4731 loop_next = FALSE;
4733 /* like native, just in case the caller forgot to call InternetReadFile
4734 * for all the data */
4735 drain_content(request);
4736 if(redirected) {
4737 request->contentLength = ~0u;
4738 request->bytesToWrite = 0;
4741 if (TRACE_ON(wininet))
4743 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4744 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4747 HTTP_FixURL(request);
4748 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4750 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4752 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4753 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4755 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4756 HTTP_InsertCookies(request);
4758 /* add the headers the caller supplied */
4759 if( lpszHeaders && dwHeaderLength )
4761 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4762 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4765 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4767 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4768 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4769 heap_free(url);
4771 else
4772 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4775 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4777 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4778 break;
4780 /* send the request as ASCII, tack on the optional data */
4781 if (!lpOptional || redirected)
4782 dwOptionalLength = 0;
4783 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4784 NULL, 0, NULL, NULL );
4785 ascii_req = heap_alloc(len + dwOptionalLength);
4786 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4787 ascii_req, len, NULL, NULL );
4788 if( lpOptional )
4789 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4790 len = (len + dwOptionalLength - 1);
4791 ascii_req[len] = 0;
4792 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4794 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4795 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4797 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4798 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4799 heap_free( ascii_req );
4800 if(res != ERROR_SUCCESS) {
4801 TRACE("send failed: %u\n", res);
4802 if(!reusing_connection)
4803 break;
4804 http_release_netconn(request, FALSE);
4805 loop_next = TRUE;
4806 continue;
4809 request->bytesWritten = dwOptionalLength;
4811 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4812 INTERNET_STATUS_REQUEST_SENT,
4813 &len, sizeof(DWORD));
4815 if (bEndRequest)
4817 DWORD dwBufferSize;
4818 DWORD dwStatusCode;
4820 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4821 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4823 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4824 /* FIXME: We should know that connection is closed before sending
4825 * headers. Otherwise wrong callbacks are executed */
4826 if(!responseLen && reusing_connection) {
4827 TRACE("Connection closed by server, reconnecting\n");
4828 http_release_netconn(request, FALSE);
4829 loop_next = TRUE;
4830 continue;
4833 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4834 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4835 sizeof(DWORD));
4837 http_process_keep_alive(request);
4838 HTTP_ProcessCookies(request);
4839 HTTP_ProcessExpires(request);
4840 HTTP_ProcessLastModified(request);
4842 dwBufferSize = sizeof(dwStatusCode);
4843 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4844 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4845 dwStatusCode = 0;
4847 res = set_content_length(request, dwStatusCode);
4848 if(res != ERROR_SUCCESS)
4849 goto lend;
4850 if(!request->contentLength)
4851 http_release_netconn(request, TRUE);
4853 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4855 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4856 dwBufferSize=sizeof(szNewLocation);
4857 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4858 dwStatusCode == HTTP_STATUS_MOVED ||
4859 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4860 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4861 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4863 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4864 dwStatusCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4866 heap_free(request->verb);
4867 request->verb = heap_strdupW(szGET);
4869 drain_content(request);
4870 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4872 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4873 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4874 res = HTTP_HandleRedirect(request, new_url);
4875 if (res == ERROR_SUCCESS)
4877 heap_free(requestString);
4878 loop_next = TRUE;
4880 heap_free( new_url );
4882 redirected = TRUE;
4885 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4887 WCHAR szAuthValue[2048];
4888 dwBufferSize=2048;
4889 if (dwStatusCode == HTTP_STATUS_DENIED)
4891 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4892 DWORD dwIndex = 0;
4893 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4895 if (HTTP_DoAuthorization(request, szAuthValue,
4896 &request->authInfo,
4897 request->session->userName,
4898 request->session->password,
4899 Host->lpszValue))
4901 heap_free(requestString);
4902 loop_next = TRUE;
4903 break;
4907 if(!loop_next) {
4908 TRACE("Cleaning wrong authorization data\n");
4909 destroy_authinfo(request->authInfo);
4910 request->authInfo = NULL;
4913 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4915 DWORD dwIndex = 0;
4916 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4918 if (HTTP_DoAuthorization(request, szAuthValue,
4919 &request->proxyAuthInfo,
4920 request->session->appInfo->proxyUsername,
4921 request->session->appInfo->proxyPassword,
4922 NULL))
4924 loop_next = TRUE;
4925 break;
4929 if(!loop_next) {
4930 TRACE("Cleaning wrong proxy authorization data\n");
4931 destroy_authinfo(request->proxyAuthInfo);
4932 request->proxyAuthInfo = NULL;
4937 else
4938 res = ERROR_SUCCESS;
4940 while (loop_next);
4942 if(res == ERROR_SUCCESS)
4943 HTTP_CacheRequest(request);
4945 lend:
4946 heap_free(requestString);
4948 /* TODO: send notification for P3P header */
4950 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4952 if (res == ERROR_SUCCESS) {
4953 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4954 HTTP_ReceiveRequestData(request, TRUE);
4955 else
4956 send_request_complete(request,
4957 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4958 }else {
4959 send_request_complete(request, 0, res);
4963 TRACE("<--\n");
4964 return res;
4967 /***********************************************************************
4969 * Helper functions for the HttpSendRequest(Ex) functions
4972 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4974 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4975 http_request_t *request = (http_request_t*) workRequest->hdr;
4977 TRACE("%p\n", request);
4979 HTTP_HttpSendRequestW(request, req->lpszHeader,
4980 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4981 req->dwContentLength, req->bEndRequest);
4983 heap_free(req->lpszHeader);
4987 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4989 INT responseLen;
4990 DWORD dwCode, dwCodeLength;
4991 DWORD dwBufferSize;
4992 DWORD res = ERROR_SUCCESS;
4994 if(!request->netconn) {
4995 WARN("Not connected\n");
4996 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
4997 return ERROR_INTERNET_OPERATION_CANCELLED;
5000 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5001 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5003 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5004 if (!responseLen)
5005 res = ERROR_HTTP_HEADER_NOT_FOUND;
5007 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5008 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5010 /* process cookies here. Is this right? */
5011 http_process_keep_alive(request);
5012 HTTP_ProcessCookies(request);
5013 HTTP_ProcessExpires(request);
5014 HTTP_ProcessLastModified(request);
5016 dwCodeLength = sizeof(dwCode);
5017 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
5018 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
5019 dwCode = 0;
5021 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
5022 if(!request->contentLength)
5023 http_release_netconn(request, TRUE);
5026 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5028 if (dwCode == HTTP_STATUS_REDIRECT ||
5029 dwCode == HTTP_STATUS_MOVED ||
5030 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
5031 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
5033 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5034 dwBufferSize=sizeof(szNewLocation);
5035 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
5037 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5038 dwCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
5040 heap_free(request->verb);
5041 request->verb = heap_strdupW(szGET);
5043 drain_content(request);
5044 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5046 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5047 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5048 res = HTTP_HandleRedirect(request, new_url);
5049 if (res == ERROR_SUCCESS)
5050 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5051 heap_free( new_url );
5057 if (res == ERROR_SUCCESS && request->contentLength)
5058 HTTP_ReceiveRequestData(request, TRUE);
5059 else
5060 send_request_complete(request, res == ERROR_SUCCESS, res);
5062 return res;
5065 /***********************************************************************
5066 * HttpEndRequestA (WININET.@)
5068 * Ends an HTTP request that was started by HttpSendRequestEx
5070 * RETURNS
5071 * TRUE if successful
5072 * FALSE on failure
5075 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5076 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5078 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5080 if (lpBuffersOut)
5082 SetLastError(ERROR_INVALID_PARAMETER);
5083 return FALSE;
5086 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5089 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5091 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5092 http_request_t *request = (http_request_t*)work->hdr;
5094 TRACE("%p\n", request);
5096 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5099 /***********************************************************************
5100 * HttpEndRequestW (WININET.@)
5102 * Ends an HTTP request that was started by HttpSendRequestEx
5104 * RETURNS
5105 * TRUE if successful
5106 * FALSE on failure
5109 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5110 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5112 http_request_t *request;
5113 DWORD res;
5115 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5117 if (lpBuffersOut)
5119 SetLastError(ERROR_INVALID_PARAMETER);
5120 return FALSE;
5123 request = (http_request_t*) get_handle_object( hRequest );
5125 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5127 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5128 if (request)
5129 WININET_Release( &request->hdr );
5130 return FALSE;
5132 request->hdr.dwFlags |= dwFlags;
5134 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5136 WORKREQUEST work;
5137 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5139 work.asyncproc = AsyncHttpEndRequestProc;
5140 work.hdr = WININET_AddRef( &request->hdr );
5142 work_endrequest = &work.u.HttpEndRequestW;
5143 work_endrequest->dwFlags = dwFlags;
5144 work_endrequest->dwContext = dwContext;
5146 INTERNET_AsyncCall(&work);
5147 res = ERROR_IO_PENDING;
5149 else
5150 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5152 WININET_Release( &request->hdr );
5153 TRACE("%u <--\n", res);
5154 if(res != ERROR_SUCCESS)
5155 SetLastError(res);
5156 return res == ERROR_SUCCESS;
5159 /***********************************************************************
5160 * HttpSendRequestExA (WININET.@)
5162 * Sends the specified request to the HTTP server and allows chunked
5163 * transfers.
5165 * RETURNS
5166 * Success: TRUE
5167 * Failure: FALSE, call GetLastError() for more information.
5169 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5170 LPINTERNET_BUFFERSA lpBuffersIn,
5171 LPINTERNET_BUFFERSA lpBuffersOut,
5172 DWORD dwFlags, DWORD_PTR dwContext)
5174 INTERNET_BUFFERSW BuffersInW;
5175 BOOL rc = FALSE;
5176 DWORD headerlen;
5177 LPWSTR header = NULL;
5179 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5180 lpBuffersOut, dwFlags, dwContext);
5182 if (lpBuffersIn)
5184 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5185 if (lpBuffersIn->lpcszHeader)
5187 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5188 lpBuffersIn->dwHeadersLength,0,0);
5189 header = heap_alloc(headerlen*sizeof(WCHAR));
5190 if (!(BuffersInW.lpcszHeader = header))
5192 SetLastError(ERROR_OUTOFMEMORY);
5193 return FALSE;
5195 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5196 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5197 header, headerlen);
5199 else
5200 BuffersInW.lpcszHeader = NULL;
5201 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5202 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5203 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5204 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5205 BuffersInW.Next = NULL;
5208 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5210 heap_free(header);
5211 return rc;
5214 /***********************************************************************
5215 * HttpSendRequestExW (WININET.@)
5217 * Sends the specified request to the HTTP server and allows chunked
5218 * transfers
5220 * RETURNS
5221 * Success: TRUE
5222 * Failure: FALSE, call GetLastError() for more information.
5224 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5225 LPINTERNET_BUFFERSW lpBuffersIn,
5226 LPINTERNET_BUFFERSW lpBuffersOut,
5227 DWORD dwFlags, DWORD_PTR dwContext)
5229 http_request_t *request;
5230 http_session_t *session;
5231 appinfo_t *hIC;
5232 DWORD res;
5234 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5235 lpBuffersOut, dwFlags, dwContext);
5237 request = (http_request_t*) get_handle_object( hRequest );
5239 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5241 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5242 goto lend;
5245 session = request->session;
5246 assert(session->hdr.htype == WH_HHTTPSESSION);
5247 hIC = session->appInfo;
5248 assert(hIC->hdr.htype == WH_HINIT);
5250 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5252 WORKREQUEST workRequest;
5253 struct WORKREQ_HTTPSENDREQUESTW *req;
5255 workRequest.asyncproc = AsyncHttpSendRequestProc;
5256 workRequest.hdr = WININET_AddRef( &request->hdr );
5257 req = &workRequest.u.HttpSendRequestW;
5258 if (lpBuffersIn)
5260 DWORD size = 0;
5262 if (lpBuffersIn->lpcszHeader)
5264 if (lpBuffersIn->dwHeadersLength == ~0u)
5265 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5266 else
5267 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5269 req->lpszHeader = heap_alloc(size);
5270 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5272 else req->lpszHeader = NULL;
5274 req->dwHeaderLength = size / sizeof(WCHAR);
5275 req->lpOptional = lpBuffersIn->lpvBuffer;
5276 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5277 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5279 else
5281 req->lpszHeader = NULL;
5282 req->dwHeaderLength = 0;
5283 req->lpOptional = NULL;
5284 req->dwOptionalLength = 0;
5285 req->dwContentLength = 0;
5288 req->bEndRequest = FALSE;
5290 INTERNET_AsyncCall(&workRequest);
5292 * This is from windows.
5294 res = ERROR_IO_PENDING;
5296 else
5298 if (lpBuffersIn)
5299 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5300 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5301 lpBuffersIn->dwBufferTotal, FALSE);
5302 else
5303 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5306 lend:
5307 if ( request )
5308 WININET_Release( &request->hdr );
5310 TRACE("<---\n");
5311 SetLastError(res);
5312 return res == ERROR_SUCCESS;
5315 /***********************************************************************
5316 * HttpSendRequestW (WININET.@)
5318 * Sends the specified request to the HTTP server
5320 * RETURNS
5321 * TRUE on success
5322 * FALSE on failure
5325 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5326 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5328 http_request_t *request;
5329 http_session_t *session = NULL;
5330 appinfo_t *hIC = NULL;
5331 DWORD res = ERROR_SUCCESS;
5333 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5334 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5336 request = (http_request_t*) get_handle_object( hHttpRequest );
5337 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5339 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5340 goto lend;
5343 session = request->session;
5344 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5346 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5347 goto lend;
5350 hIC = session->appInfo;
5351 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5353 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5354 goto lend;
5357 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5359 WORKREQUEST workRequest;
5360 struct WORKREQ_HTTPSENDREQUESTW *req;
5362 workRequest.asyncproc = AsyncHttpSendRequestProc;
5363 workRequest.hdr = WININET_AddRef( &request->hdr );
5364 req = &workRequest.u.HttpSendRequestW;
5365 if (lpszHeaders)
5367 DWORD size;
5369 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5370 else size = dwHeaderLength * sizeof(WCHAR);
5372 req->lpszHeader = heap_alloc(size);
5373 memcpy(req->lpszHeader, lpszHeaders, size);
5375 else
5376 req->lpszHeader = 0;
5377 req->dwHeaderLength = dwHeaderLength;
5378 req->lpOptional = lpOptional;
5379 req->dwOptionalLength = dwOptionalLength;
5380 req->dwContentLength = dwOptionalLength;
5381 req->bEndRequest = TRUE;
5383 INTERNET_AsyncCall(&workRequest);
5385 * This is from windows.
5387 res = ERROR_IO_PENDING;
5389 else
5391 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5392 dwHeaderLength, lpOptional, dwOptionalLength,
5393 dwOptionalLength, TRUE);
5395 lend:
5396 if( request )
5397 WININET_Release( &request->hdr );
5399 SetLastError(res);
5400 return res == ERROR_SUCCESS;
5403 /***********************************************************************
5404 * HttpSendRequestA (WININET.@)
5406 * Sends the specified request to the HTTP server
5408 * RETURNS
5409 * TRUE on success
5410 * FALSE on failure
5413 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5414 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5416 BOOL result;
5417 LPWSTR szHeaders=NULL;
5418 DWORD nLen=dwHeaderLength;
5419 if(lpszHeaders!=NULL)
5421 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5422 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5423 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5425 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5426 heap_free(szHeaders);
5427 return result;
5430 /***********************************************************************
5431 * HTTPSESSION_Destroy (internal)
5433 * Deallocate session handle
5436 static void HTTPSESSION_Destroy(object_header_t *hdr)
5438 http_session_t *session = (http_session_t*) hdr;
5440 TRACE("%p\n", session);
5442 WININET_Release(&session->appInfo->hdr);
5444 heap_free(session->hostName);
5445 heap_free(session->serverName);
5446 heap_free(session->password);
5447 heap_free(session->userName);
5450 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5452 http_session_t *ses = (http_session_t *)hdr;
5454 switch(option) {
5455 case INTERNET_OPTION_HANDLE_TYPE:
5456 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5458 if (*size < sizeof(ULONG))
5459 return ERROR_INSUFFICIENT_BUFFER;
5461 *size = sizeof(DWORD);
5462 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5463 return ERROR_SUCCESS;
5464 case INTERNET_OPTION_CONNECT_TIMEOUT:
5465 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5467 if (*size < sizeof(DWORD))
5468 return ERROR_INSUFFICIENT_BUFFER;
5470 *size = sizeof(DWORD);
5471 *(DWORD *)buffer = ses->connect_timeout;
5472 return ERROR_SUCCESS;
5474 case INTERNET_OPTION_SEND_TIMEOUT:
5475 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5477 if (*size < sizeof(DWORD))
5478 return ERROR_INSUFFICIENT_BUFFER;
5480 *size = sizeof(DWORD);
5481 *(DWORD *)buffer = ses->send_timeout;
5482 return ERROR_SUCCESS;
5484 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5485 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5487 if (*size < sizeof(DWORD))
5488 return ERROR_INSUFFICIENT_BUFFER;
5490 *size = sizeof(DWORD);
5491 *(DWORD *)buffer = ses->receive_timeout;
5492 return ERROR_SUCCESS;
5495 return INET_QueryOption(hdr, option, buffer, size, unicode);
5498 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5500 http_session_t *ses = (http_session_t*)hdr;
5502 switch(option) {
5503 case INTERNET_OPTION_USERNAME:
5505 heap_free(ses->userName);
5506 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5507 return ERROR_SUCCESS;
5509 case INTERNET_OPTION_PASSWORD:
5511 heap_free(ses->password);
5512 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5513 return ERROR_SUCCESS;
5515 case INTERNET_OPTION_CONNECT_TIMEOUT:
5517 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5518 ses->connect_timeout = *(DWORD *)buffer;
5519 return ERROR_SUCCESS;
5521 case INTERNET_OPTION_SEND_TIMEOUT:
5523 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5524 ses->send_timeout = *(DWORD *)buffer;
5525 return ERROR_SUCCESS;
5527 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5529 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5530 ses->receive_timeout = *(DWORD *)buffer;
5531 return ERROR_SUCCESS;
5533 default: break;
5536 return ERROR_INTERNET_INVALID_OPTION;
5539 static const object_vtbl_t HTTPSESSIONVtbl = {
5540 HTTPSESSION_Destroy,
5541 NULL,
5542 HTTPSESSION_QueryOption,
5543 HTTPSESSION_SetOption,
5544 NULL,
5545 NULL,
5546 NULL,
5547 NULL,
5548 NULL
5552 /***********************************************************************
5553 * HTTP_Connect (internal)
5555 * Create http session handle
5557 * RETURNS
5558 * HINTERNET a session handle on success
5559 * NULL on failure
5562 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5563 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5564 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5565 DWORD dwInternalFlags, HINTERNET *ret)
5567 http_session_t *session = NULL;
5569 TRACE("-->\n");
5571 if (!lpszServerName || !lpszServerName[0])
5572 return ERROR_INVALID_PARAMETER;
5574 assert( hIC->hdr.htype == WH_HINIT );
5576 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5577 if (!session)
5578 return ERROR_OUTOFMEMORY;
5581 * According to my tests. The name is not resolved until a request is sent
5584 session->hdr.htype = WH_HHTTPSESSION;
5585 session->hdr.dwFlags = dwFlags;
5586 session->hdr.dwContext = dwContext;
5587 session->hdr.dwInternalFlags |= dwInternalFlags;
5589 WININET_AddRef( &hIC->hdr );
5590 session->appInfo = hIC;
5591 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5593 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5594 if(hIC->proxyBypass)
5595 FIXME("Proxy bypass is ignored.\n");
5597 session->serverName = heap_strdupW(lpszServerName);
5598 session->hostName = heap_strdupW(lpszServerName);
5599 if (lpszUserName && lpszUserName[0])
5600 session->userName = heap_strdupW(lpszUserName);
5601 if (lpszPassword && lpszPassword[0])
5602 session->password = heap_strdupW(lpszPassword);
5603 session->serverPort = serverPort;
5604 session->hostPort = serverPort;
5605 session->connect_timeout = INFINITE;
5606 session->send_timeout = INFINITE;
5607 session->receive_timeout = INFINITE;
5609 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5610 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5612 INTERNET_SendCallback(&hIC->hdr, dwContext,
5613 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5614 sizeof(HINTERNET));
5618 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5619 * windows
5622 TRACE("%p --> %p\n", hIC, session);
5624 *ret = session->hdr.hInternet;
5625 return ERROR_SUCCESS;
5628 /***********************************************************************
5629 * HTTP_clear_response_headers (internal)
5631 * clear out any old response headers
5633 static void HTTP_clear_response_headers( http_request_t *request )
5635 DWORD i;
5637 for( i=0; i<request->nCustHeaders; i++)
5639 if( !request->custHeaders[i].lpszField )
5640 continue;
5641 if( !request->custHeaders[i].lpszValue )
5642 continue;
5643 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5644 continue;
5645 HTTP_DeleteCustomHeader( request, i );
5646 i--;
5650 /***********************************************************************
5651 * HTTP_GetResponseHeaders (internal)
5653 * Read server response
5655 * RETURNS
5657 * TRUE on success
5658 * FALSE on error
5660 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5662 INT cbreaks = 0;
5663 WCHAR buffer[MAX_REPLY_LEN];
5664 DWORD buflen = MAX_REPLY_LEN;
5665 BOOL bSuccess = FALSE;
5666 INT rc = 0;
5667 char bufferA[MAX_REPLY_LEN];
5668 LPWSTR status_code = NULL, status_text = NULL;
5669 DWORD cchMaxRawHeaders = 1024;
5670 LPWSTR lpszRawHeaders = NULL;
5671 LPWSTR temp;
5672 DWORD cchRawHeaders = 0;
5673 BOOL codeHundred = FALSE;
5675 TRACE("-->\n");
5677 if(!request->netconn)
5678 goto lend;
5680 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5681 do {
5682 static const WCHAR szHundred[] = {'1','0','0',0};
5684 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5686 buflen = MAX_REPLY_LEN;
5687 if (!read_line(request, bufferA, &buflen))
5688 goto lend;
5690 /* clear old response headers (eg. from a redirect response) */
5691 if (clear) {
5692 HTTP_clear_response_headers( request );
5693 clear = FALSE;
5696 rc += buflen;
5697 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5698 /* check is this a status code line? */
5699 if (!strncmpW(buffer, g_szHttp1_0, 4))
5701 /* split the version from the status code */
5702 status_code = strchrW( buffer, ' ' );
5703 if( !status_code )
5704 goto lend;
5705 *status_code++=0;
5707 /* split the status code from the status text */
5708 status_text = strchrW( status_code, ' ' );
5709 if( !status_text )
5710 goto lend;
5711 *status_text++=0;
5713 TRACE("version [%s] status code [%s] status text [%s]\n",
5714 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5716 codeHundred = (!strcmpW(status_code, szHundred));
5718 else if (!codeHundred)
5720 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5722 heap_free(request->version);
5723 heap_free(request->statusText);
5725 request->version = heap_strdupW(g_szHttp1_0);
5726 request->statusText = heap_strdupW(szOK);
5728 heap_free(request->rawHeaders);
5729 request->rawHeaders = heap_strdupW(szDefaultHeader);
5731 bSuccess = TRUE;
5732 goto lend;
5734 } while (codeHundred);
5736 /* Add status code */
5737 HTTP_ProcessHeader(request, szStatus, status_code,
5738 HTTP_ADDHDR_FLAG_REPLACE);
5740 heap_free(request->version);
5741 heap_free(request->statusText);
5743 request->version = heap_strdupW(buffer);
5744 request->statusText = heap_strdupW(status_text);
5746 /* Restore the spaces */
5747 *(status_code-1) = ' ';
5748 *(status_text-1) = ' ';
5750 /* regenerate raw headers */
5751 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5752 if (!lpszRawHeaders) goto lend;
5754 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5755 cchMaxRawHeaders *= 2;
5756 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5757 if (temp == NULL) goto lend;
5758 lpszRawHeaders = temp;
5759 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5760 cchRawHeaders += (buflen-1);
5761 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5762 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5763 lpszRawHeaders[cchRawHeaders] = '\0';
5765 /* Parse each response line */
5768 buflen = MAX_REPLY_LEN;
5769 if (read_line(request, bufferA, &buflen))
5771 LPWSTR * pFieldAndValue;
5773 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5775 if (!bufferA[0]) break;
5776 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5778 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5779 if (pFieldAndValue)
5781 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5782 cchMaxRawHeaders *= 2;
5783 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5784 if (temp == NULL) goto lend;
5785 lpszRawHeaders = temp;
5786 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5787 cchRawHeaders += (buflen-1);
5788 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5789 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5790 lpszRawHeaders[cchRawHeaders] = '\0';
5792 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5793 HTTP_ADDREQ_FLAG_ADD );
5795 HTTP_FreeTokens(pFieldAndValue);
5798 else
5800 cbreaks++;
5801 if (cbreaks >= 2)
5802 break;
5804 }while(1);
5806 /* make sure the response header is terminated with an empty line. Some apps really
5807 truly care about that empty line being there for some reason. Just add it to the
5808 header. */
5809 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5811 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5812 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5813 if (temp == NULL) goto lend;
5814 lpszRawHeaders = temp;
5817 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5819 heap_free(request->rawHeaders);
5820 request->rawHeaders = lpszRawHeaders;
5821 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5822 bSuccess = TRUE;
5824 lend:
5826 TRACE("<--\n");
5827 if (bSuccess)
5828 return rc;
5829 else
5831 heap_free(lpszRawHeaders);
5832 return 0;
5836 /***********************************************************************
5837 * HTTP_InterpretHttpHeader (internal)
5839 * Parse server response
5841 * RETURNS
5843 * Pointer to array of field, value, NULL on success.
5844 * NULL on error.
5846 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5848 LPWSTR * pTokenPair;
5849 LPWSTR pszColon;
5850 INT len;
5852 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5854 pszColon = strchrW(buffer, ':');
5855 /* must have two tokens */
5856 if (!pszColon)
5858 HTTP_FreeTokens(pTokenPair);
5859 if (buffer[0])
5860 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5861 return NULL;
5864 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5865 if (!pTokenPair[0])
5867 HTTP_FreeTokens(pTokenPair);
5868 return NULL;
5870 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5871 pTokenPair[0][pszColon - buffer] = '\0';
5873 /* skip colon */
5874 pszColon++;
5875 len = strlenW(pszColon);
5876 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5877 if (!pTokenPair[1])
5879 HTTP_FreeTokens(pTokenPair);
5880 return NULL;
5882 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5884 strip_spaces(pTokenPair[0]);
5885 strip_spaces(pTokenPair[1]);
5887 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5888 return pTokenPair;
5891 /***********************************************************************
5892 * HTTP_ProcessHeader (internal)
5894 * Stuff header into header tables according to <dwModifier>
5898 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5900 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5902 LPHTTPHEADERW lphttpHdr = NULL;
5903 INT index = -1;
5904 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5905 DWORD res = ERROR_HTTP_INVALID_HEADER;
5907 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5909 /* REPLACE wins out over ADD */
5910 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5911 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5913 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5914 index = -1;
5915 else
5916 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5918 if (index >= 0)
5920 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5921 return ERROR_HTTP_INVALID_HEADER;
5922 lphttpHdr = &request->custHeaders[index];
5924 else if (value)
5926 HTTPHEADERW hdr;
5928 hdr.lpszField = (LPWSTR)field;
5929 hdr.lpszValue = (LPWSTR)value;
5930 hdr.wFlags = hdr.wCount = 0;
5932 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5933 hdr.wFlags |= HDR_ISREQUEST;
5935 return HTTP_InsertCustomHeader(request, &hdr);
5937 /* no value to delete */
5938 else return ERROR_SUCCESS;
5940 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5941 lphttpHdr->wFlags |= HDR_ISREQUEST;
5942 else
5943 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5945 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5947 HTTP_DeleteCustomHeader( request, index );
5949 if (value)
5951 HTTPHEADERW hdr;
5953 hdr.lpszField = (LPWSTR)field;
5954 hdr.lpszValue = (LPWSTR)value;
5955 hdr.wFlags = hdr.wCount = 0;
5957 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5958 hdr.wFlags |= HDR_ISREQUEST;
5960 return HTTP_InsertCustomHeader(request, &hdr);
5963 return ERROR_SUCCESS;
5965 else if (dwModifier & COALESCEFLAGS)
5967 LPWSTR lpsztmp;
5968 WCHAR ch = 0;
5969 INT len = 0;
5970 INT origlen = strlenW(lphttpHdr->lpszValue);
5971 INT valuelen = strlenW(value);
5973 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5975 ch = ',';
5976 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5978 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5980 ch = ';';
5981 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5984 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5986 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5987 if (lpsztmp)
5989 lphttpHdr->lpszValue = lpsztmp;
5990 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5991 if (ch > 0)
5993 lphttpHdr->lpszValue[origlen] = ch;
5994 origlen++;
5995 lphttpHdr->lpszValue[origlen] = ' ';
5996 origlen++;
5999 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6000 lphttpHdr->lpszValue[len] = '\0';
6001 res = ERROR_SUCCESS;
6003 else
6005 WARN("heap_realloc (%d bytes) failed\n",len+1);
6006 res = ERROR_OUTOFMEMORY;
6009 TRACE("<-- %d\n", res);
6010 return res;
6013 /***********************************************************************
6014 * HTTP_GetCustomHeaderIndex (internal)
6016 * Return index of custom header from header array
6019 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6020 int requested_index, BOOL request_only)
6022 DWORD index;
6024 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6026 for (index = 0; index < request->nCustHeaders; index++)
6028 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6029 continue;
6031 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6032 continue;
6034 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6035 continue;
6037 if (requested_index == 0)
6038 break;
6039 requested_index --;
6042 if (index >= request->nCustHeaders)
6043 index = -1;
6045 TRACE("Return: %d\n", index);
6046 return index;
6050 /***********************************************************************
6051 * HTTP_InsertCustomHeader (internal)
6053 * Insert header into array
6056 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6058 INT count;
6059 LPHTTPHEADERW lph = NULL;
6061 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6062 count = request->nCustHeaders + 1;
6063 if (count > 1)
6064 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6065 else
6066 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6068 if (!lph)
6069 return ERROR_OUTOFMEMORY;
6071 request->custHeaders = lph;
6072 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6073 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6074 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6075 request->custHeaders[count-1].wCount= lpHdr->wCount;
6076 request->nCustHeaders++;
6078 return ERROR_SUCCESS;
6082 /***********************************************************************
6083 * HTTP_DeleteCustomHeader (internal)
6085 * Delete header from array
6086 * If this function is called, the indexs may change.
6088 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6090 if( request->nCustHeaders <= 0 )
6091 return FALSE;
6092 if( index >= request->nCustHeaders )
6093 return FALSE;
6094 request->nCustHeaders--;
6096 heap_free(request->custHeaders[index].lpszField);
6097 heap_free(request->custHeaders[index].lpszValue);
6099 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6100 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6101 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6103 return TRUE;
6107 /***********************************************************************
6108 * HTTP_VerifyValidHeader (internal)
6110 * Verify the given header is not invalid for the given http request
6113 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6115 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6116 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6117 return ERROR_HTTP_INVALID_HEADER;
6119 return ERROR_SUCCESS;
6122 /***********************************************************************
6123 * IsHostInProxyBypassList (@)
6125 * Undocumented
6128 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6130 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6131 return FALSE;
6134 /***********************************************************************
6135 * InternetShowSecurityInfoByURLA (@)
6137 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6139 FIXME("stub: %s %p\n", url, window);
6140 return FALSE;
6143 /***********************************************************************
6144 * InternetShowSecurityInfoByURLW (@)
6146 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6148 FIXME("stub: %s %p\n", debugstr_w(url), window);
6149 return FALSE;
6152 /***********************************************************************
6153 * ShowX509EncodedCertificate (@)
6155 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6157 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6158 cert, len);
6159 DWORD ret;
6161 if (certContext)
6163 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6165 memset(&view, 0, sizeof(view));
6166 view.hwndParent = parent;
6167 view.pCertContext = certContext;
6168 if (CryptUIDlgViewCertificateW(&view, NULL))
6169 ret = ERROR_SUCCESS;
6170 else
6171 ret = GetLastError();
6172 CertFreeCertificateContext(certContext);
6174 else
6175 ret = GetLastError();
6176 return ret;