wininet: Add support for setting and retrieving the connect timeout.
[wine/multimedia.git] / dlls / wininet / http.c
blobca8cc60580db25dba83b44b65fdd5ba3ac163d00
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 (size != sizeof(DWORD))
2186 return ERROR_INVALID_PARAMETER;
2188 req->connect_timeout = *(DWORD *)buffer;
2189 return ERROR_SUCCESS;
2191 case INTERNET_OPTION_SEND_TIMEOUT:
2192 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2193 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2195 if (size != sizeof(DWORD))
2196 return ERROR_INVALID_PARAMETER;
2198 if(!req->netconn) {
2199 FIXME("unsupported without active connection\n");
2200 return ERROR_SUCCESS;
2203 return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
2204 *(DWORD*)buffer);
2206 case INTERNET_OPTION_USERNAME:
2207 heap_free(req->session->userName);
2208 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2209 return ERROR_SUCCESS;
2211 case INTERNET_OPTION_PASSWORD:
2212 heap_free(req->session->password);
2213 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2214 return ERROR_SUCCESS;
2215 case INTERNET_OPTION_HTTP_DECODING:
2216 if(size != sizeof(BOOL))
2217 return ERROR_INVALID_PARAMETER;
2218 req->decoding = *(BOOL*)buffer;
2219 return ERROR_SUCCESS;
2222 return ERROR_INTERNET_INVALID_OPTION;
2225 /* read some more data into the read buffer (the read section must be held) */
2226 static DWORD read_more_data( http_request_t *req, int maxlen )
2228 DWORD res;
2229 int len;
2231 if (req->read_pos)
2233 /* move existing data to the start of the buffer */
2234 if(req->read_size)
2235 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2236 req->read_pos = 0;
2239 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2241 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2242 maxlen - req->read_size, 0, &len );
2243 if(res == ERROR_SUCCESS)
2244 req->read_size += len;
2246 return res;
2249 /* remove some amount of data from the read buffer (the read section must be held) */
2250 static void remove_data( http_request_t *req, int count )
2252 if (!(req->read_size -= count)) req->read_pos = 0;
2253 else req->read_pos += count;
2256 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2258 int count, bytes_read, pos = 0;
2259 DWORD res;
2261 EnterCriticalSection( &req->read_section );
2262 for (;;)
2264 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2266 if (eol)
2268 count = eol - (req->read_buf + req->read_pos);
2269 bytes_read = count + 1;
2271 else count = bytes_read = req->read_size;
2273 count = min( count, *len - pos );
2274 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2275 pos += count;
2276 remove_data( req, bytes_read );
2277 if (eol) break;
2279 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2281 *len = 0;
2282 TRACE( "returning empty string %u\n", res);
2283 LeaveCriticalSection( &req->read_section );
2284 INTERNET_SetLastError(res);
2285 return FALSE;
2288 LeaveCriticalSection( &req->read_section );
2290 if (pos < *len)
2292 if (pos && buffer[pos - 1] == '\r') pos--;
2293 *len = pos + 1;
2295 buffer[*len - 1] = 0;
2296 TRACE( "returning %s\n", debugstr_a(buffer));
2297 return TRUE;
2300 /* check if we have reached the end of the data to read (the read section must be held) */
2301 static BOOL end_of_read_data( http_request_t *req )
2303 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2306 /* fetch some more data into the read buffer (the read section must be held) */
2307 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2309 DWORD res, read=0;
2311 if(req->read_size == sizeof(req->read_buf))
2312 return ERROR_SUCCESS;
2314 if(req->read_pos) {
2315 if(req->read_size)
2316 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2317 req->read_pos = 0;
2320 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2321 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2322 req->read_size += read;
2324 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2325 if(read_bytes)
2326 *read_bytes = read;
2327 return res;
2330 /* return the size of data available to be read immediately (the read section must be held) */
2331 static DWORD get_avail_data( http_request_t *req )
2333 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2336 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2338 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2339 DWORD avail = 0;
2341 if(req->netconn)
2342 NETCON_query_data_available(req->netconn, &avail);
2343 return netconn_stream->content_length == ~0u
2344 ? avail
2345 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2348 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2350 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2351 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2354 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2355 DWORD *read, read_mode_t read_mode)
2357 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2358 int len = 0;
2360 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2362 if(read_mode == READMODE_NOBLOCK)
2363 size = min(size, netconn_get_avail_data(stream, req));
2365 if(size && req->netconn) {
2366 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2367 len = 0;
2368 if(!len)
2369 netconn_stream->content_length = netconn_stream->content_read;
2372 netconn_stream->content_read += *read = len;
2373 TRACE("read %u bytes\n", len);
2374 return ERROR_SUCCESS;
2377 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2379 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2380 BYTE buf[1024];
2381 DWORD avail;
2382 int len;
2384 if(netconn_end_of_data(stream, req))
2385 return TRUE;
2387 do {
2388 avail = netconn_get_avail_data(stream, req);
2389 if(!avail)
2390 return FALSE;
2392 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2393 return FALSE;
2395 netconn_stream->content_read += len;
2396 }while(netconn_stream->content_read < netconn_stream->content_length);
2398 return TRUE;
2401 static void netconn_destroy(data_stream_t *stream)
2405 static const data_stream_vtbl_t netconn_stream_vtbl = {
2406 netconn_get_avail_data,
2407 netconn_end_of_data,
2408 netconn_read,
2409 netconn_drain_content,
2410 netconn_destroy
2413 /* read some more data into the read buffer (the read section must be held) */
2414 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2416 DWORD res;
2417 int len;
2419 if (stream->buf_pos)
2421 /* move existing data to the start of the buffer */
2422 if(stream->buf_size)
2423 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2424 stream->buf_pos = 0;
2427 if (maxlen == -1) maxlen = sizeof(stream->buf);
2429 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2430 maxlen - stream->buf_size, 0, &len );
2431 if(res == ERROR_SUCCESS)
2432 stream->buf_size += len;
2434 return res;
2437 /* remove some amount of data from the read buffer (the read section must be held) */
2438 static void remove_chunked_data(chunked_stream_t *stream, int count)
2440 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2441 else stream->buf_pos += count;
2444 /* discard data contents until we reach end of line (the read section must be held) */
2445 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2447 DWORD res;
2451 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2452 if (eol)
2454 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2455 break;
2457 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2458 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2459 } while (stream->buf_size);
2460 return ERROR_SUCCESS;
2463 /* read the size of the next chunk (the read section must be held) */
2464 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2466 /* TODOO */
2467 DWORD chunk_size = 0, res;
2469 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2470 return res;
2472 for (;;)
2474 while (stream->buf_size)
2476 char ch = stream->buf[stream->buf_pos];
2477 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2478 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2479 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2480 else if (ch == ';' || ch == '\r' || ch == '\n')
2482 TRACE( "reading %u byte chunk\n", chunk_size );
2483 stream->chunk_size = chunk_size;
2484 req->contentLength += chunk_size;
2485 return discard_chunked_eol(stream, req);
2487 remove_chunked_data(stream, 1);
2489 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2490 if (!stream->buf_size)
2492 stream->chunk_size = 0;
2493 return ERROR_SUCCESS;
2498 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2500 /* Allow reading only from read buffer */
2501 return 0;
2504 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2506 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2507 return !chunked_stream->chunk_size;
2510 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2511 DWORD *read, read_mode_t read_mode)
2513 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2514 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2516 if(chunked_stream->chunk_size == ~0u) {
2517 res = start_next_chunk(chunked_stream, req);
2518 if(res != ERROR_SUCCESS)
2519 return res;
2522 while(size && chunked_stream->chunk_size) {
2523 if(chunked_stream->buf_size) {
2524 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2526 /* this could block */
2527 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2528 break;
2530 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2531 remove_chunked_data(chunked_stream, read_bytes);
2532 }else {
2533 read_bytes = min(size, chunked_stream->chunk_size);
2535 if(read_mode == READMODE_NOBLOCK) {
2536 DWORD avail;
2538 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2539 break;
2540 if(read_bytes > avail)
2541 read_bytes = avail;
2543 /* this could block */
2544 if(read_bytes == chunked_stream->chunk_size)
2545 break;
2548 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2549 if(res != ERROR_SUCCESS)
2550 break;
2553 chunked_stream->chunk_size -= read_bytes;
2554 size -= read_bytes;
2555 ret_read += read_bytes;
2556 if(!chunked_stream->chunk_size) {
2557 assert(read_mode != READMODE_NOBLOCK);
2558 res = start_next_chunk(chunked_stream, req);
2559 if(res != ERROR_SUCCESS)
2560 break;
2563 if(read_mode == READMODE_ASYNC)
2564 read_mode = READMODE_NOBLOCK;
2567 TRACE("read %u bytes\n", ret_read);
2568 *read = ret_read;
2569 return res;
2572 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2574 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2576 /* FIXME: we can do better */
2577 return !chunked_stream->chunk_size;
2580 static void chunked_destroy(data_stream_t *stream)
2582 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2583 heap_free(chunked_stream);
2586 static const data_stream_vtbl_t chunked_stream_vtbl = {
2587 chunked_get_avail_data,
2588 chunked_end_of_data,
2589 chunked_read,
2590 chunked_drain_content,
2591 chunked_destroy
2594 /* set the request content length based on the headers */
2595 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2597 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2598 WCHAR encoding[20];
2599 DWORD size;
2601 if(status_code == HTTP_STATUS_NO_CONTENT) {
2602 request->contentLength = request->netconn_stream.content_length = 0;
2603 return ERROR_SUCCESS;
2606 size = sizeof(request->contentLength);
2607 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2608 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2609 request->contentLength = ~0u;
2610 request->netconn_stream.content_length = request->contentLength;
2611 request->netconn_stream.content_read = request->read_size;
2613 size = sizeof(encoding);
2614 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2615 !strcmpiW(encoding, szChunked))
2617 chunked_stream_t *chunked_stream;
2619 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2620 if(!chunked_stream)
2621 return ERROR_OUTOFMEMORY;
2623 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2624 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2625 chunked_stream->chunk_size = ~0u;
2627 if(request->read_size) {
2628 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2629 chunked_stream->buf_size = request->read_size;
2630 request->read_size = request->read_pos = 0;
2633 request->data_stream = &chunked_stream->data_stream;
2634 request->contentLength = ~0u;
2635 request->read_chunked = TRUE;
2638 if(request->decoding) {
2639 int encoding_idx;
2641 static const WCHAR gzipW[] = {'g','z','i','p',0};
2643 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2644 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2645 return init_gzip_stream(request);
2648 return ERROR_SUCCESS;
2651 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2653 INTERNET_ASYNC_RESULT iar;
2655 iar.dwResult = result;
2656 iar.dwError = error;
2658 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2659 sizeof(INTERNET_ASYNC_RESULT));
2662 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2664 DWORD res, read = 0, avail = 0;
2665 read_mode_t mode;
2667 TRACE("%p\n", req);
2669 EnterCriticalSection( &req->read_section );
2671 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2672 res = refill_read_buffer(req, mode, &read);
2673 if(res == ERROR_SUCCESS && !first_notif)
2674 avail = get_avail_data(req);
2676 LeaveCriticalSection( &req->read_section );
2678 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2679 WARN("res %u read %u, closing connection\n", res, read);
2680 http_release_netconn(req, FALSE);
2683 if(res == ERROR_SUCCESS)
2684 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2685 else
2686 send_request_complete(req, 0, res);
2689 /* read data from the http connection (the read section must be held) */
2690 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2692 DWORD current_read = 0, ret_read = 0;
2693 read_mode_t read_mode;
2694 DWORD res = ERROR_SUCCESS;
2696 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2698 EnterCriticalSection( &req->read_section );
2700 if(req->read_size) {
2701 ret_read = min(size, req->read_size);
2702 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2703 req->read_size -= ret_read;
2704 req->read_pos += ret_read;
2705 if(read_mode == READMODE_ASYNC)
2706 read_mode = READMODE_NOBLOCK;
2709 if(ret_read < size) {
2710 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2711 ret_read += current_read;
2714 LeaveCriticalSection( &req->read_section );
2716 *read = ret_read;
2717 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2719 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2720 BOOL res;
2721 DWORD written;
2723 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2724 if(!res)
2725 WARN("WriteFile failed: %u\n", GetLastError());
2728 if(size && !ret_read)
2729 http_release_netconn(req, res == ERROR_SUCCESS);
2731 return res;
2735 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2737 http_request_t *req = (http_request_t*)hdr;
2738 DWORD res;
2740 EnterCriticalSection( &req->read_section );
2741 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2742 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2744 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2745 if(res == ERROR_SUCCESS)
2746 res = hdr->dwError;
2747 LeaveCriticalSection( &req->read_section );
2749 return res;
2752 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2754 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2755 http_request_t *req = (http_request_t*)workRequest->hdr;
2756 DWORD res;
2758 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2760 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2761 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2763 send_request_complete(req, res == ERROR_SUCCESS, res);
2766 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2767 DWORD flags, DWORD_PTR context)
2769 http_request_t *req = (http_request_t*)hdr;
2770 DWORD res, size, read, error = ERROR_SUCCESS;
2772 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2773 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2775 if (buffers->dwStructSize != sizeof(*buffers))
2776 return ERROR_INVALID_PARAMETER;
2778 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2780 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2782 WORKREQUEST workRequest;
2784 if (TryEnterCriticalSection( &req->read_section ))
2786 if (get_avail_data(req))
2788 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2789 &buffers->dwBufferLength, FALSE);
2790 size = buffers->dwBufferLength;
2791 LeaveCriticalSection( &req->read_section );
2792 goto done;
2794 LeaveCriticalSection( &req->read_section );
2797 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2798 workRequest.hdr = WININET_AddRef(&req->hdr);
2799 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2801 INTERNET_AsyncCall(&workRequest);
2803 return ERROR_IO_PENDING;
2806 read = 0;
2807 size = buffers->dwBufferLength;
2809 EnterCriticalSection( &req->read_section );
2810 if(hdr->dwError == ERROR_SUCCESS)
2811 hdr->dwError = INTERNET_HANDLE_IN_USE;
2812 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2813 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2815 while(1) {
2816 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2817 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2818 if(res != ERROR_SUCCESS)
2819 break;
2821 read += buffers->dwBufferLength;
2822 if(read == size || end_of_read_data(req))
2823 break;
2825 LeaveCriticalSection( &req->read_section );
2827 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2828 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2829 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2830 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2832 EnterCriticalSection( &req->read_section );
2835 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2836 hdr->dwError = ERROR_SUCCESS;
2837 else
2838 error = hdr->dwError;
2840 LeaveCriticalSection( &req->read_section );
2841 size = buffers->dwBufferLength;
2842 buffers->dwBufferLength = read;
2844 done:
2845 if (res == ERROR_SUCCESS) {
2846 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2847 &size, sizeof(size));
2850 return res==ERROR_SUCCESS ? error : res;
2853 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2855 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2856 http_request_t *req = (http_request_t*)workRequest->hdr;
2857 DWORD res;
2859 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2861 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2862 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2864 send_request_complete(req, res == ERROR_SUCCESS, res);
2867 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2868 DWORD flags, DWORD_PTR context)
2871 http_request_t *req = (http_request_t*)hdr;
2872 DWORD res, size, read, error = ERROR_SUCCESS;
2874 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2875 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2877 if (buffers->dwStructSize != sizeof(*buffers))
2878 return ERROR_INVALID_PARAMETER;
2880 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2882 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2884 WORKREQUEST workRequest;
2886 if (TryEnterCriticalSection( &req->read_section ))
2888 if (get_avail_data(req))
2890 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2891 &buffers->dwBufferLength, FALSE);
2892 size = buffers->dwBufferLength;
2893 LeaveCriticalSection( &req->read_section );
2894 goto done;
2896 LeaveCriticalSection( &req->read_section );
2899 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2900 workRequest.hdr = WININET_AddRef(&req->hdr);
2901 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2903 INTERNET_AsyncCall(&workRequest);
2905 return ERROR_IO_PENDING;
2908 read = 0;
2909 size = buffers->dwBufferLength;
2911 EnterCriticalSection( &req->read_section );
2912 if(hdr->dwError == ERROR_SUCCESS)
2913 hdr->dwError = INTERNET_HANDLE_IN_USE;
2914 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2915 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2917 while(1) {
2918 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2919 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2920 if(res != ERROR_SUCCESS)
2921 break;
2923 read += buffers->dwBufferLength;
2924 if(read == size || end_of_read_data(req))
2925 break;
2927 LeaveCriticalSection( &req->read_section );
2929 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2930 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2931 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2932 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2934 EnterCriticalSection( &req->read_section );
2937 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2938 hdr->dwError = ERROR_SUCCESS;
2939 else
2940 error = hdr->dwError;
2942 LeaveCriticalSection( &req->read_section );
2943 size = buffers->dwBufferLength;
2944 buffers->dwBufferLength = read;
2946 done:
2947 if (res == ERROR_SUCCESS) {
2948 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2949 &size, sizeof(size));
2952 return res==ERROR_SUCCESS ? error : res;
2955 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2957 DWORD res;
2958 http_request_t *request = (http_request_t*)hdr;
2960 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2962 *written = 0;
2963 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2964 if (res == ERROR_SUCCESS)
2965 request->bytesWritten += *written;
2967 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2968 return res;
2971 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2973 http_request_t *req = (http_request_t*)workRequest->hdr;
2975 HTTP_ReceiveRequestData(req, FALSE);
2978 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2980 http_request_t *req = (http_request_t*)hdr;
2982 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2984 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2986 WORKREQUEST workRequest;
2988 /* never wait, if we can't enter the section we queue an async request right away */
2989 if (TryEnterCriticalSection( &req->read_section ))
2991 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2992 if ((*available = get_avail_data( req ))) goto done;
2993 if (end_of_read_data( req )) goto done;
2994 LeaveCriticalSection( &req->read_section );
2997 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2998 workRequest.hdr = WININET_AddRef( &req->hdr );
3000 INTERNET_AsyncCall(&workRequest);
3002 return ERROR_IO_PENDING;
3005 EnterCriticalSection( &req->read_section );
3007 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3009 refill_read_buffer( req, READMODE_ASYNC, NULL );
3010 *available = get_avail_data( req );
3013 done:
3014 LeaveCriticalSection( &req->read_section );
3016 TRACE( "returning %u\n", *available );
3017 return ERROR_SUCCESS;
3020 static const object_vtbl_t HTTPREQVtbl = {
3021 HTTPREQ_Destroy,
3022 HTTPREQ_CloseConnection,
3023 HTTPREQ_QueryOption,
3024 HTTPREQ_SetOption,
3025 HTTPREQ_ReadFile,
3026 HTTPREQ_ReadFileExA,
3027 HTTPREQ_ReadFileExW,
3028 HTTPREQ_WriteFile,
3029 HTTPREQ_QueryDataAvailable,
3030 NULL
3033 /***********************************************************************
3034 * HTTP_HttpOpenRequestW (internal)
3036 * Open a HTTP request handle
3038 * RETURNS
3039 * HINTERNET a HTTP request handle on success
3040 * NULL on failure
3043 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3044 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3045 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3046 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3048 appinfo_t *hIC = session->appInfo;
3049 http_request_t *request;
3050 DWORD len, res = ERROR_SUCCESS;
3052 TRACE("-->\n");
3054 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3055 if(!request)
3056 return ERROR_OUTOFMEMORY;
3058 request->hdr.htype = WH_HHTTPREQ;
3059 request->hdr.dwFlags = dwFlags;
3060 request->hdr.dwContext = dwContext;
3061 request->contentLength = ~0u;
3063 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3064 request->data_stream = &request->netconn_stream.data_stream;
3065 request->connect_timeout = session->connect_timeout;
3067 InitializeCriticalSection( &request->read_section );
3068 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3070 WININET_AddRef( &session->hdr );
3071 request->session = session;
3072 list_add_head( &session->hdr.children, &request->hdr.entry );
3074 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3075 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3076 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3077 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3079 if (lpszObjectName && *lpszObjectName) {
3080 HRESULT rc;
3082 len = 0;
3083 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3084 if (rc != E_POINTER)
3085 len = strlenW(lpszObjectName)+1;
3086 request->path = heap_alloc(len*sizeof(WCHAR));
3087 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3088 URL_ESCAPE_SPACES_ONLY);
3089 if (rc != S_OK)
3091 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3092 strcpyW(request->path,lpszObjectName);
3094 }else {
3095 static const WCHAR slashW[] = {'/',0};
3097 request->path = heap_strdupW(slashW);
3100 if (lpszReferrer && *lpszReferrer)
3101 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3103 if (lpszAcceptTypes)
3105 int i;
3106 for (i = 0; lpszAcceptTypes[i]; i++)
3108 if (!*lpszAcceptTypes[i]) continue;
3109 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3110 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3111 HTTP_ADDHDR_FLAG_REQ |
3112 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3116 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3117 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3119 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3120 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3121 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3123 WCHAR *host_name;
3125 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3127 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3128 if (!host_name) {
3129 res = ERROR_OUTOFMEMORY;
3130 goto lend;
3133 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3134 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3135 heap_free(host_name);
3137 else
3138 HTTP_ProcessHeader(request, hostW, session->hostName,
3139 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3141 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3142 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3143 INTERNET_DEFAULT_HTTPS_PORT :
3144 INTERNET_DEFAULT_HTTP_PORT);
3146 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3147 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3148 INTERNET_DEFAULT_HTTPS_PORT :
3149 INTERNET_DEFAULT_HTTP_PORT);
3151 if (hIC->proxy && hIC->proxy[0])
3152 HTTP_DealWithProxy( hIC, session, request );
3154 INTERNET_SendCallback(&session->hdr, dwContext,
3155 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3156 sizeof(HINTERNET));
3158 lend:
3159 TRACE("<-- %u (%p)\n", res, request);
3161 if(res != ERROR_SUCCESS) {
3162 WININET_Release( &request->hdr );
3163 *ret = NULL;
3164 return res;
3167 *ret = request->hdr.hInternet;
3168 return ERROR_SUCCESS;
3171 /***********************************************************************
3172 * HttpOpenRequestW (WININET.@)
3174 * Open a HTTP request handle
3176 * RETURNS
3177 * HINTERNET a HTTP request handle on success
3178 * NULL on failure
3181 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3182 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3183 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3184 DWORD dwFlags, DWORD_PTR dwContext)
3186 http_session_t *session;
3187 HINTERNET handle = NULL;
3188 DWORD res;
3190 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3191 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3192 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3193 dwFlags, dwContext);
3194 if(lpszAcceptTypes!=NULL)
3196 int i;
3197 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3198 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3201 session = (http_session_t*) get_handle_object( hHttpSession );
3202 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3204 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3205 goto lend;
3209 * My tests seem to show that the windows version does not
3210 * become asynchronous until after this point. And anyhow
3211 * if this call was asynchronous then how would you get the
3212 * necessary HINTERNET pointer returned by this function.
3215 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3216 lpszVersion, lpszReferrer, lpszAcceptTypes,
3217 dwFlags, dwContext, &handle);
3218 lend:
3219 if( session )
3220 WININET_Release( &session->hdr );
3221 TRACE("returning %p\n", handle);
3222 if(res != ERROR_SUCCESS)
3223 SetLastError(res);
3224 return handle;
3227 static const LPCWSTR header_lookup[] = {
3228 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3229 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3230 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3231 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3232 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3233 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3234 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3235 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3236 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3237 szDate, /* HTTP_QUERY_DATE = 9 */
3238 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3239 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3240 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3241 szURI, /* HTTP_QUERY_URI = 13 */
3242 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3243 NULL, /* HTTP_QUERY_COST = 15 */
3244 NULL, /* HTTP_QUERY_LINK = 16 */
3245 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3246 NULL, /* HTTP_QUERY_VERSION = 18 */
3247 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3248 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3249 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3250 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3251 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3252 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3253 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3254 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3255 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3256 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3257 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3258 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3259 NULL, /* HTTP_QUERY_FROM = 31 */
3260 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3261 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3262 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3263 szReferer, /* HTTP_QUERY_REFERER = 35 */
3264 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3265 szServer, /* HTTP_QUERY_SERVER = 37 */
3266 NULL, /* HTTP_TITLE = 38 */
3267 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3268 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3269 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3270 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3271 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3272 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3273 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3274 NULL, /* HTTP_QUERY_REFRESH = 46 */
3275 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3276 szAge, /* HTTP_QUERY_AGE = 48 */
3277 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3278 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3279 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3280 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3281 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3282 szETag, /* HTTP_QUERY_ETAG = 54 */
3283 hostW, /* HTTP_QUERY_HOST = 55 */
3284 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3285 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3286 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3287 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3288 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3289 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3290 szRange, /* HTTP_QUERY_RANGE = 62 */
3291 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3292 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3293 szVary, /* HTTP_QUERY_VARY = 65 */
3294 szVia, /* HTTP_QUERY_VIA = 66 */
3295 szWarning, /* HTTP_QUERY_WARNING = 67 */
3296 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3297 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3298 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3301 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3303 /***********************************************************************
3304 * HTTP_HttpQueryInfoW (internal)
3306 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3307 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3309 LPHTTPHEADERW lphttpHdr = NULL;
3310 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3311 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3312 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3313 INT index = -1;
3315 /* Find requested header structure */
3316 switch (level)
3318 case HTTP_QUERY_CUSTOM:
3319 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3320 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3321 break;
3322 case HTTP_QUERY_RAW_HEADERS_CRLF:
3324 LPWSTR headers;
3325 DWORD len = 0;
3326 DWORD res = ERROR_INVALID_PARAMETER;
3328 if (request_only)
3329 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3330 else
3331 headers = request->rawHeaders;
3333 if (headers)
3334 len = strlenW(headers) * sizeof(WCHAR);
3336 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3338 len += sizeof(WCHAR);
3339 res = ERROR_INSUFFICIENT_BUFFER;
3341 else if (lpBuffer)
3343 if (headers)
3344 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3345 else
3347 len = strlenW(szCrLf) * sizeof(WCHAR);
3348 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3350 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3351 res = ERROR_SUCCESS;
3353 *lpdwBufferLength = len;
3355 if (request_only) heap_free(headers);
3356 return res;
3358 case HTTP_QUERY_RAW_HEADERS:
3360 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3361 DWORD i, size = 0;
3362 LPWSTR pszString = lpBuffer;
3364 for (i = 0; ppszRawHeaderLines[i]; i++)
3365 size += strlenW(ppszRawHeaderLines[i]) + 1;
3367 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3369 HTTP_FreeTokens(ppszRawHeaderLines);
3370 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3371 return ERROR_INSUFFICIENT_BUFFER;
3373 if (pszString)
3375 for (i = 0; ppszRawHeaderLines[i]; i++)
3377 DWORD len = strlenW(ppszRawHeaderLines[i]);
3378 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3379 pszString += len+1;
3381 *pszString = '\0';
3382 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3384 *lpdwBufferLength = size * sizeof(WCHAR);
3385 HTTP_FreeTokens(ppszRawHeaderLines);
3387 return ERROR_SUCCESS;
3389 case HTTP_QUERY_STATUS_TEXT:
3390 if (request->statusText)
3392 DWORD len = strlenW(request->statusText);
3393 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3395 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3396 return ERROR_INSUFFICIENT_BUFFER;
3398 if (lpBuffer)
3400 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3401 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3403 *lpdwBufferLength = len * sizeof(WCHAR);
3404 return ERROR_SUCCESS;
3406 break;
3407 case HTTP_QUERY_VERSION:
3408 if (request->version)
3410 DWORD len = strlenW(request->version);
3411 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3413 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3414 return ERROR_INSUFFICIENT_BUFFER;
3416 if (lpBuffer)
3418 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3419 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3421 *lpdwBufferLength = len * sizeof(WCHAR);
3422 return ERROR_SUCCESS;
3424 break;
3425 case HTTP_QUERY_CONTENT_ENCODING:
3426 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3427 requested_index,request_only);
3428 break;
3429 default:
3430 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3432 if (level < LAST_TABLE_HEADER && header_lookup[level])
3433 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3434 requested_index,request_only);
3437 if (index >= 0)
3438 lphttpHdr = &request->custHeaders[index];
3440 /* Ensure header satisfies requested attributes */
3441 if (!lphttpHdr ||
3442 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3443 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3445 return ERROR_HTTP_HEADER_NOT_FOUND;
3448 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3450 /* coalesce value to requested type */
3451 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3453 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3454 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3456 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3458 time_t tmpTime;
3459 struct tm tmpTM;
3460 SYSTEMTIME *STHook;
3462 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3464 tmpTM = *gmtime(&tmpTime);
3465 STHook = (SYSTEMTIME *)lpBuffer;
3466 STHook->wDay = tmpTM.tm_mday;
3467 STHook->wHour = tmpTM.tm_hour;
3468 STHook->wMilliseconds = 0;
3469 STHook->wMinute = tmpTM.tm_min;
3470 STHook->wDayOfWeek = tmpTM.tm_wday;
3471 STHook->wMonth = tmpTM.tm_mon + 1;
3472 STHook->wSecond = tmpTM.tm_sec;
3473 STHook->wYear = tmpTM.tm_year;
3475 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3476 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3477 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3479 else if (lphttpHdr->lpszValue)
3481 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3483 if (len > *lpdwBufferLength)
3485 *lpdwBufferLength = len;
3486 return ERROR_INSUFFICIENT_BUFFER;
3488 if (lpBuffer)
3490 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3491 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3493 *lpdwBufferLength = len - sizeof(WCHAR);
3495 return ERROR_SUCCESS;
3498 /***********************************************************************
3499 * HttpQueryInfoW (WININET.@)
3501 * Queries for information about an HTTP request
3503 * RETURNS
3504 * TRUE on success
3505 * FALSE on failure
3508 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3509 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3511 http_request_t *request;
3512 DWORD res;
3514 if (TRACE_ON(wininet)) {
3515 #define FE(x) { x, #x }
3516 static const wininet_flag_info query_flags[] = {
3517 FE(HTTP_QUERY_MIME_VERSION),
3518 FE(HTTP_QUERY_CONTENT_TYPE),
3519 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3520 FE(HTTP_QUERY_CONTENT_ID),
3521 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3522 FE(HTTP_QUERY_CONTENT_LENGTH),
3523 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3524 FE(HTTP_QUERY_ALLOW),
3525 FE(HTTP_QUERY_PUBLIC),
3526 FE(HTTP_QUERY_DATE),
3527 FE(HTTP_QUERY_EXPIRES),
3528 FE(HTTP_QUERY_LAST_MODIFIED),
3529 FE(HTTP_QUERY_MESSAGE_ID),
3530 FE(HTTP_QUERY_URI),
3531 FE(HTTP_QUERY_DERIVED_FROM),
3532 FE(HTTP_QUERY_COST),
3533 FE(HTTP_QUERY_LINK),
3534 FE(HTTP_QUERY_PRAGMA),
3535 FE(HTTP_QUERY_VERSION),
3536 FE(HTTP_QUERY_STATUS_CODE),
3537 FE(HTTP_QUERY_STATUS_TEXT),
3538 FE(HTTP_QUERY_RAW_HEADERS),
3539 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3540 FE(HTTP_QUERY_CONNECTION),
3541 FE(HTTP_QUERY_ACCEPT),
3542 FE(HTTP_QUERY_ACCEPT_CHARSET),
3543 FE(HTTP_QUERY_ACCEPT_ENCODING),
3544 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3545 FE(HTTP_QUERY_AUTHORIZATION),
3546 FE(HTTP_QUERY_CONTENT_ENCODING),
3547 FE(HTTP_QUERY_FORWARDED),
3548 FE(HTTP_QUERY_FROM),
3549 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3550 FE(HTTP_QUERY_LOCATION),
3551 FE(HTTP_QUERY_ORIG_URI),
3552 FE(HTTP_QUERY_REFERER),
3553 FE(HTTP_QUERY_RETRY_AFTER),
3554 FE(HTTP_QUERY_SERVER),
3555 FE(HTTP_QUERY_TITLE),
3556 FE(HTTP_QUERY_USER_AGENT),
3557 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3558 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3559 FE(HTTP_QUERY_ACCEPT_RANGES),
3560 FE(HTTP_QUERY_SET_COOKIE),
3561 FE(HTTP_QUERY_COOKIE),
3562 FE(HTTP_QUERY_REQUEST_METHOD),
3563 FE(HTTP_QUERY_REFRESH),
3564 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3565 FE(HTTP_QUERY_AGE),
3566 FE(HTTP_QUERY_CACHE_CONTROL),
3567 FE(HTTP_QUERY_CONTENT_BASE),
3568 FE(HTTP_QUERY_CONTENT_LOCATION),
3569 FE(HTTP_QUERY_CONTENT_MD5),
3570 FE(HTTP_QUERY_CONTENT_RANGE),
3571 FE(HTTP_QUERY_ETAG),
3572 FE(HTTP_QUERY_HOST),
3573 FE(HTTP_QUERY_IF_MATCH),
3574 FE(HTTP_QUERY_IF_NONE_MATCH),
3575 FE(HTTP_QUERY_IF_RANGE),
3576 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3577 FE(HTTP_QUERY_MAX_FORWARDS),
3578 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3579 FE(HTTP_QUERY_RANGE),
3580 FE(HTTP_QUERY_TRANSFER_ENCODING),
3581 FE(HTTP_QUERY_UPGRADE),
3582 FE(HTTP_QUERY_VARY),
3583 FE(HTTP_QUERY_VIA),
3584 FE(HTTP_QUERY_WARNING),
3585 FE(HTTP_QUERY_CUSTOM)
3587 static const wininet_flag_info modifier_flags[] = {
3588 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3589 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3590 FE(HTTP_QUERY_FLAG_NUMBER),
3591 FE(HTTP_QUERY_FLAG_COALESCE)
3593 #undef FE
3594 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3595 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3596 DWORD i;
3598 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3599 TRACE(" Attribute:");
3600 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3601 if (query_flags[i].val == info) {
3602 TRACE(" %s", query_flags[i].name);
3603 break;
3606 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3607 TRACE(" Unknown (%08x)", info);
3610 TRACE(" Modifier:");
3611 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3612 if (modifier_flags[i].val & info_mod) {
3613 TRACE(" %s", modifier_flags[i].name);
3614 info_mod &= ~ modifier_flags[i].val;
3618 if (info_mod) {
3619 TRACE(" Unknown (%08x)", info_mod);
3621 TRACE("\n");
3624 request = (http_request_t*) get_handle_object( hHttpRequest );
3625 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3627 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3628 goto lend;
3631 if (lpBuffer == NULL)
3632 *lpdwBufferLength = 0;
3633 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3634 lpBuffer, lpdwBufferLength, lpdwIndex);
3636 lend:
3637 if( request )
3638 WININET_Release( &request->hdr );
3640 TRACE("%u <--\n", res);
3641 if(res != ERROR_SUCCESS)
3642 SetLastError(res);
3643 return res == ERROR_SUCCESS;
3646 /***********************************************************************
3647 * HttpQueryInfoA (WININET.@)
3649 * Queries for information about an HTTP request
3651 * RETURNS
3652 * TRUE on success
3653 * FALSE on failure
3656 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3657 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3659 BOOL result;
3660 DWORD len;
3661 WCHAR* bufferW;
3663 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3664 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3666 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3667 lpdwBufferLength, lpdwIndex );
3670 if (lpBuffer)
3672 DWORD alloclen;
3673 len = (*lpdwBufferLength)*sizeof(WCHAR);
3674 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3676 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3677 if (alloclen < len)
3678 alloclen = len;
3680 else
3681 alloclen = len;
3682 bufferW = heap_alloc(alloclen);
3683 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3684 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3685 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3686 } else
3688 bufferW = NULL;
3689 len = 0;
3692 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3693 &len, lpdwIndex );
3694 if( result )
3696 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3697 lpBuffer, *lpdwBufferLength, NULL, NULL );
3698 *lpdwBufferLength = len - 1;
3700 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3702 else
3703 /* since the strings being returned from HttpQueryInfoW should be
3704 * only ASCII characters, it is reasonable to assume that all of
3705 * the Unicode characters can be reduced to a single byte */
3706 *lpdwBufferLength = len / sizeof(WCHAR);
3708 heap_free( bufferW );
3709 return result;
3712 /***********************************************************************
3713 * HTTP_GetRedirectURL (internal)
3715 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3717 static WCHAR szHttp[] = {'h','t','t','p',0};
3718 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3719 http_session_t *session = request->session;
3720 URL_COMPONENTSW urlComponents;
3721 DWORD url_length = 0;
3722 LPWSTR orig_url;
3723 LPWSTR combined_url;
3725 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3726 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3727 urlComponents.dwSchemeLength = 0;
3728 urlComponents.lpszHostName = session->hostName;
3729 urlComponents.dwHostNameLength = 0;
3730 urlComponents.nPort = session->hostPort;
3731 urlComponents.lpszUserName = session->userName;
3732 urlComponents.dwUserNameLength = 0;
3733 urlComponents.lpszPassword = NULL;
3734 urlComponents.dwPasswordLength = 0;
3735 urlComponents.lpszUrlPath = request->path;
3736 urlComponents.dwUrlPathLength = 0;
3737 urlComponents.lpszExtraInfo = NULL;
3738 urlComponents.dwExtraInfoLength = 0;
3740 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3741 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3742 return NULL;
3744 orig_url = heap_alloc(url_length);
3746 /* convert from bytes to characters */
3747 url_length = url_length / sizeof(WCHAR) - 1;
3748 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3750 heap_free(orig_url);
3751 return NULL;
3754 url_length = 0;
3755 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3756 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3758 heap_free(orig_url);
3759 return NULL;
3761 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3763 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3765 heap_free(orig_url);
3766 heap_free(combined_url);
3767 return NULL;
3769 heap_free(orig_url);
3770 return combined_url;
3774 /***********************************************************************
3775 * HTTP_HandleRedirect (internal)
3777 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3779 http_session_t *session = request->session;
3780 appinfo_t *hIC = session->appInfo;
3781 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3782 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3783 int index;
3785 if(lpszUrl[0]=='/')
3787 /* if it's an absolute path, keep the same session info */
3788 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3790 else
3792 URL_COMPONENTSW urlComponents;
3793 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3794 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3795 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3796 static WCHAR szHttp[] = {'h','t','t','p',0};
3797 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3799 userName[0] = 0;
3800 hostName[0] = 0;
3801 protocol[0] = 0;
3803 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3804 urlComponents.lpszScheme = protocol;
3805 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3806 urlComponents.lpszHostName = hostName;
3807 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3808 urlComponents.lpszUserName = userName;
3809 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3810 urlComponents.lpszPassword = NULL;
3811 urlComponents.dwPasswordLength = 0;
3812 urlComponents.lpszUrlPath = path;
3813 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3814 urlComponents.lpszExtraInfo = NULL;
3815 urlComponents.dwExtraInfoLength = 0;
3816 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3817 return INTERNET_GetLastError();
3819 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3820 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3822 TRACE("redirect from secure page to non-secure page\n");
3823 /* FIXME: warn about from secure redirect to non-secure page */
3824 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3826 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3827 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3829 TRACE("redirect from non-secure page to secure page\n");
3830 /* FIXME: notify about redirect to secure page */
3831 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3834 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3836 if (lstrlenW(protocol)>4) /*https*/
3837 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3838 else /*http*/
3839 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3842 #if 0
3844 * This upsets redirects to binary files on sourceforge.net
3845 * and gives an html page instead of the target file
3846 * Examination of the HTTP request sent by native wininet.dll
3847 * reveals that it doesn't send a referrer in that case.
3848 * Maybe there's a flag that enables this, or maybe a referrer
3849 * shouldn't be added in case of a redirect.
3852 /* consider the current host as the referrer */
3853 if (session->lpszServerName && *session->lpszServerName)
3854 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3855 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3856 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3857 #endif
3859 heap_free(session->hostName);
3860 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3861 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3863 int len;
3864 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3865 len = lstrlenW(hostName);
3866 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3867 session->hostName = heap_alloc(len*sizeof(WCHAR));
3868 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3870 else
3871 session->hostName = heap_strdupW(hostName);
3873 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3875 heap_free(session->userName);
3876 session->userName = NULL;
3877 if (userName[0])
3878 session->userName = heap_strdupW(userName);
3880 reset_data_stream(request);
3882 if(!using_proxy) {
3883 if(strcmpiW(session->serverName, hostName)) {
3884 heap_free(session->serverName);
3885 session->serverName = heap_strdupW(hostName);
3887 session->serverPort = urlComponents.nPort;
3890 heap_free(request->path);
3891 request->path=NULL;
3892 if (*path)
3894 DWORD needed = 0;
3895 HRESULT rc;
3897 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3898 if (rc != E_POINTER)
3899 needed = strlenW(path)+1;
3900 request->path = heap_alloc(needed*sizeof(WCHAR));
3901 rc = UrlEscapeW(path, request->path, &needed,
3902 URL_ESCAPE_SPACES_ONLY);
3903 if (rc != S_OK)
3905 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3906 strcpyW(request->path,path);
3910 /* Remove custom content-type/length headers on redirects. */
3911 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3912 if (0 <= index)
3913 HTTP_DeleteCustomHeader(request, index);
3914 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3915 if (0 <= index)
3916 HTTP_DeleteCustomHeader(request, index);
3918 return ERROR_SUCCESS;
3921 /***********************************************************************
3922 * HTTP_build_req (internal)
3924 * concatenate all the strings in the request together
3926 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3928 LPCWSTR *t;
3929 LPWSTR str;
3931 for( t = list; *t ; t++ )
3932 len += strlenW( *t );
3933 len++;
3935 str = heap_alloc(len*sizeof(WCHAR));
3936 *str = 0;
3938 for( t = list; *t ; t++ )
3939 strcatW( str, *t );
3941 return str;
3944 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3946 LPWSTR lpszPath;
3947 LPWSTR requestString;
3948 INT len;
3949 INT cnt;
3950 INT responseLen;
3951 char *ascii_req;
3952 DWORD res;
3953 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3954 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3955 http_session_t *session = request->session;
3957 TRACE("\n");
3959 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3960 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3961 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3962 heap_free( lpszPath );
3964 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3965 NULL, 0, NULL, NULL );
3966 len--; /* the nul terminator isn't needed */
3967 ascii_req = heap_alloc(len);
3968 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3969 heap_free( requestString );
3971 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3973 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3974 heap_free( ascii_req );
3975 if (res != ERROR_SUCCESS)
3976 return res;
3978 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3979 if (!responseLen)
3980 return ERROR_HTTP_INVALID_HEADER;
3982 return ERROR_SUCCESS;
3985 static void HTTP_InsertCookies(http_request_t *request)
3987 DWORD cookie_size, size, cnt = 0;
3988 HTTPHEADERW *host;
3989 WCHAR *cookies;
3991 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3993 host = HTTP_GetHeader(request, hostW);
3994 if(!host)
3995 return;
3997 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3998 return;
4000 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4001 if(!(cookies = heap_alloc(size)))
4002 return;
4004 cnt += sprintfW(cookies, cookieW);
4005 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4006 strcatW(cookies, szCrLf);
4008 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4010 heap_free(cookies);
4013 static WORD HTTP_ParseWkday(LPCWSTR day)
4015 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4016 { 'm','o','n',0 },
4017 { 't','u','e',0 },
4018 { 'w','e','d',0 },
4019 { 't','h','u',0 },
4020 { 'f','r','i',0 },
4021 { 's','a','t',0 }};
4022 int i;
4023 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4024 if (!strcmpiW(day, days[i]))
4025 return i;
4027 /* Invalid */
4028 return 7;
4031 static WORD HTTP_ParseMonth(LPCWSTR month)
4033 static const WCHAR jan[] = { 'j','a','n',0 };
4034 static const WCHAR feb[] = { 'f','e','b',0 };
4035 static const WCHAR mar[] = { 'm','a','r',0 };
4036 static const WCHAR apr[] = { 'a','p','r',0 };
4037 static const WCHAR may[] = { 'm','a','y',0 };
4038 static const WCHAR jun[] = { 'j','u','n',0 };
4039 static const WCHAR jul[] = { 'j','u','l',0 };
4040 static const WCHAR aug[] = { 'a','u','g',0 };
4041 static const WCHAR sep[] = { 's','e','p',0 };
4042 static const WCHAR oct[] = { 'o','c','t',0 };
4043 static const WCHAR nov[] = { 'n','o','v',0 };
4044 static const WCHAR dec[] = { 'd','e','c',0 };
4046 if (!strcmpiW(month, jan)) return 1;
4047 if (!strcmpiW(month, feb)) return 2;
4048 if (!strcmpiW(month, mar)) return 3;
4049 if (!strcmpiW(month, apr)) return 4;
4050 if (!strcmpiW(month, may)) return 5;
4051 if (!strcmpiW(month, jun)) return 6;
4052 if (!strcmpiW(month, jul)) return 7;
4053 if (!strcmpiW(month, aug)) return 8;
4054 if (!strcmpiW(month, sep)) return 9;
4055 if (!strcmpiW(month, oct)) return 10;
4056 if (!strcmpiW(month, nov)) return 11;
4057 if (!strcmpiW(month, dec)) return 12;
4058 /* Invalid */
4059 return 0;
4062 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4063 * optionally preceded by whitespace.
4064 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4065 * st, and sets *str to the first character after the time format.
4067 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4069 LPCWSTR ptr = *str;
4070 WCHAR *nextPtr;
4071 unsigned long num;
4073 while (isspaceW(*ptr))
4074 ptr++;
4076 num = strtoulW(ptr, &nextPtr, 10);
4077 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4079 ERR("unexpected time format %s\n", debugstr_w(ptr));
4080 return FALSE;
4082 if (num > 23)
4084 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4085 return FALSE;
4087 ptr = nextPtr + 1;
4088 st->wHour = (WORD)num;
4089 num = strtoulW(ptr, &nextPtr, 10);
4090 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4092 ERR("unexpected time format %s\n", debugstr_w(ptr));
4093 return FALSE;
4095 if (num > 59)
4097 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4098 return FALSE;
4100 ptr = nextPtr + 1;
4101 st->wMinute = (WORD)num;
4102 num = strtoulW(ptr, &nextPtr, 10);
4103 if (!nextPtr || nextPtr <= ptr)
4105 ERR("unexpected time format %s\n", debugstr_w(ptr));
4106 return FALSE;
4108 if (num > 59)
4110 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4111 return FALSE;
4113 ptr = nextPtr + 1;
4114 *str = ptr;
4115 st->wSecond = (WORD)num;
4116 return TRUE;
4119 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4121 static const WCHAR gmt[]= { 'G','M','T',0 };
4122 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4123 LPCWSTR ptr;
4124 SYSTEMTIME st = { 0 };
4125 unsigned long num;
4127 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4128 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4129 *dayPtr = *ptr;
4130 *dayPtr = 0;
4131 st.wDayOfWeek = HTTP_ParseWkday(day);
4132 if (st.wDayOfWeek >= 7)
4134 ERR("unexpected weekday %s\n", debugstr_w(day));
4135 return FALSE;
4138 while (isspaceW(*ptr))
4139 ptr++;
4141 for (monthPtr = month; !isspace(*ptr) &&
4142 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4143 monthPtr++, ptr++)
4144 *monthPtr = *ptr;
4145 *monthPtr = 0;
4146 st.wMonth = HTTP_ParseMonth(month);
4147 if (!st.wMonth || st.wMonth > 12)
4149 ERR("unexpected month %s\n", debugstr_w(month));
4150 return FALSE;
4153 while (isspaceW(*ptr))
4154 ptr++;
4156 num = strtoulW(ptr, &nextPtr, 10);
4157 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4159 ERR("unexpected day %s\n", debugstr_w(ptr));
4160 return FALSE;
4162 ptr = nextPtr;
4163 st.wDay = (WORD)num;
4165 while (isspaceW(*ptr))
4166 ptr++;
4168 if (!HTTP_ParseTime(&st, &ptr))
4169 return FALSE;
4171 while (isspaceW(*ptr))
4172 ptr++;
4174 num = strtoulW(ptr, &nextPtr, 10);
4175 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4177 ERR("unexpected year %s\n", debugstr_w(ptr));
4178 return FALSE;
4180 ptr = nextPtr;
4181 st.wYear = (WORD)num;
4183 while (isspaceW(*ptr))
4184 ptr++;
4186 /* asctime() doesn't report a timezone, but some web servers do, so accept
4187 * with or without GMT.
4189 if (*ptr && strcmpW(ptr, gmt))
4191 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4192 return FALSE;
4194 return SystemTimeToFileTime(&st, ft);
4197 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4199 static const WCHAR gmt[]= { 'G','M','T',0 };
4200 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4201 LPCWSTR ptr;
4202 unsigned long num;
4203 SYSTEMTIME st = { 0 };
4205 ptr = strchrW(value, ',');
4206 if (!ptr)
4207 return FALSE;
4208 if (ptr - value != 3)
4210 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4211 return FALSE;
4213 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4214 day[3] = 0;
4215 st.wDayOfWeek = HTTP_ParseWkday(day);
4216 if (st.wDayOfWeek > 6)
4218 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4219 return FALSE;
4221 ptr++;
4223 while (isspaceW(*ptr))
4224 ptr++;
4226 num = strtoulW(ptr, &nextPtr, 10);
4227 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4229 WARN("unexpected day %s\n", debugstr_w(value));
4230 return FALSE;
4232 ptr = nextPtr;
4233 st.wDay = (WORD)num;
4235 while (isspaceW(*ptr))
4236 ptr++;
4238 for (monthPtr = month; !isspace(*ptr) &&
4239 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4240 monthPtr++, ptr++)
4241 *monthPtr = *ptr;
4242 *monthPtr = 0;
4243 st.wMonth = HTTP_ParseMonth(month);
4244 if (!st.wMonth || st.wMonth > 12)
4246 WARN("unexpected month %s\n", debugstr_w(month));
4247 return FALSE;
4250 while (isspaceW(*ptr))
4251 ptr++;
4253 num = strtoulW(ptr, &nextPtr, 10);
4254 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4256 ERR("unexpected year %s\n", debugstr_w(value));
4257 return FALSE;
4259 ptr = nextPtr;
4260 st.wYear = (WORD)num;
4262 if (!HTTP_ParseTime(&st, &ptr))
4263 return FALSE;
4265 while (isspaceW(*ptr))
4266 ptr++;
4268 if (strcmpW(ptr, gmt))
4270 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4271 return FALSE;
4273 return SystemTimeToFileTime(&st, ft);
4276 static WORD HTTP_ParseWeekday(LPCWSTR day)
4278 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4279 { 'm','o','n','d','a','y',0 },
4280 { 't','u','e','s','d','a','y',0 },
4281 { 'w','e','d','n','e','s','d','a','y',0 },
4282 { 't','h','u','r','s','d','a','y',0 },
4283 { 'f','r','i','d','a','y',0 },
4284 { 's','a','t','u','r','d','a','y',0 }};
4285 int i;
4286 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4287 if (!strcmpiW(day, days[i]))
4288 return i;
4290 /* Invalid */
4291 return 7;
4294 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4296 static const WCHAR gmt[]= { 'G','M','T',0 };
4297 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4298 LPCWSTR ptr;
4299 unsigned long num;
4300 SYSTEMTIME st = { 0 };
4302 ptr = strchrW(value, ',');
4303 if (!ptr)
4304 return FALSE;
4305 if (ptr - value == 3)
4307 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4308 day[3] = 0;
4309 st.wDayOfWeek = HTTP_ParseWkday(day);
4310 if (st.wDayOfWeek > 6)
4312 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4313 return FALSE;
4316 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4318 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4319 day[ptr - value + 1] = 0;
4320 st.wDayOfWeek = HTTP_ParseWeekday(day);
4321 if (st.wDayOfWeek > 6)
4323 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4324 return FALSE;
4327 else
4329 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4330 return FALSE;
4332 ptr++;
4334 while (isspaceW(*ptr))
4335 ptr++;
4337 num = strtoulW(ptr, &nextPtr, 10);
4338 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4340 ERR("unexpected day %s\n", debugstr_w(value));
4341 return FALSE;
4343 ptr = nextPtr;
4344 st.wDay = (WORD)num;
4346 if (*ptr != '-')
4348 ERR("unexpected month format %s\n", debugstr_w(ptr));
4349 return FALSE;
4351 ptr++;
4353 for (monthPtr = month; *ptr != '-' &&
4354 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4355 monthPtr++, ptr++)
4356 *monthPtr = *ptr;
4357 *monthPtr = 0;
4358 st.wMonth = HTTP_ParseMonth(month);
4359 if (!st.wMonth || st.wMonth > 12)
4361 ERR("unexpected month %s\n", debugstr_w(month));
4362 return FALSE;
4365 if (*ptr != '-')
4367 ERR("unexpected year format %s\n", debugstr_w(ptr));
4368 return FALSE;
4370 ptr++;
4372 num = strtoulW(ptr, &nextPtr, 10);
4373 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4375 ERR("unexpected year %s\n", debugstr_w(value));
4376 return FALSE;
4378 ptr = nextPtr;
4379 st.wYear = (WORD)num;
4381 if (!HTTP_ParseTime(&st, &ptr))
4382 return FALSE;
4384 while (isspaceW(*ptr))
4385 ptr++;
4387 if (strcmpW(ptr, gmt))
4389 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4390 return FALSE;
4392 return SystemTimeToFileTime(&st, ft);
4395 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4397 static const WCHAR zero[] = { '0',0 };
4398 BOOL ret;
4400 if (!strcmpW(value, zero))
4402 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4403 ret = TRUE;
4405 else if (strchrW(value, ','))
4407 ret = HTTP_ParseRfc1123Date(value, ft);
4408 if (!ret)
4410 ret = HTTP_ParseRfc850Date(value, ft);
4411 if (!ret)
4412 ERR("unexpected date format %s\n", debugstr_w(value));
4415 else
4417 ret = HTTP_ParseDateAsAsctime(value, ft);
4418 if (!ret)
4419 ERR("unexpected date format %s\n", debugstr_w(value));
4421 return ret;
4424 static void HTTP_ProcessExpires(http_request_t *request)
4426 BOOL expirationFound = FALSE;
4427 int headerIndex;
4429 /* Look for a Cache-Control header with a max-age directive, as it takes
4430 * precedence over the Expires header.
4432 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4433 if (headerIndex != -1)
4435 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4436 LPWSTR ptr;
4438 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4440 LPWSTR comma = strchrW(ptr, ','), end, equal;
4442 if (comma)
4443 end = comma;
4444 else
4445 end = ptr + strlenW(ptr);
4446 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4448 if (*equal == '=')
4450 static const WCHAR max_age[] = {
4451 'm','a','x','-','a','g','e',0 };
4453 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4455 LPWSTR nextPtr;
4456 unsigned long age;
4458 age = strtoulW(equal + 1, &nextPtr, 10);
4459 if (nextPtr > equal + 1)
4461 LARGE_INTEGER ft;
4463 NtQuerySystemTime( &ft );
4464 /* Age is in seconds, FILETIME resolution is in
4465 * 100 nanosecond intervals.
4467 ft.QuadPart += age * (ULONGLONG)1000000;
4468 request->expires.dwLowDateTime = ft.u.LowPart;
4469 request->expires.dwHighDateTime = ft.u.HighPart;
4470 expirationFound = TRUE;
4474 if (comma)
4476 ptr = comma + 1;
4477 while (isspaceW(*ptr))
4478 ptr++;
4480 else
4481 ptr = NULL;
4484 if (!expirationFound)
4486 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4487 if (headerIndex != -1)
4489 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4490 FILETIME ft;
4492 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4494 expirationFound = TRUE;
4495 request->expires = ft;
4499 if (!expirationFound)
4501 LARGE_INTEGER t;
4503 /* With no known age, default to 10 minutes until expiration. */
4504 NtQuerySystemTime( &t );
4505 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4506 request->expires.dwLowDateTime = t.u.LowPart;
4507 request->expires.dwHighDateTime = t.u.HighPart;
4511 static void HTTP_ProcessLastModified(http_request_t *request)
4513 int headerIndex;
4515 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4516 if (headerIndex != -1)
4518 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4519 FILETIME ft;
4521 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4522 request->last_modified = ft;
4526 static void http_process_keep_alive(http_request_t *req)
4528 int index;
4530 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4531 if(index != -1)
4532 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4533 else
4534 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4537 static void HTTP_CacheRequest(http_request_t *request)
4539 WCHAR url[INTERNET_MAX_URL_LENGTH];
4540 WCHAR cacheFileName[MAX_PATH+1];
4541 BOOL b;
4543 b = HTTP_GetRequestURL(request, url);
4544 if(!b) {
4545 WARN("Could not get URL\n");
4546 return;
4549 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4550 if(b) {
4551 heap_free(request->cacheFile);
4552 CloseHandle(request->hCacheFile);
4554 request->cacheFile = heap_strdupW(cacheFileName);
4555 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4556 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4557 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4558 WARN("Could not create file: %u\n", GetLastError());
4559 request->hCacheFile = NULL;
4561 }else {
4562 WARN("Could not create cache entry: %08x\n", GetLastError());
4566 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4568 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4569 http_session_t *session = request->session;
4570 netconn_t *netconn = NULL;
4571 server_t *server;
4572 DWORD res;
4574 assert(!request->netconn);
4575 reset_data_stream(request);
4577 server = get_server(session->serverName, session->serverPort);
4578 if(!server)
4579 return ERROR_OUTOFMEMORY;
4581 res = HTTP_ResolveName(request, server);
4582 if(res != ERROR_SUCCESS) {
4583 server_release(server);
4584 return res;
4587 EnterCriticalSection(&connection_pool_cs);
4589 while(!list_empty(&server->conn_pool)) {
4590 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4591 list_remove(&netconn->pool_entry);
4593 if(NETCON_is_alive(netconn))
4594 break;
4596 TRACE("connection %p closed during idle\n", netconn);
4597 free_netconn(netconn);
4598 netconn = NULL;
4601 LeaveCriticalSection(&connection_pool_cs);
4603 if(netconn) {
4604 TRACE("<-- reusing %p netconn\n", netconn);
4605 request->netconn = netconn;
4606 *reusing = TRUE;
4607 return ERROR_SUCCESS;
4610 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4611 INTERNET_STATUS_CONNECTING_TO_SERVER,
4612 server->addr_str,
4613 strlen(server->addr_str)+1);
4615 res = create_netconn(is_https, server, request->security_flags, request->connect_timeout, &netconn);
4616 server_release(server);
4617 if(res != ERROR_SUCCESS) {
4618 ERR("create_netconn failed: %u\n", res);
4619 return res;
4622 request->netconn = netconn;
4624 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4625 INTERNET_STATUS_CONNECTED_TO_SERVER,
4626 server->addr_str, strlen(server->addr_str)+1);
4628 if(is_https) {
4629 /* Note: we differ from Microsoft's WinINet here. they seem to have
4630 * a bug that causes no status callbacks to be sent when starting
4631 * a tunnel to a proxy server using the CONNECT verb. i believe our
4632 * behaviour to be more correct and to not cause any incompatibilities
4633 * because using a secure connection through a proxy server is a rare
4634 * case that would be hard for anyone to depend on */
4635 if(session->appInfo->proxy)
4636 res = HTTP_SecureProxyConnect(request);
4637 if(res == ERROR_SUCCESS)
4638 res = NETCON_secure_connect(request->netconn, session->hostName);
4639 if(res != ERROR_SUCCESS)
4641 WARN("Couldn't connect securely to host\n");
4643 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4644 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4645 || res == ERROR_INTERNET_INVALID_CA
4646 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4647 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4648 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4649 || res == ERROR_INTERNET_SEC_INVALID_CERT
4650 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4651 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4655 if(res != ERROR_SUCCESS) {
4656 http_release_netconn(request, FALSE);
4657 return res;
4660 *reusing = FALSE;
4661 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4662 return ERROR_SUCCESS;
4665 /***********************************************************************
4666 * HTTP_HttpSendRequestW (internal)
4668 * Sends the specified request to the HTTP server
4670 * RETURNS
4671 * ERROR_SUCCESS on success
4672 * win32 error code on failure
4675 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4676 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4677 DWORD dwContentLength, BOOL bEndRequest)
4679 INT cnt;
4680 BOOL redirected = FALSE;
4681 LPWSTR requestString = NULL;
4682 INT responseLen;
4683 BOOL loop_next;
4684 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4685 static const WCHAR szContentLength[] =
4686 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4687 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4688 DWORD res;
4690 TRACE("--> %p\n", request);
4692 assert(request->hdr.htype == WH_HHTTPREQ);
4694 /* if the verb is NULL default to GET */
4695 if (!request->verb)
4696 request->verb = heap_strdupW(szGET);
4698 if (dwContentLength || strcmpW(request->verb, szGET))
4700 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4701 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4702 request->bytesToWrite = dwContentLength;
4704 if (request->session->appInfo->agent)
4706 WCHAR *agent_header;
4707 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4708 int len;
4710 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4711 agent_header = heap_alloc(len * sizeof(WCHAR));
4712 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4714 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4715 heap_free(agent_header);
4717 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4719 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4720 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4722 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4724 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4725 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4726 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4731 DWORD len;
4732 BOOL reusing_connection;
4733 char *ascii_req;
4735 loop_next = FALSE;
4737 /* like native, just in case the caller forgot to call InternetReadFile
4738 * for all the data */
4739 drain_content(request);
4740 if(redirected) {
4741 request->contentLength = ~0u;
4742 request->bytesToWrite = 0;
4745 if (TRACE_ON(wininet))
4747 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4748 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4751 HTTP_FixURL(request);
4752 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4754 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4756 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4757 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4759 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4760 HTTP_InsertCookies(request);
4762 /* add the headers the caller supplied */
4763 if( lpszHeaders && dwHeaderLength )
4765 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4766 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4769 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4771 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4772 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4773 heap_free(url);
4775 else
4776 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4779 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4781 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4782 break;
4784 /* send the request as ASCII, tack on the optional data */
4785 if (!lpOptional || redirected)
4786 dwOptionalLength = 0;
4787 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4788 NULL, 0, NULL, NULL );
4789 ascii_req = heap_alloc(len + dwOptionalLength);
4790 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4791 ascii_req, len, NULL, NULL );
4792 if( lpOptional )
4793 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4794 len = (len + dwOptionalLength - 1);
4795 ascii_req[len] = 0;
4796 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4798 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4799 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4801 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4802 heap_free( ascii_req );
4803 if(res != ERROR_SUCCESS) {
4804 TRACE("send failed: %u\n", res);
4805 if(!reusing_connection)
4806 break;
4807 http_release_netconn(request, FALSE);
4808 loop_next = TRUE;
4809 continue;
4812 request->bytesWritten = dwOptionalLength;
4814 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4815 INTERNET_STATUS_REQUEST_SENT,
4816 &len, sizeof(DWORD));
4818 if (bEndRequest)
4820 DWORD dwBufferSize;
4821 DWORD dwStatusCode;
4823 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4824 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4826 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4827 /* FIXME: We should know that connection is closed before sending
4828 * headers. Otherwise wrong callbacks are executed */
4829 if(!responseLen && reusing_connection) {
4830 TRACE("Connection closed by server, reconnecting\n");
4831 http_release_netconn(request, FALSE);
4832 loop_next = TRUE;
4833 continue;
4836 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4837 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4838 sizeof(DWORD));
4840 http_process_keep_alive(request);
4841 HTTP_ProcessCookies(request);
4842 HTTP_ProcessExpires(request);
4843 HTTP_ProcessLastModified(request);
4845 dwBufferSize = sizeof(dwStatusCode);
4846 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4847 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4848 dwStatusCode = 0;
4850 res = set_content_length(request, dwStatusCode);
4851 if(res != ERROR_SUCCESS)
4852 goto lend;
4853 if(!request->contentLength)
4854 http_release_netconn(request, TRUE);
4856 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4858 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4859 dwBufferSize=sizeof(szNewLocation);
4860 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4861 dwStatusCode == HTTP_STATUS_MOVED ||
4862 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4863 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4864 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4866 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4867 dwStatusCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4869 heap_free(request->verb);
4870 request->verb = heap_strdupW(szGET);
4872 drain_content(request);
4873 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4875 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4876 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4877 res = HTTP_HandleRedirect(request, new_url);
4878 if (res == ERROR_SUCCESS)
4880 heap_free(requestString);
4881 loop_next = TRUE;
4883 heap_free( new_url );
4885 redirected = TRUE;
4888 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4890 WCHAR szAuthValue[2048];
4891 dwBufferSize=2048;
4892 if (dwStatusCode == HTTP_STATUS_DENIED)
4894 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4895 DWORD dwIndex = 0;
4896 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4898 if (HTTP_DoAuthorization(request, szAuthValue,
4899 &request->authInfo,
4900 request->session->userName,
4901 request->session->password,
4902 Host->lpszValue))
4904 heap_free(requestString);
4905 loop_next = TRUE;
4906 break;
4910 if(!loop_next) {
4911 TRACE("Cleaning wrong authorization data\n");
4912 destroy_authinfo(request->authInfo);
4913 request->authInfo = NULL;
4916 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4918 DWORD dwIndex = 0;
4919 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4921 if (HTTP_DoAuthorization(request, szAuthValue,
4922 &request->proxyAuthInfo,
4923 request->session->appInfo->proxyUsername,
4924 request->session->appInfo->proxyPassword,
4925 NULL))
4927 loop_next = TRUE;
4928 break;
4932 if(!loop_next) {
4933 TRACE("Cleaning wrong proxy authorization data\n");
4934 destroy_authinfo(request->proxyAuthInfo);
4935 request->proxyAuthInfo = NULL;
4940 else
4941 res = ERROR_SUCCESS;
4943 while (loop_next);
4945 if(res == ERROR_SUCCESS)
4946 HTTP_CacheRequest(request);
4948 lend:
4949 heap_free(requestString);
4951 /* TODO: send notification for P3P header */
4953 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4955 if (res == ERROR_SUCCESS) {
4956 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4957 HTTP_ReceiveRequestData(request, TRUE);
4958 else
4959 send_request_complete(request,
4960 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4961 }else {
4962 send_request_complete(request, 0, res);
4966 TRACE("<--\n");
4967 return res;
4970 /***********************************************************************
4972 * Helper functions for the HttpSendRequest(Ex) functions
4975 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4977 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4978 http_request_t *request = (http_request_t*) workRequest->hdr;
4980 TRACE("%p\n", request);
4982 HTTP_HttpSendRequestW(request, req->lpszHeader,
4983 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4984 req->dwContentLength, req->bEndRequest);
4986 heap_free(req->lpszHeader);
4990 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4992 INT responseLen;
4993 DWORD dwCode, dwCodeLength;
4994 DWORD dwBufferSize;
4995 DWORD res = ERROR_SUCCESS;
4997 if(!request->netconn) {
4998 WARN("Not connected\n");
4999 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5000 return ERROR_INTERNET_OPERATION_CANCELLED;
5003 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5004 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5006 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5007 if (!responseLen)
5008 res = ERROR_HTTP_HEADER_NOT_FOUND;
5010 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5011 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5013 /* process cookies here. Is this right? */
5014 http_process_keep_alive(request);
5015 HTTP_ProcessCookies(request);
5016 HTTP_ProcessExpires(request);
5017 HTTP_ProcessLastModified(request);
5019 dwCodeLength = sizeof(dwCode);
5020 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
5021 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
5022 dwCode = 0;
5024 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
5025 if(!request->contentLength)
5026 http_release_netconn(request, TRUE);
5029 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5031 if (dwCode == HTTP_STATUS_REDIRECT ||
5032 dwCode == HTTP_STATUS_MOVED ||
5033 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
5034 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
5036 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5037 dwBufferSize=sizeof(szNewLocation);
5038 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
5040 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5041 dwCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
5043 heap_free(request->verb);
5044 request->verb = heap_strdupW(szGET);
5046 drain_content(request);
5047 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5049 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5050 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5051 res = HTTP_HandleRedirect(request, new_url);
5052 if (res == ERROR_SUCCESS)
5053 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5054 heap_free( new_url );
5060 if (res == ERROR_SUCCESS && request->contentLength)
5061 HTTP_ReceiveRequestData(request, TRUE);
5062 else
5063 send_request_complete(request, res == ERROR_SUCCESS, res);
5065 return res;
5068 /***********************************************************************
5069 * HttpEndRequestA (WININET.@)
5071 * Ends an HTTP request that was started by HttpSendRequestEx
5073 * RETURNS
5074 * TRUE if successful
5075 * FALSE on failure
5078 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5079 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5081 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5083 if (lpBuffersOut)
5085 SetLastError(ERROR_INVALID_PARAMETER);
5086 return FALSE;
5089 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5092 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5094 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5095 http_request_t *request = (http_request_t*)work->hdr;
5097 TRACE("%p\n", request);
5099 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5102 /***********************************************************************
5103 * HttpEndRequestW (WININET.@)
5105 * Ends an HTTP request that was started by HttpSendRequestEx
5107 * RETURNS
5108 * TRUE if successful
5109 * FALSE on failure
5112 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5113 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5115 http_request_t *request;
5116 DWORD res;
5118 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5120 if (lpBuffersOut)
5122 SetLastError(ERROR_INVALID_PARAMETER);
5123 return FALSE;
5126 request = (http_request_t*) get_handle_object( hRequest );
5128 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5130 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5131 if (request)
5132 WININET_Release( &request->hdr );
5133 return FALSE;
5135 request->hdr.dwFlags |= dwFlags;
5137 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5139 WORKREQUEST work;
5140 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5142 work.asyncproc = AsyncHttpEndRequestProc;
5143 work.hdr = WININET_AddRef( &request->hdr );
5145 work_endrequest = &work.u.HttpEndRequestW;
5146 work_endrequest->dwFlags = dwFlags;
5147 work_endrequest->dwContext = dwContext;
5149 INTERNET_AsyncCall(&work);
5150 res = ERROR_IO_PENDING;
5152 else
5153 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5155 WININET_Release( &request->hdr );
5156 TRACE("%u <--\n", res);
5157 if(res != ERROR_SUCCESS)
5158 SetLastError(res);
5159 return res == ERROR_SUCCESS;
5162 /***********************************************************************
5163 * HttpSendRequestExA (WININET.@)
5165 * Sends the specified request to the HTTP server and allows chunked
5166 * transfers.
5168 * RETURNS
5169 * Success: TRUE
5170 * Failure: FALSE, call GetLastError() for more information.
5172 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5173 LPINTERNET_BUFFERSA lpBuffersIn,
5174 LPINTERNET_BUFFERSA lpBuffersOut,
5175 DWORD dwFlags, DWORD_PTR dwContext)
5177 INTERNET_BUFFERSW BuffersInW;
5178 BOOL rc = FALSE;
5179 DWORD headerlen;
5180 LPWSTR header = NULL;
5182 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5183 lpBuffersOut, dwFlags, dwContext);
5185 if (lpBuffersIn)
5187 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5188 if (lpBuffersIn->lpcszHeader)
5190 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5191 lpBuffersIn->dwHeadersLength,0,0);
5192 header = heap_alloc(headerlen*sizeof(WCHAR));
5193 if (!(BuffersInW.lpcszHeader = header))
5195 SetLastError(ERROR_OUTOFMEMORY);
5196 return FALSE;
5198 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5199 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5200 header, headerlen);
5202 else
5203 BuffersInW.lpcszHeader = NULL;
5204 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5205 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5206 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5207 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5208 BuffersInW.Next = NULL;
5211 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5213 heap_free(header);
5214 return rc;
5217 /***********************************************************************
5218 * HttpSendRequestExW (WININET.@)
5220 * Sends the specified request to the HTTP server and allows chunked
5221 * transfers
5223 * RETURNS
5224 * Success: TRUE
5225 * Failure: FALSE, call GetLastError() for more information.
5227 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5228 LPINTERNET_BUFFERSW lpBuffersIn,
5229 LPINTERNET_BUFFERSW lpBuffersOut,
5230 DWORD dwFlags, DWORD_PTR dwContext)
5232 http_request_t *request;
5233 http_session_t *session;
5234 appinfo_t *hIC;
5235 DWORD res;
5237 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5238 lpBuffersOut, dwFlags, dwContext);
5240 request = (http_request_t*) get_handle_object( hRequest );
5242 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5244 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5245 goto lend;
5248 session = request->session;
5249 assert(session->hdr.htype == WH_HHTTPSESSION);
5250 hIC = session->appInfo;
5251 assert(hIC->hdr.htype == WH_HINIT);
5253 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5255 WORKREQUEST workRequest;
5256 struct WORKREQ_HTTPSENDREQUESTW *req;
5258 workRequest.asyncproc = AsyncHttpSendRequestProc;
5259 workRequest.hdr = WININET_AddRef( &request->hdr );
5260 req = &workRequest.u.HttpSendRequestW;
5261 if (lpBuffersIn)
5263 DWORD size = 0;
5265 if (lpBuffersIn->lpcszHeader)
5267 if (lpBuffersIn->dwHeadersLength == ~0u)
5268 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5269 else
5270 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5272 req->lpszHeader = heap_alloc(size);
5273 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5275 else req->lpszHeader = NULL;
5277 req->dwHeaderLength = size / sizeof(WCHAR);
5278 req->lpOptional = lpBuffersIn->lpvBuffer;
5279 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5280 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5282 else
5284 req->lpszHeader = NULL;
5285 req->dwHeaderLength = 0;
5286 req->lpOptional = NULL;
5287 req->dwOptionalLength = 0;
5288 req->dwContentLength = 0;
5291 req->bEndRequest = FALSE;
5293 INTERNET_AsyncCall(&workRequest);
5295 * This is from windows.
5297 res = ERROR_IO_PENDING;
5299 else
5301 if (lpBuffersIn)
5302 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5303 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5304 lpBuffersIn->dwBufferTotal, FALSE);
5305 else
5306 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5309 lend:
5310 if ( request )
5311 WININET_Release( &request->hdr );
5313 TRACE("<---\n");
5314 SetLastError(res);
5315 return res == ERROR_SUCCESS;
5318 /***********************************************************************
5319 * HttpSendRequestW (WININET.@)
5321 * Sends the specified request to the HTTP server
5323 * RETURNS
5324 * TRUE on success
5325 * FALSE on failure
5328 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5329 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5331 http_request_t *request;
5332 http_session_t *session = NULL;
5333 appinfo_t *hIC = NULL;
5334 DWORD res = ERROR_SUCCESS;
5336 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5337 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5339 request = (http_request_t*) get_handle_object( hHttpRequest );
5340 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5342 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5343 goto lend;
5346 session = request->session;
5347 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5349 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5350 goto lend;
5353 hIC = session->appInfo;
5354 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5356 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5357 goto lend;
5360 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5362 WORKREQUEST workRequest;
5363 struct WORKREQ_HTTPSENDREQUESTW *req;
5365 workRequest.asyncproc = AsyncHttpSendRequestProc;
5366 workRequest.hdr = WININET_AddRef( &request->hdr );
5367 req = &workRequest.u.HttpSendRequestW;
5368 if (lpszHeaders)
5370 DWORD size;
5372 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5373 else size = dwHeaderLength * sizeof(WCHAR);
5375 req->lpszHeader = heap_alloc(size);
5376 memcpy(req->lpszHeader, lpszHeaders, size);
5378 else
5379 req->lpszHeader = 0;
5380 req->dwHeaderLength = dwHeaderLength;
5381 req->lpOptional = lpOptional;
5382 req->dwOptionalLength = dwOptionalLength;
5383 req->dwContentLength = dwOptionalLength;
5384 req->bEndRequest = TRUE;
5386 INTERNET_AsyncCall(&workRequest);
5388 * This is from windows.
5390 res = ERROR_IO_PENDING;
5392 else
5394 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5395 dwHeaderLength, lpOptional, dwOptionalLength,
5396 dwOptionalLength, TRUE);
5398 lend:
5399 if( request )
5400 WININET_Release( &request->hdr );
5402 SetLastError(res);
5403 return res == ERROR_SUCCESS;
5406 /***********************************************************************
5407 * HttpSendRequestA (WININET.@)
5409 * Sends the specified request to the HTTP server
5411 * RETURNS
5412 * TRUE on success
5413 * FALSE on failure
5416 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5417 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5419 BOOL result;
5420 LPWSTR szHeaders=NULL;
5421 DWORD nLen=dwHeaderLength;
5422 if(lpszHeaders!=NULL)
5424 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5425 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5426 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5428 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5429 heap_free(szHeaders);
5430 return result;
5433 /***********************************************************************
5434 * HTTPSESSION_Destroy (internal)
5436 * Deallocate session handle
5439 static void HTTPSESSION_Destroy(object_header_t *hdr)
5441 http_session_t *session = (http_session_t*) hdr;
5443 TRACE("%p\n", session);
5445 WININET_Release(&session->appInfo->hdr);
5447 heap_free(session->hostName);
5448 heap_free(session->serverName);
5449 heap_free(session->password);
5450 heap_free(session->userName);
5453 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5455 http_session_t *ses = (http_session_t *)hdr;
5457 switch(option) {
5458 case INTERNET_OPTION_HANDLE_TYPE:
5459 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5461 if (*size < sizeof(ULONG))
5462 return ERROR_INSUFFICIENT_BUFFER;
5464 *size = sizeof(DWORD);
5465 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5466 return ERROR_SUCCESS;
5467 case INTERNET_OPTION_CONNECT_TIMEOUT:
5468 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5470 if (*size < sizeof(DWORD))
5471 return ERROR_INSUFFICIENT_BUFFER;
5473 *size = sizeof(DWORD);
5474 *(DWORD *)buffer = ses->connect_timeout;
5475 return ERROR_SUCCESS;
5478 return INET_QueryOption(hdr, option, buffer, size, unicode);
5481 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5483 http_session_t *ses = (http_session_t*)hdr;
5485 switch(option) {
5486 case INTERNET_OPTION_USERNAME:
5488 heap_free(ses->userName);
5489 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5490 return ERROR_SUCCESS;
5492 case INTERNET_OPTION_PASSWORD:
5494 heap_free(ses->password);
5495 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5496 return ERROR_SUCCESS;
5498 case INTERNET_OPTION_CONNECT_TIMEOUT:
5500 ses->connect_timeout = *(DWORD *)buffer;
5501 return ERROR_SUCCESS;
5503 default: break;
5506 return ERROR_INTERNET_INVALID_OPTION;
5509 static const object_vtbl_t HTTPSESSIONVtbl = {
5510 HTTPSESSION_Destroy,
5511 NULL,
5512 HTTPSESSION_QueryOption,
5513 HTTPSESSION_SetOption,
5514 NULL,
5515 NULL,
5516 NULL,
5517 NULL,
5518 NULL
5522 /***********************************************************************
5523 * HTTP_Connect (internal)
5525 * Create http session handle
5527 * RETURNS
5528 * HINTERNET a session handle on success
5529 * NULL on failure
5532 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5533 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5534 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5535 DWORD dwInternalFlags, HINTERNET *ret)
5537 http_session_t *session = NULL;
5539 TRACE("-->\n");
5541 if (!lpszServerName || !lpszServerName[0])
5542 return ERROR_INVALID_PARAMETER;
5544 assert( hIC->hdr.htype == WH_HINIT );
5546 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5547 if (!session)
5548 return ERROR_OUTOFMEMORY;
5551 * According to my tests. The name is not resolved until a request is sent
5554 session->hdr.htype = WH_HHTTPSESSION;
5555 session->hdr.dwFlags = dwFlags;
5556 session->hdr.dwContext = dwContext;
5557 session->hdr.dwInternalFlags |= dwInternalFlags;
5559 WININET_AddRef( &hIC->hdr );
5560 session->appInfo = hIC;
5561 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5563 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5564 if(hIC->proxyBypass)
5565 FIXME("Proxy bypass is ignored.\n");
5567 session->serverName = heap_strdupW(lpszServerName);
5568 session->hostName = heap_strdupW(lpszServerName);
5569 if (lpszUserName && lpszUserName[0])
5570 session->userName = heap_strdupW(lpszUserName);
5571 if (lpszPassword && lpszPassword[0])
5572 session->password = heap_strdupW(lpszPassword);
5573 session->serverPort = serverPort;
5574 session->hostPort = serverPort;
5575 session->connect_timeout = INFINITE;
5577 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5578 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5580 INTERNET_SendCallback(&hIC->hdr, dwContext,
5581 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5582 sizeof(HINTERNET));
5586 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5587 * windows
5590 TRACE("%p --> %p\n", hIC, session);
5592 *ret = session->hdr.hInternet;
5593 return ERROR_SUCCESS;
5596 /***********************************************************************
5597 * HTTP_clear_response_headers (internal)
5599 * clear out any old response headers
5601 static void HTTP_clear_response_headers( http_request_t *request )
5603 DWORD i;
5605 for( i=0; i<request->nCustHeaders; i++)
5607 if( !request->custHeaders[i].lpszField )
5608 continue;
5609 if( !request->custHeaders[i].lpszValue )
5610 continue;
5611 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5612 continue;
5613 HTTP_DeleteCustomHeader( request, i );
5614 i--;
5618 /***********************************************************************
5619 * HTTP_GetResponseHeaders (internal)
5621 * Read server response
5623 * RETURNS
5625 * TRUE on success
5626 * FALSE on error
5628 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5630 INT cbreaks = 0;
5631 WCHAR buffer[MAX_REPLY_LEN];
5632 DWORD buflen = MAX_REPLY_LEN;
5633 BOOL bSuccess = FALSE;
5634 INT rc = 0;
5635 char bufferA[MAX_REPLY_LEN];
5636 LPWSTR status_code = NULL, status_text = NULL;
5637 DWORD cchMaxRawHeaders = 1024;
5638 LPWSTR lpszRawHeaders = NULL;
5639 LPWSTR temp;
5640 DWORD cchRawHeaders = 0;
5641 BOOL codeHundred = FALSE;
5643 TRACE("-->\n");
5645 if(!request->netconn)
5646 goto lend;
5648 do {
5649 static const WCHAR szHundred[] = {'1','0','0',0};
5651 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5653 buflen = MAX_REPLY_LEN;
5654 if (!read_line(request, bufferA, &buflen))
5655 goto lend;
5657 /* clear old response headers (eg. from a redirect response) */
5658 if (clear) {
5659 HTTP_clear_response_headers( request );
5660 clear = FALSE;
5663 rc += buflen;
5664 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5665 /* check is this a status code line? */
5666 if (!strncmpW(buffer, g_szHttp1_0, 4))
5668 /* split the version from the status code */
5669 status_code = strchrW( buffer, ' ' );
5670 if( !status_code )
5671 goto lend;
5672 *status_code++=0;
5674 /* split the status code from the status text */
5675 status_text = strchrW( status_code, ' ' );
5676 if( !status_text )
5677 goto lend;
5678 *status_text++=0;
5680 TRACE("version [%s] status code [%s] status text [%s]\n",
5681 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5683 codeHundred = (!strcmpW(status_code, szHundred));
5685 else if (!codeHundred)
5687 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5689 heap_free(request->version);
5690 heap_free(request->statusText);
5692 request->version = heap_strdupW(g_szHttp1_0);
5693 request->statusText = heap_strdupW(szOK);
5695 heap_free(request->rawHeaders);
5696 request->rawHeaders = heap_strdupW(szDefaultHeader);
5698 bSuccess = TRUE;
5699 goto lend;
5701 } while (codeHundred);
5703 /* Add status code */
5704 HTTP_ProcessHeader(request, szStatus, status_code,
5705 HTTP_ADDHDR_FLAG_REPLACE);
5707 heap_free(request->version);
5708 heap_free(request->statusText);
5710 request->version = heap_strdupW(buffer);
5711 request->statusText = heap_strdupW(status_text);
5713 /* Restore the spaces */
5714 *(status_code-1) = ' ';
5715 *(status_text-1) = ' ';
5717 /* regenerate raw headers */
5718 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5719 if (!lpszRawHeaders) goto lend;
5721 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5722 cchMaxRawHeaders *= 2;
5723 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5724 if (temp == NULL) goto lend;
5725 lpszRawHeaders = temp;
5726 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5727 cchRawHeaders += (buflen-1);
5728 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5729 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5730 lpszRawHeaders[cchRawHeaders] = '\0';
5732 /* Parse each response line */
5735 buflen = MAX_REPLY_LEN;
5736 if (read_line(request, bufferA, &buflen))
5738 LPWSTR * pFieldAndValue;
5740 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5742 if (!bufferA[0]) break;
5743 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5745 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5746 if (pFieldAndValue)
5748 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5749 cchMaxRawHeaders *= 2;
5750 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5751 if (temp == NULL) goto lend;
5752 lpszRawHeaders = temp;
5753 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5754 cchRawHeaders += (buflen-1);
5755 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5756 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5757 lpszRawHeaders[cchRawHeaders] = '\0';
5759 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5760 HTTP_ADDREQ_FLAG_ADD );
5762 HTTP_FreeTokens(pFieldAndValue);
5765 else
5767 cbreaks++;
5768 if (cbreaks >= 2)
5769 break;
5771 }while(1);
5773 /* make sure the response header is terminated with an empty line. Some apps really
5774 truly care about that empty line being there for some reason. Just add it to the
5775 header. */
5776 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5778 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5779 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5780 if (temp == NULL) goto lend;
5781 lpszRawHeaders = temp;
5784 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5786 heap_free(request->rawHeaders);
5787 request->rawHeaders = lpszRawHeaders;
5788 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5789 bSuccess = TRUE;
5791 lend:
5793 TRACE("<--\n");
5794 if (bSuccess)
5795 return rc;
5796 else
5798 heap_free(lpszRawHeaders);
5799 return 0;
5803 /***********************************************************************
5804 * HTTP_InterpretHttpHeader (internal)
5806 * Parse server response
5808 * RETURNS
5810 * Pointer to array of field, value, NULL on success.
5811 * NULL on error.
5813 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5815 LPWSTR * pTokenPair;
5816 LPWSTR pszColon;
5817 INT len;
5819 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5821 pszColon = strchrW(buffer, ':');
5822 /* must have two tokens */
5823 if (!pszColon)
5825 HTTP_FreeTokens(pTokenPair);
5826 if (buffer[0])
5827 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5828 return NULL;
5831 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5832 if (!pTokenPair[0])
5834 HTTP_FreeTokens(pTokenPair);
5835 return NULL;
5837 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5838 pTokenPair[0][pszColon - buffer] = '\0';
5840 /* skip colon */
5841 pszColon++;
5842 len = strlenW(pszColon);
5843 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5844 if (!pTokenPair[1])
5846 HTTP_FreeTokens(pTokenPair);
5847 return NULL;
5849 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5851 strip_spaces(pTokenPair[0]);
5852 strip_spaces(pTokenPair[1]);
5854 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5855 return pTokenPair;
5858 /***********************************************************************
5859 * HTTP_ProcessHeader (internal)
5861 * Stuff header into header tables according to <dwModifier>
5865 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5867 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5869 LPHTTPHEADERW lphttpHdr = NULL;
5870 INT index = -1;
5871 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5872 DWORD res = ERROR_HTTP_INVALID_HEADER;
5874 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5876 /* REPLACE wins out over ADD */
5877 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5878 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5880 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5881 index = -1;
5882 else
5883 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5885 if (index >= 0)
5887 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5888 return ERROR_HTTP_INVALID_HEADER;
5889 lphttpHdr = &request->custHeaders[index];
5891 else if (value)
5893 HTTPHEADERW hdr;
5895 hdr.lpszField = (LPWSTR)field;
5896 hdr.lpszValue = (LPWSTR)value;
5897 hdr.wFlags = hdr.wCount = 0;
5899 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5900 hdr.wFlags |= HDR_ISREQUEST;
5902 return HTTP_InsertCustomHeader(request, &hdr);
5904 /* no value to delete */
5905 else return ERROR_SUCCESS;
5907 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5908 lphttpHdr->wFlags |= HDR_ISREQUEST;
5909 else
5910 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5912 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5914 HTTP_DeleteCustomHeader( request, index );
5916 if (value)
5918 HTTPHEADERW hdr;
5920 hdr.lpszField = (LPWSTR)field;
5921 hdr.lpszValue = (LPWSTR)value;
5922 hdr.wFlags = hdr.wCount = 0;
5924 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5925 hdr.wFlags |= HDR_ISREQUEST;
5927 return HTTP_InsertCustomHeader(request, &hdr);
5930 return ERROR_SUCCESS;
5932 else if (dwModifier & COALESCEFLAGS)
5934 LPWSTR lpsztmp;
5935 WCHAR ch = 0;
5936 INT len = 0;
5937 INT origlen = strlenW(lphttpHdr->lpszValue);
5938 INT valuelen = strlenW(value);
5940 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5942 ch = ',';
5943 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5945 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5947 ch = ';';
5948 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5951 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5953 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5954 if (lpsztmp)
5956 lphttpHdr->lpszValue = lpsztmp;
5957 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5958 if (ch > 0)
5960 lphttpHdr->lpszValue[origlen] = ch;
5961 origlen++;
5962 lphttpHdr->lpszValue[origlen] = ' ';
5963 origlen++;
5966 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5967 lphttpHdr->lpszValue[len] = '\0';
5968 res = ERROR_SUCCESS;
5970 else
5972 WARN("heap_realloc (%d bytes) failed\n",len+1);
5973 res = ERROR_OUTOFMEMORY;
5976 TRACE("<-- %d\n", res);
5977 return res;
5980 /***********************************************************************
5981 * HTTP_GetCustomHeaderIndex (internal)
5983 * Return index of custom header from header array
5986 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5987 int requested_index, BOOL request_only)
5989 DWORD index;
5991 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5993 for (index = 0; index < request->nCustHeaders; index++)
5995 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5996 continue;
5998 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5999 continue;
6001 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6002 continue;
6004 if (requested_index == 0)
6005 break;
6006 requested_index --;
6009 if (index >= request->nCustHeaders)
6010 index = -1;
6012 TRACE("Return: %d\n", index);
6013 return index;
6017 /***********************************************************************
6018 * HTTP_InsertCustomHeader (internal)
6020 * Insert header into array
6023 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6025 INT count;
6026 LPHTTPHEADERW lph = NULL;
6028 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6029 count = request->nCustHeaders + 1;
6030 if (count > 1)
6031 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6032 else
6033 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6035 if (!lph)
6036 return ERROR_OUTOFMEMORY;
6038 request->custHeaders = lph;
6039 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6040 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6041 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6042 request->custHeaders[count-1].wCount= lpHdr->wCount;
6043 request->nCustHeaders++;
6045 return ERROR_SUCCESS;
6049 /***********************************************************************
6050 * HTTP_DeleteCustomHeader (internal)
6052 * Delete header from array
6053 * If this function is called, the indexs may change.
6055 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6057 if( request->nCustHeaders <= 0 )
6058 return FALSE;
6059 if( index >= request->nCustHeaders )
6060 return FALSE;
6061 request->nCustHeaders--;
6063 heap_free(request->custHeaders[index].lpszField);
6064 heap_free(request->custHeaders[index].lpszValue);
6066 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6067 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6068 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6070 return TRUE;
6074 /***********************************************************************
6075 * HTTP_VerifyValidHeader (internal)
6077 * Verify the given header is not invalid for the given http request
6080 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6082 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6083 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6084 return ERROR_HTTP_INVALID_HEADER;
6086 return ERROR_SUCCESS;
6089 /***********************************************************************
6090 * IsHostInProxyBypassList (@)
6092 * Undocumented
6095 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6097 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6098 return FALSE;
6101 /***********************************************************************
6102 * InternetShowSecurityInfoByURLA (@)
6104 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6106 FIXME("stub: %s %p\n", url, window);
6107 return FALSE;
6110 /***********************************************************************
6111 * InternetShowSecurityInfoByURLW (@)
6113 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6115 FIXME("stub: %s %p\n", debugstr_w(url), window);
6116 return FALSE;
6119 /***********************************************************************
6120 * ShowX509EncodedCertificate (@)
6122 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6124 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6125 cert, len);
6126 DWORD ret;
6128 if (certContext)
6130 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6132 memset(&view, 0, sizeof(view));
6133 view.hwndParent = parent;
6134 view.pCertContext = certContext;
6135 if (CryptUIDlgViewCertificateW(&view, NULL))
6136 ret = ERROR_SUCCESS;
6137 else
6138 ret = GetLastError();
6139 CertFreeCertificateContext(certContext);
6141 else
6142 ret = GetLastError();
6143 return ret;