wininet: Fix handle leak.
[wine.git] / dlls / wininet / http.c
blob8fc01fe42218f2a274a9f9b7ec2de52076d93e4e
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;
2156 return INET_QueryOption(hdr, option, buffer, size, unicode);
2159 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2161 http_request_t *req = (http_request_t*)hdr;
2163 switch(option) {
2164 case INTERNET_OPTION_SECURITY_FLAGS:
2166 DWORD flags;
2168 if (!buffer || size != sizeof(DWORD))
2169 return ERROR_INVALID_PARAMETER;
2170 flags = *(DWORD *)buffer;
2171 TRACE("%08x\n", flags);
2172 req->security_flags = flags;
2173 if(req->netconn)
2174 req->netconn->security_flags = flags;
2175 return ERROR_SUCCESS;
2177 case INTERNET_OPTION_SEND_TIMEOUT:
2178 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2179 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2181 if (size != sizeof(DWORD))
2182 return ERROR_INVALID_PARAMETER;
2184 if(!req->netconn) {
2185 FIXME("unsupported without active connection\n");
2186 return ERROR_SUCCESS;
2189 return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
2190 *(DWORD*)buffer);
2192 case INTERNET_OPTION_USERNAME:
2193 heap_free(req->session->userName);
2194 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2195 return ERROR_SUCCESS;
2197 case INTERNET_OPTION_PASSWORD:
2198 heap_free(req->session->password);
2199 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2200 return ERROR_SUCCESS;
2201 case INTERNET_OPTION_HTTP_DECODING:
2202 if(size != sizeof(BOOL))
2203 return ERROR_INVALID_PARAMETER;
2204 req->decoding = *(BOOL*)buffer;
2205 return ERROR_SUCCESS;
2208 return ERROR_INTERNET_INVALID_OPTION;
2211 /* read some more data into the read buffer (the read section must be held) */
2212 static DWORD read_more_data( http_request_t *req, int maxlen )
2214 DWORD res;
2215 int len;
2217 if (req->read_pos)
2219 /* move existing data to the start of the buffer */
2220 if(req->read_size)
2221 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2222 req->read_pos = 0;
2225 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2227 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2228 maxlen - req->read_size, 0, &len );
2229 if(res == ERROR_SUCCESS)
2230 req->read_size += len;
2232 return res;
2235 /* remove some amount of data from the read buffer (the read section must be held) */
2236 static void remove_data( http_request_t *req, int count )
2238 if (!(req->read_size -= count)) req->read_pos = 0;
2239 else req->read_pos += count;
2242 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2244 int count, bytes_read, pos = 0;
2245 DWORD res;
2247 EnterCriticalSection( &req->read_section );
2248 for (;;)
2250 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2252 if (eol)
2254 count = eol - (req->read_buf + req->read_pos);
2255 bytes_read = count + 1;
2257 else count = bytes_read = req->read_size;
2259 count = min( count, *len - pos );
2260 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2261 pos += count;
2262 remove_data( req, bytes_read );
2263 if (eol) break;
2265 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2267 *len = 0;
2268 TRACE( "returning empty string %u\n", res);
2269 LeaveCriticalSection( &req->read_section );
2270 INTERNET_SetLastError(res);
2271 return FALSE;
2274 LeaveCriticalSection( &req->read_section );
2276 if (pos < *len)
2278 if (pos && buffer[pos - 1] == '\r') pos--;
2279 *len = pos + 1;
2281 buffer[*len - 1] = 0;
2282 TRACE( "returning %s\n", debugstr_a(buffer));
2283 return TRUE;
2286 /* check if we have reached the end of the data to read (the read section must be held) */
2287 static BOOL end_of_read_data( http_request_t *req )
2289 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2292 /* fetch some more data into the read buffer (the read section must be held) */
2293 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2295 DWORD res, read=0;
2297 if(req->read_size == sizeof(req->read_buf))
2298 return ERROR_SUCCESS;
2300 if(req->read_pos) {
2301 if(req->read_size)
2302 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2303 req->read_pos = 0;
2306 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2307 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2308 req->read_size += read;
2310 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2311 if(read_bytes)
2312 *read_bytes = read;
2313 return res;
2316 /* return the size of data available to be read immediately (the read section must be held) */
2317 static DWORD get_avail_data( http_request_t *req )
2319 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2322 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2324 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2325 DWORD avail = 0;
2327 if(req->netconn)
2328 NETCON_query_data_available(req->netconn, &avail);
2329 return netconn_stream->content_length == ~0u
2330 ? avail
2331 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2334 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2336 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2337 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2340 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2341 DWORD *read, read_mode_t read_mode)
2343 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2344 int len = 0;
2346 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2348 if(read_mode == READMODE_NOBLOCK)
2349 size = min(size, netconn_get_avail_data(stream, req));
2351 if(size && req->netconn) {
2352 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2353 len = 0;
2354 if(!len)
2355 netconn_stream->content_length = netconn_stream->content_read;
2358 netconn_stream->content_read += *read = len;
2359 TRACE("read %u bytes\n", len);
2360 return ERROR_SUCCESS;
2363 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2365 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2366 BYTE buf[1024];
2367 DWORD avail;
2368 int len;
2370 if(netconn_end_of_data(stream, req))
2371 return TRUE;
2373 do {
2374 avail = netconn_get_avail_data(stream, req);
2375 if(!avail)
2376 return FALSE;
2378 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2379 return FALSE;
2381 netconn_stream->content_read += len;
2382 }while(netconn_stream->content_read < netconn_stream->content_length);
2384 return TRUE;
2387 static void netconn_destroy(data_stream_t *stream)
2391 static const data_stream_vtbl_t netconn_stream_vtbl = {
2392 netconn_get_avail_data,
2393 netconn_end_of_data,
2394 netconn_read,
2395 netconn_drain_content,
2396 netconn_destroy
2399 /* read some more data into the read buffer (the read section must be held) */
2400 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2402 DWORD res;
2403 int len;
2405 if (stream->buf_pos)
2407 /* move existing data to the start of the buffer */
2408 if(stream->buf_size)
2409 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2410 stream->buf_pos = 0;
2413 if (maxlen == -1) maxlen = sizeof(stream->buf);
2415 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2416 maxlen - stream->buf_size, 0, &len );
2417 if(res == ERROR_SUCCESS)
2418 stream->buf_size += len;
2420 return res;
2423 /* remove some amount of data from the read buffer (the read section must be held) */
2424 static void remove_chunked_data(chunked_stream_t *stream, int count)
2426 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2427 else stream->buf_pos += count;
2430 /* discard data contents until we reach end of line (the read section must be held) */
2431 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2433 DWORD res;
2437 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2438 if (eol)
2440 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2441 break;
2443 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2444 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2445 } while (stream->buf_size);
2446 return ERROR_SUCCESS;
2449 /* read the size of the next chunk (the read section must be held) */
2450 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2452 /* TODOO */
2453 DWORD chunk_size = 0, res;
2455 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2456 return res;
2458 for (;;)
2460 while (stream->buf_size)
2462 char ch = stream->buf[stream->buf_pos];
2463 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2464 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2465 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2466 else if (ch == ';' || ch == '\r' || ch == '\n')
2468 TRACE( "reading %u byte chunk\n", chunk_size );
2469 stream->chunk_size = chunk_size;
2470 req->contentLength += chunk_size;
2471 return discard_chunked_eol(stream, req);
2473 remove_chunked_data(stream, 1);
2475 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2476 if (!stream->buf_size)
2478 stream->chunk_size = 0;
2479 return ERROR_SUCCESS;
2484 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2486 /* Allow reading only from read buffer */
2487 return 0;
2490 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2492 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2493 return !chunked_stream->chunk_size;
2496 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2497 DWORD *read, read_mode_t read_mode)
2499 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2500 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2502 if(chunked_stream->chunk_size == ~0u) {
2503 res = start_next_chunk(chunked_stream, req);
2504 if(res != ERROR_SUCCESS)
2505 return res;
2508 while(size && chunked_stream->chunk_size) {
2509 if(chunked_stream->buf_size) {
2510 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2512 /* this could block */
2513 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2514 break;
2516 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2517 remove_chunked_data(chunked_stream, read_bytes);
2518 }else {
2519 read_bytes = min(size, chunked_stream->chunk_size);
2521 if(read_mode == READMODE_NOBLOCK) {
2522 DWORD avail;
2524 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2525 break;
2526 if(read_bytes > avail)
2527 read_bytes = avail;
2529 /* this could block */
2530 if(read_bytes == chunked_stream->chunk_size)
2531 break;
2534 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2535 if(res != ERROR_SUCCESS)
2536 break;
2539 chunked_stream->chunk_size -= read_bytes;
2540 size -= read_bytes;
2541 ret_read += read_bytes;
2542 if(!chunked_stream->chunk_size) {
2543 assert(read_mode != READMODE_NOBLOCK);
2544 res = start_next_chunk(chunked_stream, req);
2545 if(res != ERROR_SUCCESS)
2546 break;
2549 if(read_mode == READMODE_ASYNC)
2550 read_mode = READMODE_NOBLOCK;
2553 TRACE("read %u bytes\n", ret_read);
2554 *read = ret_read;
2555 return res;
2558 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2560 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2562 /* FIXME: we can do better */
2563 return !chunked_stream->chunk_size;
2566 static void chunked_destroy(data_stream_t *stream)
2568 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2569 heap_free(chunked_stream);
2572 static const data_stream_vtbl_t chunked_stream_vtbl = {
2573 chunked_get_avail_data,
2574 chunked_end_of_data,
2575 chunked_read,
2576 chunked_drain_content,
2577 chunked_destroy
2580 /* set the request content length based on the headers */
2581 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2583 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2584 WCHAR encoding[20];
2585 DWORD size;
2587 if(status_code == HTTP_STATUS_NO_CONTENT) {
2588 request->contentLength = request->netconn_stream.content_length = 0;
2589 return ERROR_SUCCESS;
2592 size = sizeof(request->contentLength);
2593 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2594 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2595 request->contentLength = ~0u;
2596 request->netconn_stream.content_length = request->contentLength;
2597 request->netconn_stream.content_read = request->read_size;
2599 size = sizeof(encoding);
2600 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2601 !strcmpiW(encoding, szChunked))
2603 chunked_stream_t *chunked_stream;
2605 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2606 if(!chunked_stream)
2607 return ERROR_OUTOFMEMORY;
2609 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2610 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2611 chunked_stream->chunk_size = ~0u;
2613 if(request->read_size) {
2614 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2615 chunked_stream->buf_size = request->read_size;
2616 request->read_size = request->read_pos = 0;
2619 request->data_stream = &chunked_stream->data_stream;
2620 request->contentLength = ~0u;
2621 request->read_chunked = TRUE;
2624 if(request->decoding) {
2625 int encoding_idx;
2627 static const WCHAR gzipW[] = {'g','z','i','p',0};
2629 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2630 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2631 return init_gzip_stream(request);
2634 return ERROR_SUCCESS;
2637 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2639 INTERNET_ASYNC_RESULT iar;
2641 iar.dwResult = result;
2642 iar.dwError = error;
2644 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2645 sizeof(INTERNET_ASYNC_RESULT));
2648 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2650 DWORD res, read = 0, avail = 0;
2651 read_mode_t mode;
2653 TRACE("%p\n", req);
2655 EnterCriticalSection( &req->read_section );
2657 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2658 res = refill_read_buffer(req, mode, &read);
2659 if(res == ERROR_SUCCESS && !first_notif)
2660 avail = get_avail_data(req);
2662 LeaveCriticalSection( &req->read_section );
2664 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2665 WARN("res %u read %u, closing connection\n", res, read);
2666 http_release_netconn(req, FALSE);
2669 if(res == ERROR_SUCCESS)
2670 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2671 else
2672 send_request_complete(req, 0, res);
2675 /* read data from the http connection (the read section must be held) */
2676 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2678 DWORD current_read = 0, ret_read = 0;
2679 read_mode_t read_mode;
2680 DWORD res = ERROR_SUCCESS;
2682 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2684 EnterCriticalSection( &req->read_section );
2686 if(req->read_size) {
2687 ret_read = min(size, req->read_size);
2688 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2689 req->read_size -= ret_read;
2690 req->read_pos += ret_read;
2691 if(read_mode == READMODE_ASYNC)
2692 read_mode = READMODE_NOBLOCK;
2695 if(ret_read < size) {
2696 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2697 ret_read += current_read;
2700 LeaveCriticalSection( &req->read_section );
2702 *read = ret_read;
2703 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2705 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2706 BOOL res;
2707 DWORD written;
2709 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2710 if(!res)
2711 WARN("WriteFile failed: %u\n", GetLastError());
2714 if(size && !ret_read)
2715 http_release_netconn(req, res == ERROR_SUCCESS);
2717 return res;
2721 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2723 http_request_t *req = (http_request_t*)hdr;
2724 DWORD res;
2726 EnterCriticalSection( &req->read_section );
2727 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2728 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2730 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2731 if(res == ERROR_SUCCESS)
2732 res = hdr->dwError;
2733 LeaveCriticalSection( &req->read_section );
2735 return res;
2738 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2740 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2741 http_request_t *req = (http_request_t*)workRequest->hdr;
2742 DWORD res;
2744 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2746 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2747 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2749 send_request_complete(req, res == ERROR_SUCCESS, res);
2752 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2753 DWORD flags, DWORD_PTR context)
2755 http_request_t *req = (http_request_t*)hdr;
2756 DWORD res, size, read, error = ERROR_SUCCESS;
2758 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2759 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2761 if (buffers->dwStructSize != sizeof(*buffers))
2762 return ERROR_INVALID_PARAMETER;
2764 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2766 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2768 WORKREQUEST workRequest;
2770 if (TryEnterCriticalSection( &req->read_section ))
2772 if (get_avail_data(req))
2774 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2775 &buffers->dwBufferLength, FALSE);
2776 size = buffers->dwBufferLength;
2777 LeaveCriticalSection( &req->read_section );
2778 goto done;
2780 LeaveCriticalSection( &req->read_section );
2783 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2784 workRequest.hdr = WININET_AddRef(&req->hdr);
2785 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2787 INTERNET_AsyncCall(&workRequest);
2789 return ERROR_IO_PENDING;
2792 read = 0;
2793 size = buffers->dwBufferLength;
2795 EnterCriticalSection( &req->read_section );
2796 if(hdr->dwError == ERROR_SUCCESS)
2797 hdr->dwError = INTERNET_HANDLE_IN_USE;
2798 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2799 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2801 while(1) {
2802 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2803 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2804 if(res != ERROR_SUCCESS)
2805 break;
2807 read += buffers->dwBufferLength;
2808 if(read == size || end_of_read_data(req))
2809 break;
2811 LeaveCriticalSection( &req->read_section );
2813 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2814 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2815 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2816 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2818 EnterCriticalSection( &req->read_section );
2821 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2822 hdr->dwError = ERROR_SUCCESS;
2823 else
2824 error = hdr->dwError;
2826 LeaveCriticalSection( &req->read_section );
2827 size = buffers->dwBufferLength;
2828 buffers->dwBufferLength = read;
2830 done:
2831 if (res == ERROR_SUCCESS) {
2832 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2833 &size, sizeof(size));
2836 return res==ERROR_SUCCESS ? error : res;
2839 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2841 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2842 http_request_t *req = (http_request_t*)workRequest->hdr;
2843 DWORD res;
2845 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2847 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2848 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2850 send_request_complete(req, res == ERROR_SUCCESS, res);
2853 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2854 DWORD flags, DWORD_PTR context)
2857 http_request_t *req = (http_request_t*)hdr;
2858 DWORD res, size, read, error = ERROR_SUCCESS;
2860 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2861 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2863 if (buffers->dwStructSize != sizeof(*buffers))
2864 return ERROR_INVALID_PARAMETER;
2866 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2868 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2870 WORKREQUEST workRequest;
2872 if (TryEnterCriticalSection( &req->read_section ))
2874 if (get_avail_data(req))
2876 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2877 &buffers->dwBufferLength, FALSE);
2878 size = buffers->dwBufferLength;
2879 LeaveCriticalSection( &req->read_section );
2880 goto done;
2882 LeaveCriticalSection( &req->read_section );
2885 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2886 workRequest.hdr = WININET_AddRef(&req->hdr);
2887 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2889 INTERNET_AsyncCall(&workRequest);
2891 return ERROR_IO_PENDING;
2894 read = 0;
2895 size = buffers->dwBufferLength;
2897 EnterCriticalSection( &req->read_section );
2898 if(hdr->dwError == ERROR_SUCCESS)
2899 hdr->dwError = INTERNET_HANDLE_IN_USE;
2900 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2901 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2903 while(1) {
2904 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2905 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2906 if(res != ERROR_SUCCESS)
2907 break;
2909 read += buffers->dwBufferLength;
2910 if(read == size || end_of_read_data(req))
2911 break;
2913 LeaveCriticalSection( &req->read_section );
2915 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2916 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2917 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2918 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2920 EnterCriticalSection( &req->read_section );
2923 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2924 hdr->dwError = ERROR_SUCCESS;
2925 else
2926 error = hdr->dwError;
2928 LeaveCriticalSection( &req->read_section );
2929 size = buffers->dwBufferLength;
2930 buffers->dwBufferLength = read;
2932 done:
2933 if (res == ERROR_SUCCESS) {
2934 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2935 &size, sizeof(size));
2938 return res==ERROR_SUCCESS ? error : res;
2941 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2943 DWORD res;
2944 http_request_t *request = (http_request_t*)hdr;
2946 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2948 *written = 0;
2949 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2950 if (res == ERROR_SUCCESS)
2951 request->bytesWritten += *written;
2953 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2954 return res;
2957 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2959 http_request_t *req = (http_request_t*)workRequest->hdr;
2961 HTTP_ReceiveRequestData(req, FALSE);
2964 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2966 http_request_t *req = (http_request_t*)hdr;
2968 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2970 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2972 WORKREQUEST workRequest;
2974 /* never wait, if we can't enter the section we queue an async request right away */
2975 if (TryEnterCriticalSection( &req->read_section ))
2977 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2978 if ((*available = get_avail_data( req ))) goto done;
2979 if (end_of_read_data( req )) goto done;
2980 LeaveCriticalSection( &req->read_section );
2983 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2984 workRequest.hdr = WININET_AddRef( &req->hdr );
2986 INTERNET_AsyncCall(&workRequest);
2988 return ERROR_IO_PENDING;
2991 EnterCriticalSection( &req->read_section );
2993 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2995 refill_read_buffer( req, READMODE_ASYNC, NULL );
2996 *available = get_avail_data( req );
2999 done:
3000 LeaveCriticalSection( &req->read_section );
3002 TRACE( "returning %u\n", *available );
3003 return ERROR_SUCCESS;
3006 static const object_vtbl_t HTTPREQVtbl = {
3007 HTTPREQ_Destroy,
3008 HTTPREQ_CloseConnection,
3009 HTTPREQ_QueryOption,
3010 HTTPREQ_SetOption,
3011 HTTPREQ_ReadFile,
3012 HTTPREQ_ReadFileExA,
3013 HTTPREQ_ReadFileExW,
3014 HTTPREQ_WriteFile,
3015 HTTPREQ_QueryDataAvailable,
3016 NULL
3019 /***********************************************************************
3020 * HTTP_HttpOpenRequestW (internal)
3022 * Open a HTTP request handle
3024 * RETURNS
3025 * HINTERNET a HTTP request handle on success
3026 * NULL on failure
3029 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3030 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3031 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3032 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3034 appinfo_t *hIC = session->appInfo;
3035 http_request_t *request;
3036 DWORD len, res = ERROR_SUCCESS;
3038 TRACE("-->\n");
3040 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3041 if(!request)
3042 return ERROR_OUTOFMEMORY;
3044 request->hdr.htype = WH_HHTTPREQ;
3045 request->hdr.dwFlags = dwFlags;
3046 request->hdr.dwContext = dwContext;
3047 request->contentLength = ~0u;
3049 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3050 request->data_stream = &request->netconn_stream.data_stream;
3052 InitializeCriticalSection( &request->read_section );
3053 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3055 WININET_AddRef( &session->hdr );
3056 request->session = session;
3057 list_add_head( &session->hdr.children, &request->hdr.entry );
3059 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3060 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3061 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3062 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3064 if (lpszObjectName && *lpszObjectName) {
3065 HRESULT rc;
3067 len = 0;
3068 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3069 if (rc != E_POINTER)
3070 len = strlenW(lpszObjectName)+1;
3071 request->path = heap_alloc(len*sizeof(WCHAR));
3072 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3073 URL_ESCAPE_SPACES_ONLY);
3074 if (rc != S_OK)
3076 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3077 strcpyW(request->path,lpszObjectName);
3079 }else {
3080 static const WCHAR slashW[] = {'/',0};
3082 request->path = heap_strdupW(slashW);
3085 if (lpszReferrer && *lpszReferrer)
3086 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3088 if (lpszAcceptTypes)
3090 int i;
3091 for (i = 0; lpszAcceptTypes[i]; i++)
3093 if (!*lpszAcceptTypes[i]) continue;
3094 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3095 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3096 HTTP_ADDHDR_FLAG_REQ |
3097 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3101 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3102 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3104 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3105 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3106 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3108 WCHAR *host_name;
3110 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3112 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3113 if (!host_name) {
3114 res = ERROR_OUTOFMEMORY;
3115 goto lend;
3118 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3119 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3120 heap_free(host_name);
3122 else
3123 HTTP_ProcessHeader(request, hostW, session->hostName,
3124 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3126 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3127 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3128 INTERNET_DEFAULT_HTTPS_PORT :
3129 INTERNET_DEFAULT_HTTP_PORT);
3131 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3132 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3133 INTERNET_DEFAULT_HTTPS_PORT :
3134 INTERNET_DEFAULT_HTTP_PORT);
3136 if (hIC->proxy && hIC->proxy[0])
3137 HTTP_DealWithProxy( hIC, session, request );
3139 INTERNET_SendCallback(&session->hdr, dwContext,
3140 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3141 sizeof(HINTERNET));
3143 lend:
3144 TRACE("<-- %u (%p)\n", res, request);
3146 if(res != ERROR_SUCCESS) {
3147 WININET_Release( &request->hdr );
3148 *ret = NULL;
3149 return res;
3152 *ret = request->hdr.hInternet;
3153 return ERROR_SUCCESS;
3156 /***********************************************************************
3157 * HttpOpenRequestW (WININET.@)
3159 * Open a HTTP request handle
3161 * RETURNS
3162 * HINTERNET a HTTP request handle on success
3163 * NULL on failure
3166 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3167 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3168 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3169 DWORD dwFlags, DWORD_PTR dwContext)
3171 http_session_t *session;
3172 HINTERNET handle = NULL;
3173 DWORD res;
3175 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3176 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3177 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3178 dwFlags, dwContext);
3179 if(lpszAcceptTypes!=NULL)
3181 int i;
3182 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3183 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3186 session = (http_session_t*) get_handle_object( hHttpSession );
3187 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3189 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3190 goto lend;
3194 * My tests seem to show that the windows version does not
3195 * become asynchronous until after this point. And anyhow
3196 * if this call was asynchronous then how would you get the
3197 * necessary HINTERNET pointer returned by this function.
3200 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3201 lpszVersion, lpszReferrer, lpszAcceptTypes,
3202 dwFlags, dwContext, &handle);
3203 lend:
3204 if( session )
3205 WININET_Release( &session->hdr );
3206 TRACE("returning %p\n", handle);
3207 if(res != ERROR_SUCCESS)
3208 SetLastError(res);
3209 return handle;
3212 static const LPCWSTR header_lookup[] = {
3213 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3214 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3215 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3216 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3217 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3218 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3219 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3220 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3221 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3222 szDate, /* HTTP_QUERY_DATE = 9 */
3223 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3224 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3225 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3226 szURI, /* HTTP_QUERY_URI = 13 */
3227 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3228 NULL, /* HTTP_QUERY_COST = 15 */
3229 NULL, /* HTTP_QUERY_LINK = 16 */
3230 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3231 NULL, /* HTTP_QUERY_VERSION = 18 */
3232 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3233 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3234 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3235 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3236 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3237 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3238 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3239 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3240 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3241 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3242 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3243 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3244 NULL, /* HTTP_QUERY_FROM = 31 */
3245 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3246 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3247 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3248 szReferer, /* HTTP_QUERY_REFERER = 35 */
3249 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3250 szServer, /* HTTP_QUERY_SERVER = 37 */
3251 NULL, /* HTTP_TITLE = 38 */
3252 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3253 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3254 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3255 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3256 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3257 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3258 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3259 NULL, /* HTTP_QUERY_REFRESH = 46 */
3260 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3261 szAge, /* HTTP_QUERY_AGE = 48 */
3262 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3263 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3264 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3265 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3266 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3267 szETag, /* HTTP_QUERY_ETAG = 54 */
3268 hostW, /* HTTP_QUERY_HOST = 55 */
3269 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3270 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3271 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3272 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3273 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3274 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3275 szRange, /* HTTP_QUERY_RANGE = 62 */
3276 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3277 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3278 szVary, /* HTTP_QUERY_VARY = 65 */
3279 szVia, /* HTTP_QUERY_VIA = 66 */
3280 szWarning, /* HTTP_QUERY_WARNING = 67 */
3281 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3282 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3283 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3286 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3288 /***********************************************************************
3289 * HTTP_HttpQueryInfoW (internal)
3291 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3292 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3294 LPHTTPHEADERW lphttpHdr = NULL;
3295 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3296 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3297 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3298 INT index = -1;
3300 /* Find requested header structure */
3301 switch (level)
3303 case HTTP_QUERY_CUSTOM:
3304 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3305 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3306 break;
3307 case HTTP_QUERY_RAW_HEADERS_CRLF:
3309 LPWSTR headers;
3310 DWORD len = 0;
3311 DWORD res = ERROR_INVALID_PARAMETER;
3313 if (request_only)
3314 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3315 else
3316 headers = request->rawHeaders;
3318 if (headers)
3319 len = strlenW(headers) * sizeof(WCHAR);
3321 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3323 len += sizeof(WCHAR);
3324 res = ERROR_INSUFFICIENT_BUFFER;
3326 else if (lpBuffer)
3328 if (headers)
3329 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3330 else
3332 len = strlenW(szCrLf) * sizeof(WCHAR);
3333 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3335 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3336 res = ERROR_SUCCESS;
3338 *lpdwBufferLength = len;
3340 if (request_only) heap_free(headers);
3341 return res;
3343 case HTTP_QUERY_RAW_HEADERS:
3345 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3346 DWORD i, size = 0;
3347 LPWSTR pszString = lpBuffer;
3349 for (i = 0; ppszRawHeaderLines[i]; i++)
3350 size += strlenW(ppszRawHeaderLines[i]) + 1;
3352 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3354 HTTP_FreeTokens(ppszRawHeaderLines);
3355 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3356 return ERROR_INSUFFICIENT_BUFFER;
3358 if (pszString)
3360 for (i = 0; ppszRawHeaderLines[i]; i++)
3362 DWORD len = strlenW(ppszRawHeaderLines[i]);
3363 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3364 pszString += len+1;
3366 *pszString = '\0';
3367 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3369 *lpdwBufferLength = size * sizeof(WCHAR);
3370 HTTP_FreeTokens(ppszRawHeaderLines);
3372 return ERROR_SUCCESS;
3374 case HTTP_QUERY_STATUS_TEXT:
3375 if (request->statusText)
3377 DWORD len = strlenW(request->statusText);
3378 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3380 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3381 return ERROR_INSUFFICIENT_BUFFER;
3383 if (lpBuffer)
3385 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3386 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3388 *lpdwBufferLength = len * sizeof(WCHAR);
3389 return ERROR_SUCCESS;
3391 break;
3392 case HTTP_QUERY_VERSION:
3393 if (request->version)
3395 DWORD len = strlenW(request->version);
3396 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3398 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3399 return ERROR_INSUFFICIENT_BUFFER;
3401 if (lpBuffer)
3403 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3404 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3406 *lpdwBufferLength = len * sizeof(WCHAR);
3407 return ERROR_SUCCESS;
3409 break;
3410 case HTTP_QUERY_CONTENT_ENCODING:
3411 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3412 requested_index,request_only);
3413 break;
3414 default:
3415 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3417 if (level < LAST_TABLE_HEADER && header_lookup[level])
3418 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3419 requested_index,request_only);
3422 if (index >= 0)
3423 lphttpHdr = &request->custHeaders[index];
3425 /* Ensure header satisfies requested attributes */
3426 if (!lphttpHdr ||
3427 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3428 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3430 return ERROR_HTTP_HEADER_NOT_FOUND;
3433 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3435 /* coalesce value to requested type */
3436 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3438 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3439 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3441 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3443 time_t tmpTime;
3444 struct tm tmpTM;
3445 SYSTEMTIME *STHook;
3447 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3449 tmpTM = *gmtime(&tmpTime);
3450 STHook = (SYSTEMTIME *)lpBuffer;
3451 STHook->wDay = tmpTM.tm_mday;
3452 STHook->wHour = tmpTM.tm_hour;
3453 STHook->wMilliseconds = 0;
3454 STHook->wMinute = tmpTM.tm_min;
3455 STHook->wDayOfWeek = tmpTM.tm_wday;
3456 STHook->wMonth = tmpTM.tm_mon + 1;
3457 STHook->wSecond = tmpTM.tm_sec;
3458 STHook->wYear = tmpTM.tm_year;
3460 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3461 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3462 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3464 else if (lphttpHdr->lpszValue)
3466 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3468 if (len > *lpdwBufferLength)
3470 *lpdwBufferLength = len;
3471 return ERROR_INSUFFICIENT_BUFFER;
3473 if (lpBuffer)
3475 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3476 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3478 *lpdwBufferLength = len - sizeof(WCHAR);
3480 return ERROR_SUCCESS;
3483 /***********************************************************************
3484 * HttpQueryInfoW (WININET.@)
3486 * Queries for information about an HTTP request
3488 * RETURNS
3489 * TRUE on success
3490 * FALSE on failure
3493 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3494 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3496 http_request_t *request;
3497 DWORD res;
3499 if (TRACE_ON(wininet)) {
3500 #define FE(x) { x, #x }
3501 static const wininet_flag_info query_flags[] = {
3502 FE(HTTP_QUERY_MIME_VERSION),
3503 FE(HTTP_QUERY_CONTENT_TYPE),
3504 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3505 FE(HTTP_QUERY_CONTENT_ID),
3506 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3507 FE(HTTP_QUERY_CONTENT_LENGTH),
3508 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3509 FE(HTTP_QUERY_ALLOW),
3510 FE(HTTP_QUERY_PUBLIC),
3511 FE(HTTP_QUERY_DATE),
3512 FE(HTTP_QUERY_EXPIRES),
3513 FE(HTTP_QUERY_LAST_MODIFIED),
3514 FE(HTTP_QUERY_MESSAGE_ID),
3515 FE(HTTP_QUERY_URI),
3516 FE(HTTP_QUERY_DERIVED_FROM),
3517 FE(HTTP_QUERY_COST),
3518 FE(HTTP_QUERY_LINK),
3519 FE(HTTP_QUERY_PRAGMA),
3520 FE(HTTP_QUERY_VERSION),
3521 FE(HTTP_QUERY_STATUS_CODE),
3522 FE(HTTP_QUERY_STATUS_TEXT),
3523 FE(HTTP_QUERY_RAW_HEADERS),
3524 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3525 FE(HTTP_QUERY_CONNECTION),
3526 FE(HTTP_QUERY_ACCEPT),
3527 FE(HTTP_QUERY_ACCEPT_CHARSET),
3528 FE(HTTP_QUERY_ACCEPT_ENCODING),
3529 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3530 FE(HTTP_QUERY_AUTHORIZATION),
3531 FE(HTTP_QUERY_CONTENT_ENCODING),
3532 FE(HTTP_QUERY_FORWARDED),
3533 FE(HTTP_QUERY_FROM),
3534 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3535 FE(HTTP_QUERY_LOCATION),
3536 FE(HTTP_QUERY_ORIG_URI),
3537 FE(HTTP_QUERY_REFERER),
3538 FE(HTTP_QUERY_RETRY_AFTER),
3539 FE(HTTP_QUERY_SERVER),
3540 FE(HTTP_QUERY_TITLE),
3541 FE(HTTP_QUERY_USER_AGENT),
3542 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3543 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3544 FE(HTTP_QUERY_ACCEPT_RANGES),
3545 FE(HTTP_QUERY_SET_COOKIE),
3546 FE(HTTP_QUERY_COOKIE),
3547 FE(HTTP_QUERY_REQUEST_METHOD),
3548 FE(HTTP_QUERY_REFRESH),
3549 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3550 FE(HTTP_QUERY_AGE),
3551 FE(HTTP_QUERY_CACHE_CONTROL),
3552 FE(HTTP_QUERY_CONTENT_BASE),
3553 FE(HTTP_QUERY_CONTENT_LOCATION),
3554 FE(HTTP_QUERY_CONTENT_MD5),
3555 FE(HTTP_QUERY_CONTENT_RANGE),
3556 FE(HTTP_QUERY_ETAG),
3557 FE(HTTP_QUERY_HOST),
3558 FE(HTTP_QUERY_IF_MATCH),
3559 FE(HTTP_QUERY_IF_NONE_MATCH),
3560 FE(HTTP_QUERY_IF_RANGE),
3561 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3562 FE(HTTP_QUERY_MAX_FORWARDS),
3563 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3564 FE(HTTP_QUERY_RANGE),
3565 FE(HTTP_QUERY_TRANSFER_ENCODING),
3566 FE(HTTP_QUERY_UPGRADE),
3567 FE(HTTP_QUERY_VARY),
3568 FE(HTTP_QUERY_VIA),
3569 FE(HTTP_QUERY_WARNING),
3570 FE(HTTP_QUERY_CUSTOM)
3572 static const wininet_flag_info modifier_flags[] = {
3573 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3574 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3575 FE(HTTP_QUERY_FLAG_NUMBER),
3576 FE(HTTP_QUERY_FLAG_COALESCE)
3578 #undef FE
3579 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3580 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3581 DWORD i;
3583 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3584 TRACE(" Attribute:");
3585 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3586 if (query_flags[i].val == info) {
3587 TRACE(" %s", query_flags[i].name);
3588 break;
3591 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3592 TRACE(" Unknown (%08x)", info);
3595 TRACE(" Modifier:");
3596 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3597 if (modifier_flags[i].val & info_mod) {
3598 TRACE(" %s", modifier_flags[i].name);
3599 info_mod &= ~ modifier_flags[i].val;
3603 if (info_mod) {
3604 TRACE(" Unknown (%08x)", info_mod);
3606 TRACE("\n");
3609 request = (http_request_t*) get_handle_object( hHttpRequest );
3610 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3612 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3613 goto lend;
3616 if (lpBuffer == NULL)
3617 *lpdwBufferLength = 0;
3618 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3619 lpBuffer, lpdwBufferLength, lpdwIndex);
3621 lend:
3622 if( request )
3623 WININET_Release( &request->hdr );
3625 TRACE("%u <--\n", res);
3626 if(res != ERROR_SUCCESS)
3627 SetLastError(res);
3628 return res == ERROR_SUCCESS;
3631 /***********************************************************************
3632 * HttpQueryInfoA (WININET.@)
3634 * Queries for information about an HTTP request
3636 * RETURNS
3637 * TRUE on success
3638 * FALSE on failure
3641 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3642 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3644 BOOL result;
3645 DWORD len;
3646 WCHAR* bufferW;
3648 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3649 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3651 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3652 lpdwBufferLength, lpdwIndex );
3655 if (lpBuffer)
3657 DWORD alloclen;
3658 len = (*lpdwBufferLength)*sizeof(WCHAR);
3659 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3661 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3662 if (alloclen < len)
3663 alloclen = len;
3665 else
3666 alloclen = len;
3667 bufferW = heap_alloc(alloclen);
3668 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3669 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3670 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3671 } else
3673 bufferW = NULL;
3674 len = 0;
3677 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3678 &len, lpdwIndex );
3679 if( result )
3681 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3682 lpBuffer, *lpdwBufferLength, NULL, NULL );
3683 *lpdwBufferLength = len - 1;
3685 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3687 else
3688 /* since the strings being returned from HttpQueryInfoW should be
3689 * only ASCII characters, it is reasonable to assume that all of
3690 * the Unicode characters can be reduced to a single byte */
3691 *lpdwBufferLength = len / sizeof(WCHAR);
3693 heap_free( bufferW );
3694 return result;
3697 /***********************************************************************
3698 * HTTP_GetRedirectURL (internal)
3700 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3702 static WCHAR szHttp[] = {'h','t','t','p',0};
3703 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3704 http_session_t *session = request->session;
3705 URL_COMPONENTSW urlComponents;
3706 DWORD url_length = 0;
3707 LPWSTR orig_url;
3708 LPWSTR combined_url;
3710 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3711 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3712 urlComponents.dwSchemeLength = 0;
3713 urlComponents.lpszHostName = session->hostName;
3714 urlComponents.dwHostNameLength = 0;
3715 urlComponents.nPort = session->hostPort;
3716 urlComponents.lpszUserName = session->userName;
3717 urlComponents.dwUserNameLength = 0;
3718 urlComponents.lpszPassword = NULL;
3719 urlComponents.dwPasswordLength = 0;
3720 urlComponents.lpszUrlPath = request->path;
3721 urlComponents.dwUrlPathLength = 0;
3722 urlComponents.lpszExtraInfo = NULL;
3723 urlComponents.dwExtraInfoLength = 0;
3725 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3726 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3727 return NULL;
3729 orig_url = heap_alloc(url_length);
3731 /* convert from bytes to characters */
3732 url_length = url_length / sizeof(WCHAR) - 1;
3733 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3735 heap_free(orig_url);
3736 return NULL;
3739 url_length = 0;
3740 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3741 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3743 heap_free(orig_url);
3744 return NULL;
3746 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3748 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3750 heap_free(orig_url);
3751 heap_free(combined_url);
3752 return NULL;
3754 heap_free(orig_url);
3755 return combined_url;
3759 /***********************************************************************
3760 * HTTP_HandleRedirect (internal)
3762 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3764 http_session_t *session = request->session;
3765 appinfo_t *hIC = session->appInfo;
3766 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3767 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3768 int index;
3770 if(lpszUrl[0]=='/')
3772 /* if it's an absolute path, keep the same session info */
3773 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3775 else
3777 URL_COMPONENTSW urlComponents;
3778 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3779 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3780 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3781 static WCHAR szHttp[] = {'h','t','t','p',0};
3782 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3784 userName[0] = 0;
3785 hostName[0] = 0;
3786 protocol[0] = 0;
3788 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3789 urlComponents.lpszScheme = protocol;
3790 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3791 urlComponents.lpszHostName = hostName;
3792 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3793 urlComponents.lpszUserName = userName;
3794 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3795 urlComponents.lpszPassword = NULL;
3796 urlComponents.dwPasswordLength = 0;
3797 urlComponents.lpszUrlPath = path;
3798 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3799 urlComponents.lpszExtraInfo = NULL;
3800 urlComponents.dwExtraInfoLength = 0;
3801 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3802 return INTERNET_GetLastError();
3804 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3805 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3807 TRACE("redirect from secure page to non-secure page\n");
3808 /* FIXME: warn about from secure redirect to non-secure page */
3809 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3811 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3812 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3814 TRACE("redirect from non-secure page to secure page\n");
3815 /* FIXME: notify about redirect to secure page */
3816 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3819 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3821 if (lstrlenW(protocol)>4) /*https*/
3822 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3823 else /*http*/
3824 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3827 #if 0
3829 * This upsets redirects to binary files on sourceforge.net
3830 * and gives an html page instead of the target file
3831 * Examination of the HTTP request sent by native wininet.dll
3832 * reveals that it doesn't send a referrer in that case.
3833 * Maybe there's a flag that enables this, or maybe a referrer
3834 * shouldn't be added in case of a redirect.
3837 /* consider the current host as the referrer */
3838 if (session->lpszServerName && *session->lpszServerName)
3839 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3840 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3841 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3842 #endif
3844 heap_free(session->hostName);
3845 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3846 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3848 int len;
3849 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3850 len = lstrlenW(hostName);
3851 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3852 session->hostName = heap_alloc(len*sizeof(WCHAR));
3853 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3855 else
3856 session->hostName = heap_strdupW(hostName);
3858 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3860 heap_free(session->userName);
3861 session->userName = NULL;
3862 if (userName[0])
3863 session->userName = heap_strdupW(userName);
3865 reset_data_stream(request);
3867 if(!using_proxy) {
3868 if(strcmpiW(session->serverName, hostName)) {
3869 heap_free(session->serverName);
3870 session->serverName = heap_strdupW(hostName);
3872 session->serverPort = urlComponents.nPort;
3875 heap_free(request->path);
3876 request->path=NULL;
3877 if (*path)
3879 DWORD needed = 0;
3880 HRESULT rc;
3882 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3883 if (rc != E_POINTER)
3884 needed = strlenW(path)+1;
3885 request->path = heap_alloc(needed*sizeof(WCHAR));
3886 rc = UrlEscapeW(path, request->path, &needed,
3887 URL_ESCAPE_SPACES_ONLY);
3888 if (rc != S_OK)
3890 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3891 strcpyW(request->path,path);
3895 /* Remove custom content-type/length headers on redirects. */
3896 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3897 if (0 <= index)
3898 HTTP_DeleteCustomHeader(request, index);
3899 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3900 if (0 <= index)
3901 HTTP_DeleteCustomHeader(request, index);
3903 return ERROR_SUCCESS;
3906 /***********************************************************************
3907 * HTTP_build_req (internal)
3909 * concatenate all the strings in the request together
3911 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3913 LPCWSTR *t;
3914 LPWSTR str;
3916 for( t = list; *t ; t++ )
3917 len += strlenW( *t );
3918 len++;
3920 str = heap_alloc(len*sizeof(WCHAR));
3921 *str = 0;
3923 for( t = list; *t ; t++ )
3924 strcatW( str, *t );
3926 return str;
3929 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3931 LPWSTR lpszPath;
3932 LPWSTR requestString;
3933 INT len;
3934 INT cnt;
3935 INT responseLen;
3936 char *ascii_req;
3937 DWORD res;
3938 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3939 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3940 http_session_t *session = request->session;
3942 TRACE("\n");
3944 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3945 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3946 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3947 heap_free( lpszPath );
3949 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3950 NULL, 0, NULL, NULL );
3951 len--; /* the nul terminator isn't needed */
3952 ascii_req = heap_alloc(len);
3953 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3954 heap_free( requestString );
3956 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3958 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3959 heap_free( ascii_req );
3960 if (res != ERROR_SUCCESS)
3961 return res;
3963 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3964 if (!responseLen)
3965 return ERROR_HTTP_INVALID_HEADER;
3967 return ERROR_SUCCESS;
3970 static void HTTP_InsertCookies(http_request_t *request)
3972 DWORD cookie_size, size, cnt = 0;
3973 HTTPHEADERW *host;
3974 WCHAR *cookies;
3976 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3978 host = HTTP_GetHeader(request, hostW);
3979 if(!host)
3980 return;
3982 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3983 return;
3985 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
3986 if(!(cookies = heap_alloc(size)))
3987 return;
3989 cnt += sprintfW(cookies, cookieW);
3990 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
3991 strcatW(cookies, szCrLf);
3993 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
3995 heap_free(cookies);
3998 static WORD HTTP_ParseWkday(LPCWSTR day)
4000 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4001 { 'm','o','n',0 },
4002 { 't','u','e',0 },
4003 { 'w','e','d',0 },
4004 { 't','h','u',0 },
4005 { 'f','r','i',0 },
4006 { 's','a','t',0 }};
4007 int i;
4008 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4009 if (!strcmpiW(day, days[i]))
4010 return i;
4012 /* Invalid */
4013 return 7;
4016 static WORD HTTP_ParseMonth(LPCWSTR month)
4018 static const WCHAR jan[] = { 'j','a','n',0 };
4019 static const WCHAR feb[] = { 'f','e','b',0 };
4020 static const WCHAR mar[] = { 'm','a','r',0 };
4021 static const WCHAR apr[] = { 'a','p','r',0 };
4022 static const WCHAR may[] = { 'm','a','y',0 };
4023 static const WCHAR jun[] = { 'j','u','n',0 };
4024 static const WCHAR jul[] = { 'j','u','l',0 };
4025 static const WCHAR aug[] = { 'a','u','g',0 };
4026 static const WCHAR sep[] = { 's','e','p',0 };
4027 static const WCHAR oct[] = { 'o','c','t',0 };
4028 static const WCHAR nov[] = { 'n','o','v',0 };
4029 static const WCHAR dec[] = { 'd','e','c',0 };
4031 if (!strcmpiW(month, jan)) return 1;
4032 if (!strcmpiW(month, feb)) return 2;
4033 if (!strcmpiW(month, mar)) return 3;
4034 if (!strcmpiW(month, apr)) return 4;
4035 if (!strcmpiW(month, may)) return 5;
4036 if (!strcmpiW(month, jun)) return 6;
4037 if (!strcmpiW(month, jul)) return 7;
4038 if (!strcmpiW(month, aug)) return 8;
4039 if (!strcmpiW(month, sep)) return 9;
4040 if (!strcmpiW(month, oct)) return 10;
4041 if (!strcmpiW(month, nov)) return 11;
4042 if (!strcmpiW(month, dec)) return 12;
4043 /* Invalid */
4044 return 0;
4047 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4048 * optionally preceded by whitespace.
4049 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4050 * st, and sets *str to the first character after the time format.
4052 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4054 LPCWSTR ptr = *str;
4055 WCHAR *nextPtr;
4056 unsigned long num;
4058 while (isspaceW(*ptr))
4059 ptr++;
4061 num = strtoulW(ptr, &nextPtr, 10);
4062 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4064 ERR("unexpected time format %s\n", debugstr_w(ptr));
4065 return FALSE;
4067 if (num > 23)
4069 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4070 return FALSE;
4072 ptr = nextPtr + 1;
4073 st->wHour = (WORD)num;
4074 num = strtoulW(ptr, &nextPtr, 10);
4075 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4077 ERR("unexpected time format %s\n", debugstr_w(ptr));
4078 return FALSE;
4080 if (num > 59)
4082 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4083 return FALSE;
4085 ptr = nextPtr + 1;
4086 st->wMinute = (WORD)num;
4087 num = strtoulW(ptr, &nextPtr, 10);
4088 if (!nextPtr || nextPtr <= ptr)
4090 ERR("unexpected time format %s\n", debugstr_w(ptr));
4091 return FALSE;
4093 if (num > 59)
4095 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4096 return FALSE;
4098 ptr = nextPtr + 1;
4099 *str = ptr;
4100 st->wSecond = (WORD)num;
4101 return TRUE;
4104 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4106 static const WCHAR gmt[]= { 'G','M','T',0 };
4107 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4108 LPCWSTR ptr;
4109 SYSTEMTIME st = { 0 };
4110 unsigned long num;
4112 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4113 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4114 *dayPtr = *ptr;
4115 *dayPtr = 0;
4116 st.wDayOfWeek = HTTP_ParseWkday(day);
4117 if (st.wDayOfWeek >= 7)
4119 ERR("unexpected weekday %s\n", debugstr_w(day));
4120 return FALSE;
4123 while (isspaceW(*ptr))
4124 ptr++;
4126 for (monthPtr = month; !isspace(*ptr) &&
4127 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4128 monthPtr++, ptr++)
4129 *monthPtr = *ptr;
4130 *monthPtr = 0;
4131 st.wMonth = HTTP_ParseMonth(month);
4132 if (!st.wMonth || st.wMonth > 12)
4134 ERR("unexpected month %s\n", debugstr_w(month));
4135 return FALSE;
4138 while (isspaceW(*ptr))
4139 ptr++;
4141 num = strtoulW(ptr, &nextPtr, 10);
4142 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4144 ERR("unexpected day %s\n", debugstr_w(ptr));
4145 return FALSE;
4147 ptr = nextPtr;
4148 st.wDay = (WORD)num;
4150 while (isspaceW(*ptr))
4151 ptr++;
4153 if (!HTTP_ParseTime(&st, &ptr))
4154 return FALSE;
4156 while (isspaceW(*ptr))
4157 ptr++;
4159 num = strtoulW(ptr, &nextPtr, 10);
4160 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4162 ERR("unexpected year %s\n", debugstr_w(ptr));
4163 return FALSE;
4165 ptr = nextPtr;
4166 st.wYear = (WORD)num;
4168 while (isspaceW(*ptr))
4169 ptr++;
4171 /* asctime() doesn't report a timezone, but some web servers do, so accept
4172 * with or without GMT.
4174 if (*ptr && strcmpW(ptr, gmt))
4176 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4177 return FALSE;
4179 return SystemTimeToFileTime(&st, ft);
4182 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4184 static const WCHAR gmt[]= { 'G','M','T',0 };
4185 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4186 LPCWSTR ptr;
4187 unsigned long num;
4188 SYSTEMTIME st = { 0 };
4190 ptr = strchrW(value, ',');
4191 if (!ptr)
4192 return FALSE;
4193 if (ptr - value != 3)
4195 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4196 return FALSE;
4198 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4199 day[3] = 0;
4200 st.wDayOfWeek = HTTP_ParseWkday(day);
4201 if (st.wDayOfWeek > 6)
4203 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4204 return FALSE;
4206 ptr++;
4208 while (isspaceW(*ptr))
4209 ptr++;
4211 num = strtoulW(ptr, &nextPtr, 10);
4212 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4214 WARN("unexpected day %s\n", debugstr_w(value));
4215 return FALSE;
4217 ptr = nextPtr;
4218 st.wDay = (WORD)num;
4220 while (isspaceW(*ptr))
4221 ptr++;
4223 for (monthPtr = month; !isspace(*ptr) &&
4224 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4225 monthPtr++, ptr++)
4226 *monthPtr = *ptr;
4227 *monthPtr = 0;
4228 st.wMonth = HTTP_ParseMonth(month);
4229 if (!st.wMonth || st.wMonth > 12)
4231 WARN("unexpected month %s\n", debugstr_w(month));
4232 return FALSE;
4235 while (isspaceW(*ptr))
4236 ptr++;
4238 num = strtoulW(ptr, &nextPtr, 10);
4239 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4241 ERR("unexpected year %s\n", debugstr_w(value));
4242 return FALSE;
4244 ptr = nextPtr;
4245 st.wYear = (WORD)num;
4247 if (!HTTP_ParseTime(&st, &ptr))
4248 return FALSE;
4250 while (isspaceW(*ptr))
4251 ptr++;
4253 if (strcmpW(ptr, gmt))
4255 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4256 return FALSE;
4258 return SystemTimeToFileTime(&st, ft);
4261 static WORD HTTP_ParseWeekday(LPCWSTR day)
4263 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4264 { 'm','o','n','d','a','y',0 },
4265 { 't','u','e','s','d','a','y',0 },
4266 { 'w','e','d','n','e','s','d','a','y',0 },
4267 { 't','h','u','r','s','d','a','y',0 },
4268 { 'f','r','i','d','a','y',0 },
4269 { 's','a','t','u','r','d','a','y',0 }};
4270 int i;
4271 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4272 if (!strcmpiW(day, days[i]))
4273 return i;
4275 /* Invalid */
4276 return 7;
4279 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4281 static const WCHAR gmt[]= { 'G','M','T',0 };
4282 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4283 LPCWSTR ptr;
4284 unsigned long num;
4285 SYSTEMTIME st = { 0 };
4287 ptr = strchrW(value, ',');
4288 if (!ptr)
4289 return FALSE;
4290 if (ptr - value == 3)
4292 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4293 day[3] = 0;
4294 st.wDayOfWeek = HTTP_ParseWkday(day);
4295 if (st.wDayOfWeek > 6)
4297 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4298 return FALSE;
4301 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4303 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4304 day[ptr - value + 1] = 0;
4305 st.wDayOfWeek = HTTP_ParseWeekday(day);
4306 if (st.wDayOfWeek > 6)
4308 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4309 return FALSE;
4312 else
4314 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4315 return FALSE;
4317 ptr++;
4319 while (isspaceW(*ptr))
4320 ptr++;
4322 num = strtoulW(ptr, &nextPtr, 10);
4323 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4325 ERR("unexpected day %s\n", debugstr_w(value));
4326 return FALSE;
4328 ptr = nextPtr;
4329 st.wDay = (WORD)num;
4331 if (*ptr != '-')
4333 ERR("unexpected month format %s\n", debugstr_w(ptr));
4334 return FALSE;
4336 ptr++;
4338 for (monthPtr = month; *ptr != '-' &&
4339 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4340 monthPtr++, ptr++)
4341 *monthPtr = *ptr;
4342 *monthPtr = 0;
4343 st.wMonth = HTTP_ParseMonth(month);
4344 if (!st.wMonth || st.wMonth > 12)
4346 ERR("unexpected month %s\n", debugstr_w(month));
4347 return FALSE;
4350 if (*ptr != '-')
4352 ERR("unexpected year format %s\n", debugstr_w(ptr));
4353 return FALSE;
4355 ptr++;
4357 num = strtoulW(ptr, &nextPtr, 10);
4358 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4360 ERR("unexpected year %s\n", debugstr_w(value));
4361 return FALSE;
4363 ptr = nextPtr;
4364 st.wYear = (WORD)num;
4366 if (!HTTP_ParseTime(&st, &ptr))
4367 return FALSE;
4369 while (isspaceW(*ptr))
4370 ptr++;
4372 if (strcmpW(ptr, gmt))
4374 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4375 return FALSE;
4377 return SystemTimeToFileTime(&st, ft);
4380 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4382 static const WCHAR zero[] = { '0',0 };
4383 BOOL ret;
4385 if (!strcmpW(value, zero))
4387 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4388 ret = TRUE;
4390 else if (strchrW(value, ','))
4392 ret = HTTP_ParseRfc1123Date(value, ft);
4393 if (!ret)
4395 ret = HTTP_ParseRfc850Date(value, ft);
4396 if (!ret)
4397 ERR("unexpected date format %s\n", debugstr_w(value));
4400 else
4402 ret = HTTP_ParseDateAsAsctime(value, ft);
4403 if (!ret)
4404 ERR("unexpected date format %s\n", debugstr_w(value));
4406 return ret;
4409 static void HTTP_ProcessExpires(http_request_t *request)
4411 BOOL expirationFound = FALSE;
4412 int headerIndex;
4414 /* Look for a Cache-Control header with a max-age directive, as it takes
4415 * precedence over the Expires header.
4417 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4418 if (headerIndex != -1)
4420 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4421 LPWSTR ptr;
4423 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4425 LPWSTR comma = strchrW(ptr, ','), end, equal;
4427 if (comma)
4428 end = comma;
4429 else
4430 end = ptr + strlenW(ptr);
4431 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4433 if (*equal == '=')
4435 static const WCHAR max_age[] = {
4436 'm','a','x','-','a','g','e',0 };
4438 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4440 LPWSTR nextPtr;
4441 unsigned long age;
4443 age = strtoulW(equal + 1, &nextPtr, 10);
4444 if (nextPtr > equal + 1)
4446 LARGE_INTEGER ft;
4448 NtQuerySystemTime( &ft );
4449 /* Age is in seconds, FILETIME resolution is in
4450 * 100 nanosecond intervals.
4452 ft.QuadPart += age * (ULONGLONG)1000000;
4453 request->expires.dwLowDateTime = ft.u.LowPart;
4454 request->expires.dwHighDateTime = ft.u.HighPart;
4455 expirationFound = TRUE;
4459 if (comma)
4461 ptr = comma + 1;
4462 while (isspaceW(*ptr))
4463 ptr++;
4465 else
4466 ptr = NULL;
4469 if (!expirationFound)
4471 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4472 if (headerIndex != -1)
4474 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4475 FILETIME ft;
4477 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4479 expirationFound = TRUE;
4480 request->expires = ft;
4484 if (!expirationFound)
4486 LARGE_INTEGER t;
4488 /* With no known age, default to 10 minutes until expiration. */
4489 NtQuerySystemTime( &t );
4490 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4491 request->expires.dwLowDateTime = t.u.LowPart;
4492 request->expires.dwHighDateTime = t.u.HighPart;
4496 static void HTTP_ProcessLastModified(http_request_t *request)
4498 int headerIndex;
4500 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4501 if (headerIndex != -1)
4503 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4504 FILETIME ft;
4506 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4507 request->last_modified = ft;
4511 static void http_process_keep_alive(http_request_t *req)
4513 int index;
4515 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4516 if(index != -1)
4517 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4518 else
4519 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4522 static void HTTP_CacheRequest(http_request_t *request)
4524 WCHAR url[INTERNET_MAX_URL_LENGTH];
4525 WCHAR cacheFileName[MAX_PATH+1];
4526 BOOL b;
4528 b = HTTP_GetRequestURL(request, url);
4529 if(!b) {
4530 WARN("Could not get URL\n");
4531 return;
4534 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4535 if(b) {
4536 heap_free(request->cacheFile);
4537 CloseHandle(request->hCacheFile);
4539 request->cacheFile = heap_strdupW(cacheFileName);
4540 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4541 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4542 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4543 WARN("Could not create file: %u\n", GetLastError());
4544 request->hCacheFile = NULL;
4546 }else {
4547 WARN("Could not create cache entry: %08x\n", GetLastError());
4551 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4553 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4554 http_session_t *session = request->session;
4555 netconn_t *netconn = NULL;
4556 server_t *server;
4557 DWORD res;
4559 assert(!request->netconn);
4560 reset_data_stream(request);
4562 server = get_server(session->serverName, session->serverPort);
4563 if(!server)
4564 return ERROR_OUTOFMEMORY;
4566 res = HTTP_ResolveName(request, server);
4567 if(res != ERROR_SUCCESS) {
4568 server_release(server);
4569 return res;
4572 EnterCriticalSection(&connection_pool_cs);
4574 while(!list_empty(&server->conn_pool)) {
4575 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4576 list_remove(&netconn->pool_entry);
4578 if(NETCON_is_alive(netconn))
4579 break;
4581 TRACE("connection %p closed during idle\n", netconn);
4582 free_netconn(netconn);
4583 netconn = NULL;
4586 LeaveCriticalSection(&connection_pool_cs);
4588 if(netconn) {
4589 TRACE("<-- reusing %p netconn\n", netconn);
4590 request->netconn = netconn;
4591 *reusing = TRUE;
4592 return ERROR_SUCCESS;
4595 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4596 INTERNET_STATUS_CONNECTING_TO_SERVER,
4597 server->addr_str,
4598 strlen(server->addr_str)+1);
4600 res = create_netconn(is_https, server, request->security_flags, &netconn);
4601 server_release(server);
4602 if(res != ERROR_SUCCESS) {
4603 ERR("create_netconn failed: %u\n", res);
4604 return res;
4607 request->netconn = netconn;
4609 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4610 INTERNET_STATUS_CONNECTED_TO_SERVER,
4611 server->addr_str, strlen(server->addr_str)+1);
4613 if(is_https) {
4614 /* Note: we differ from Microsoft's WinINet here. they seem to have
4615 * a bug that causes no status callbacks to be sent when starting
4616 * a tunnel to a proxy server using the CONNECT verb. i believe our
4617 * behaviour to be more correct and to not cause any incompatibilities
4618 * because using a secure connection through a proxy server is a rare
4619 * case that would be hard for anyone to depend on */
4620 if(session->appInfo->proxy)
4621 res = HTTP_SecureProxyConnect(request);
4622 if(res == ERROR_SUCCESS)
4623 res = NETCON_secure_connect(request->netconn, session->hostName);
4624 if(res != ERROR_SUCCESS)
4626 WARN("Couldn't connect securely to host\n");
4628 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4629 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4630 || res == ERROR_INTERNET_INVALID_CA
4631 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4632 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4633 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4634 || res == ERROR_INTERNET_SEC_INVALID_CERT
4635 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4636 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4640 if(res != ERROR_SUCCESS) {
4641 http_release_netconn(request, FALSE);
4642 return res;
4645 *reusing = FALSE;
4646 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4647 return ERROR_SUCCESS;
4650 /***********************************************************************
4651 * HTTP_HttpSendRequestW (internal)
4653 * Sends the specified request to the HTTP server
4655 * RETURNS
4656 * ERROR_SUCCESS on success
4657 * win32 error code on failure
4660 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4661 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4662 DWORD dwContentLength, BOOL bEndRequest)
4664 INT cnt;
4665 BOOL redirected = FALSE;
4666 LPWSTR requestString = NULL;
4667 INT responseLen;
4668 BOOL loop_next;
4669 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4670 static const WCHAR szContentLength[] =
4671 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4672 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4673 DWORD res;
4675 TRACE("--> %p\n", request);
4677 assert(request->hdr.htype == WH_HHTTPREQ);
4679 /* if the verb is NULL default to GET */
4680 if (!request->verb)
4681 request->verb = heap_strdupW(szGET);
4683 if (dwContentLength || strcmpW(request->verb, szGET))
4685 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4686 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4687 request->bytesToWrite = dwContentLength;
4689 if (request->session->appInfo->agent)
4691 WCHAR *agent_header;
4692 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4693 int len;
4695 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4696 agent_header = heap_alloc(len * sizeof(WCHAR));
4697 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4699 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4700 heap_free(agent_header);
4702 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4704 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4705 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4707 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4709 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4710 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4711 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4716 DWORD len;
4717 BOOL reusing_connection;
4718 char *ascii_req;
4720 loop_next = FALSE;
4722 /* like native, just in case the caller forgot to call InternetReadFile
4723 * for all the data */
4724 drain_content(request);
4725 if(redirected) {
4726 request->contentLength = ~0u;
4727 request->bytesToWrite = 0;
4730 if (TRACE_ON(wininet))
4732 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4733 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4736 HTTP_FixURL(request);
4737 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4739 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4741 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4742 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4744 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4745 HTTP_InsertCookies(request);
4747 /* add the headers the caller supplied */
4748 if( lpszHeaders && dwHeaderLength )
4750 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4751 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4754 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4756 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4757 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4758 heap_free(url);
4760 else
4761 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4764 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4766 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4767 break;
4769 /* send the request as ASCII, tack on the optional data */
4770 if (!lpOptional || redirected)
4771 dwOptionalLength = 0;
4772 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4773 NULL, 0, NULL, NULL );
4774 ascii_req = heap_alloc(len + dwOptionalLength);
4775 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4776 ascii_req, len, NULL, NULL );
4777 if( lpOptional )
4778 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4779 len = (len + dwOptionalLength - 1);
4780 ascii_req[len] = 0;
4781 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4783 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4784 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4786 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4787 heap_free( ascii_req );
4788 if(res != ERROR_SUCCESS) {
4789 TRACE("send failed: %u\n", res);
4790 if(!reusing_connection)
4791 break;
4792 http_release_netconn(request, FALSE);
4793 loop_next = TRUE;
4794 continue;
4797 request->bytesWritten = dwOptionalLength;
4799 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4800 INTERNET_STATUS_REQUEST_SENT,
4801 &len, sizeof(DWORD));
4803 if (bEndRequest)
4805 DWORD dwBufferSize;
4806 DWORD dwStatusCode;
4808 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4809 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4811 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4812 /* FIXME: We should know that connection is closed before sending
4813 * headers. Otherwise wrong callbacks are executed */
4814 if(!responseLen && reusing_connection) {
4815 TRACE("Connection closed by server, reconnecting\n");
4816 http_release_netconn(request, FALSE);
4817 loop_next = TRUE;
4818 continue;
4821 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4822 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4823 sizeof(DWORD));
4825 http_process_keep_alive(request);
4826 HTTP_ProcessCookies(request);
4827 HTTP_ProcessExpires(request);
4828 HTTP_ProcessLastModified(request);
4830 dwBufferSize = sizeof(dwStatusCode);
4831 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4832 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4833 dwStatusCode = 0;
4835 res = set_content_length(request, dwStatusCode);
4836 if(res != ERROR_SUCCESS)
4837 goto lend;
4838 if(!request->contentLength)
4839 http_release_netconn(request, TRUE);
4841 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4843 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4844 dwBufferSize=sizeof(szNewLocation);
4845 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4846 dwStatusCode == HTTP_STATUS_MOVED ||
4847 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4848 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4849 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4851 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4852 dwStatusCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4854 heap_free(request->verb);
4855 request->verb = heap_strdupW(szGET);
4857 drain_content(request);
4858 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4860 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4861 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4862 res = HTTP_HandleRedirect(request, new_url);
4863 if (res == ERROR_SUCCESS)
4865 heap_free(requestString);
4866 loop_next = TRUE;
4868 heap_free( new_url );
4870 redirected = TRUE;
4873 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4875 WCHAR szAuthValue[2048];
4876 dwBufferSize=2048;
4877 if (dwStatusCode == HTTP_STATUS_DENIED)
4879 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4880 DWORD dwIndex = 0;
4881 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4883 if (HTTP_DoAuthorization(request, szAuthValue,
4884 &request->authInfo,
4885 request->session->userName,
4886 request->session->password,
4887 Host->lpszValue))
4889 heap_free(requestString);
4890 loop_next = TRUE;
4891 break;
4895 if(!loop_next) {
4896 TRACE("Cleaning wrong authorization data\n");
4897 destroy_authinfo(request->authInfo);
4898 request->authInfo = NULL;
4901 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4903 DWORD dwIndex = 0;
4904 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4906 if (HTTP_DoAuthorization(request, szAuthValue,
4907 &request->proxyAuthInfo,
4908 request->session->appInfo->proxyUsername,
4909 request->session->appInfo->proxyPassword,
4910 NULL))
4912 loop_next = TRUE;
4913 break;
4917 if(!loop_next) {
4918 TRACE("Cleaning wrong proxy authorization data\n");
4919 destroy_authinfo(request->proxyAuthInfo);
4920 request->proxyAuthInfo = NULL;
4925 else
4926 res = ERROR_SUCCESS;
4928 while (loop_next);
4930 if(res == ERROR_SUCCESS)
4931 HTTP_CacheRequest(request);
4933 lend:
4934 heap_free(requestString);
4936 /* TODO: send notification for P3P header */
4938 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4940 if (res == ERROR_SUCCESS) {
4941 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4942 HTTP_ReceiveRequestData(request, TRUE);
4943 else
4944 send_request_complete(request,
4945 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4946 }else {
4947 send_request_complete(request, 0, res);
4951 TRACE("<--\n");
4952 return res;
4955 /***********************************************************************
4957 * Helper functions for the HttpSendRequest(Ex) functions
4960 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4962 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4963 http_request_t *request = (http_request_t*) workRequest->hdr;
4965 TRACE("%p\n", request);
4967 HTTP_HttpSendRequestW(request, req->lpszHeader,
4968 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4969 req->dwContentLength, req->bEndRequest);
4971 heap_free(req->lpszHeader);
4975 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4977 INT responseLen;
4978 DWORD dwCode, dwCodeLength;
4979 DWORD dwBufferSize;
4980 DWORD res = ERROR_SUCCESS;
4982 if(!request->netconn) {
4983 WARN("Not connected\n");
4984 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
4985 return ERROR_INTERNET_OPERATION_CANCELLED;
4988 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4989 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4991 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4992 if (!responseLen)
4993 res = ERROR_HTTP_HEADER_NOT_FOUND;
4995 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4996 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4998 /* process cookies here. Is this right? */
4999 http_process_keep_alive(request);
5000 HTTP_ProcessCookies(request);
5001 HTTP_ProcessExpires(request);
5002 HTTP_ProcessLastModified(request);
5004 dwCodeLength = sizeof(dwCode);
5005 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
5006 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
5007 dwCode = 0;
5009 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
5010 if(!request->contentLength)
5011 http_release_netconn(request, TRUE);
5014 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5016 if (dwCode == HTTP_STATUS_REDIRECT ||
5017 dwCode == HTTP_STATUS_MOVED ||
5018 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
5019 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
5021 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5022 dwBufferSize=sizeof(szNewLocation);
5023 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
5025 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5026 dwCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
5028 heap_free(request->verb);
5029 request->verb = heap_strdupW(szGET);
5031 drain_content(request);
5032 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5034 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5035 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5036 res = HTTP_HandleRedirect(request, new_url);
5037 if (res == ERROR_SUCCESS)
5038 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5039 heap_free( new_url );
5045 if (res == ERROR_SUCCESS && request->contentLength)
5046 HTTP_ReceiveRequestData(request, TRUE);
5047 else
5048 send_request_complete(request, res == ERROR_SUCCESS, res);
5050 return res;
5053 /***********************************************************************
5054 * HttpEndRequestA (WININET.@)
5056 * Ends an HTTP request that was started by HttpSendRequestEx
5058 * RETURNS
5059 * TRUE if successful
5060 * FALSE on failure
5063 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5064 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5066 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5068 if (lpBuffersOut)
5070 SetLastError(ERROR_INVALID_PARAMETER);
5071 return FALSE;
5074 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5077 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5079 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5080 http_request_t *request = (http_request_t*)work->hdr;
5082 TRACE("%p\n", request);
5084 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5087 /***********************************************************************
5088 * HttpEndRequestW (WININET.@)
5090 * Ends an HTTP request that was started by HttpSendRequestEx
5092 * RETURNS
5093 * TRUE if successful
5094 * FALSE on failure
5097 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5098 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5100 http_request_t *request;
5101 DWORD res;
5103 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5105 if (lpBuffersOut)
5107 SetLastError(ERROR_INVALID_PARAMETER);
5108 return FALSE;
5111 request = (http_request_t*) get_handle_object( hRequest );
5113 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5115 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5116 if (request)
5117 WININET_Release( &request->hdr );
5118 return FALSE;
5120 request->hdr.dwFlags |= dwFlags;
5122 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5124 WORKREQUEST work;
5125 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5127 work.asyncproc = AsyncHttpEndRequestProc;
5128 work.hdr = WININET_AddRef( &request->hdr );
5130 work_endrequest = &work.u.HttpEndRequestW;
5131 work_endrequest->dwFlags = dwFlags;
5132 work_endrequest->dwContext = dwContext;
5134 INTERNET_AsyncCall(&work);
5135 res = ERROR_IO_PENDING;
5137 else
5138 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5140 WININET_Release( &request->hdr );
5141 TRACE("%u <--\n", res);
5142 if(res != ERROR_SUCCESS)
5143 SetLastError(res);
5144 return res == ERROR_SUCCESS;
5147 /***********************************************************************
5148 * HttpSendRequestExA (WININET.@)
5150 * Sends the specified request to the HTTP server and allows chunked
5151 * transfers.
5153 * RETURNS
5154 * Success: TRUE
5155 * Failure: FALSE, call GetLastError() for more information.
5157 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5158 LPINTERNET_BUFFERSA lpBuffersIn,
5159 LPINTERNET_BUFFERSA lpBuffersOut,
5160 DWORD dwFlags, DWORD_PTR dwContext)
5162 INTERNET_BUFFERSW BuffersInW;
5163 BOOL rc = FALSE;
5164 DWORD headerlen;
5165 LPWSTR header = NULL;
5167 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5168 lpBuffersOut, dwFlags, dwContext);
5170 if (lpBuffersIn)
5172 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5173 if (lpBuffersIn->lpcszHeader)
5175 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5176 lpBuffersIn->dwHeadersLength,0,0);
5177 header = heap_alloc(headerlen*sizeof(WCHAR));
5178 if (!(BuffersInW.lpcszHeader = header))
5180 SetLastError(ERROR_OUTOFMEMORY);
5181 return FALSE;
5183 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5184 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5185 header, headerlen);
5187 else
5188 BuffersInW.lpcszHeader = NULL;
5189 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5190 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5191 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5192 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5193 BuffersInW.Next = NULL;
5196 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5198 heap_free(header);
5199 return rc;
5202 /***********************************************************************
5203 * HttpSendRequestExW (WININET.@)
5205 * Sends the specified request to the HTTP server and allows chunked
5206 * transfers
5208 * RETURNS
5209 * Success: TRUE
5210 * Failure: FALSE, call GetLastError() for more information.
5212 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5213 LPINTERNET_BUFFERSW lpBuffersIn,
5214 LPINTERNET_BUFFERSW lpBuffersOut,
5215 DWORD dwFlags, DWORD_PTR dwContext)
5217 http_request_t *request;
5218 http_session_t *session;
5219 appinfo_t *hIC;
5220 DWORD res;
5222 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5223 lpBuffersOut, dwFlags, dwContext);
5225 request = (http_request_t*) get_handle_object( hRequest );
5227 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5229 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5230 goto lend;
5233 session = request->session;
5234 assert(session->hdr.htype == WH_HHTTPSESSION);
5235 hIC = session->appInfo;
5236 assert(hIC->hdr.htype == WH_HINIT);
5238 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5240 WORKREQUEST workRequest;
5241 struct WORKREQ_HTTPSENDREQUESTW *req;
5243 workRequest.asyncproc = AsyncHttpSendRequestProc;
5244 workRequest.hdr = WININET_AddRef( &request->hdr );
5245 req = &workRequest.u.HttpSendRequestW;
5246 if (lpBuffersIn)
5248 DWORD size = 0;
5250 if (lpBuffersIn->lpcszHeader)
5252 if (lpBuffersIn->dwHeadersLength == ~0u)
5253 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5254 else
5255 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5257 req->lpszHeader = heap_alloc(size);
5258 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5260 else req->lpszHeader = NULL;
5262 req->dwHeaderLength = size / sizeof(WCHAR);
5263 req->lpOptional = lpBuffersIn->lpvBuffer;
5264 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5265 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5267 else
5269 req->lpszHeader = NULL;
5270 req->dwHeaderLength = 0;
5271 req->lpOptional = NULL;
5272 req->dwOptionalLength = 0;
5273 req->dwContentLength = 0;
5276 req->bEndRequest = FALSE;
5278 INTERNET_AsyncCall(&workRequest);
5280 * This is from windows.
5282 res = ERROR_IO_PENDING;
5284 else
5286 if (lpBuffersIn)
5287 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5288 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5289 lpBuffersIn->dwBufferTotal, FALSE);
5290 else
5291 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5294 lend:
5295 if ( request )
5296 WININET_Release( &request->hdr );
5298 TRACE("<---\n");
5299 SetLastError(res);
5300 return res == ERROR_SUCCESS;
5303 /***********************************************************************
5304 * HttpSendRequestW (WININET.@)
5306 * Sends the specified request to the HTTP server
5308 * RETURNS
5309 * TRUE on success
5310 * FALSE on failure
5313 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5314 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5316 http_request_t *request;
5317 http_session_t *session = NULL;
5318 appinfo_t *hIC = NULL;
5319 DWORD res = ERROR_SUCCESS;
5321 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5322 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5324 request = (http_request_t*) get_handle_object( hHttpRequest );
5325 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5327 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5328 goto lend;
5331 session = request->session;
5332 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5334 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5335 goto lend;
5338 hIC = session->appInfo;
5339 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5341 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5342 goto lend;
5345 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5347 WORKREQUEST workRequest;
5348 struct WORKREQ_HTTPSENDREQUESTW *req;
5350 workRequest.asyncproc = AsyncHttpSendRequestProc;
5351 workRequest.hdr = WININET_AddRef( &request->hdr );
5352 req = &workRequest.u.HttpSendRequestW;
5353 if (lpszHeaders)
5355 DWORD size;
5357 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5358 else size = dwHeaderLength * sizeof(WCHAR);
5360 req->lpszHeader = heap_alloc(size);
5361 memcpy(req->lpszHeader, lpszHeaders, size);
5363 else
5364 req->lpszHeader = 0;
5365 req->dwHeaderLength = dwHeaderLength;
5366 req->lpOptional = lpOptional;
5367 req->dwOptionalLength = dwOptionalLength;
5368 req->dwContentLength = dwOptionalLength;
5369 req->bEndRequest = TRUE;
5371 INTERNET_AsyncCall(&workRequest);
5373 * This is from windows.
5375 res = ERROR_IO_PENDING;
5377 else
5379 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5380 dwHeaderLength, lpOptional, dwOptionalLength,
5381 dwOptionalLength, TRUE);
5383 lend:
5384 if( request )
5385 WININET_Release( &request->hdr );
5387 SetLastError(res);
5388 return res == ERROR_SUCCESS;
5391 /***********************************************************************
5392 * HttpSendRequestA (WININET.@)
5394 * Sends the specified request to the HTTP server
5396 * RETURNS
5397 * TRUE on success
5398 * FALSE on failure
5401 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5402 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5404 BOOL result;
5405 LPWSTR szHeaders=NULL;
5406 DWORD nLen=dwHeaderLength;
5407 if(lpszHeaders!=NULL)
5409 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5410 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5411 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5413 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5414 heap_free(szHeaders);
5415 return result;
5418 /***********************************************************************
5419 * HTTPSESSION_Destroy (internal)
5421 * Deallocate session handle
5424 static void HTTPSESSION_Destroy(object_header_t *hdr)
5426 http_session_t *session = (http_session_t*) hdr;
5428 TRACE("%p\n", session);
5430 WININET_Release(&session->appInfo->hdr);
5432 heap_free(session->hostName);
5433 heap_free(session->serverName);
5434 heap_free(session->password);
5435 heap_free(session->userName);
5438 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5440 switch(option) {
5441 case INTERNET_OPTION_HANDLE_TYPE:
5442 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5444 if (*size < sizeof(ULONG))
5445 return ERROR_INSUFFICIENT_BUFFER;
5447 *size = sizeof(DWORD);
5448 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5449 return ERROR_SUCCESS;
5452 return INET_QueryOption(hdr, option, buffer, size, unicode);
5455 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5457 http_session_t *ses = (http_session_t*)hdr;
5459 switch(option) {
5460 case INTERNET_OPTION_USERNAME:
5462 heap_free(ses->userName);
5463 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5464 return ERROR_SUCCESS;
5466 case INTERNET_OPTION_PASSWORD:
5468 heap_free(ses->password);
5469 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5470 return ERROR_SUCCESS;
5472 default: break;
5475 return ERROR_INTERNET_INVALID_OPTION;
5478 static const object_vtbl_t HTTPSESSIONVtbl = {
5479 HTTPSESSION_Destroy,
5480 NULL,
5481 HTTPSESSION_QueryOption,
5482 HTTPSESSION_SetOption,
5483 NULL,
5484 NULL,
5485 NULL,
5486 NULL,
5487 NULL
5491 /***********************************************************************
5492 * HTTP_Connect (internal)
5494 * Create http session handle
5496 * RETURNS
5497 * HINTERNET a session handle on success
5498 * NULL on failure
5501 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5502 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5503 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5504 DWORD dwInternalFlags, HINTERNET *ret)
5506 http_session_t *session = NULL;
5508 TRACE("-->\n");
5510 if (!lpszServerName || !lpszServerName[0])
5511 return ERROR_INVALID_PARAMETER;
5513 assert( hIC->hdr.htype == WH_HINIT );
5515 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5516 if (!session)
5517 return ERROR_OUTOFMEMORY;
5520 * According to my tests. The name is not resolved until a request is sent
5523 session->hdr.htype = WH_HHTTPSESSION;
5524 session->hdr.dwFlags = dwFlags;
5525 session->hdr.dwContext = dwContext;
5526 session->hdr.dwInternalFlags |= dwInternalFlags;
5528 WININET_AddRef( &hIC->hdr );
5529 session->appInfo = hIC;
5530 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5532 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5533 if(hIC->proxyBypass)
5534 FIXME("Proxy bypass is ignored.\n");
5536 session->serverName = heap_strdupW(lpszServerName);
5537 session->hostName = heap_strdupW(lpszServerName);
5538 if (lpszUserName && lpszUserName[0])
5539 session->userName = heap_strdupW(lpszUserName);
5540 if (lpszPassword && lpszPassword[0])
5541 session->password = heap_strdupW(lpszPassword);
5542 session->serverPort = serverPort;
5543 session->hostPort = serverPort;
5545 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5546 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5548 INTERNET_SendCallback(&hIC->hdr, dwContext,
5549 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5550 sizeof(HINTERNET));
5554 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5555 * windows
5558 TRACE("%p --> %p\n", hIC, session);
5560 *ret = session->hdr.hInternet;
5561 return ERROR_SUCCESS;
5564 /***********************************************************************
5565 * HTTP_clear_response_headers (internal)
5567 * clear out any old response headers
5569 static void HTTP_clear_response_headers( http_request_t *request )
5571 DWORD i;
5573 for( i=0; i<request->nCustHeaders; i++)
5575 if( !request->custHeaders[i].lpszField )
5576 continue;
5577 if( !request->custHeaders[i].lpszValue )
5578 continue;
5579 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5580 continue;
5581 HTTP_DeleteCustomHeader( request, i );
5582 i--;
5586 /***********************************************************************
5587 * HTTP_GetResponseHeaders (internal)
5589 * Read server response
5591 * RETURNS
5593 * TRUE on success
5594 * FALSE on error
5596 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5598 INT cbreaks = 0;
5599 WCHAR buffer[MAX_REPLY_LEN];
5600 DWORD buflen = MAX_REPLY_LEN;
5601 BOOL bSuccess = FALSE;
5602 INT rc = 0;
5603 char bufferA[MAX_REPLY_LEN];
5604 LPWSTR status_code = NULL, status_text = NULL;
5605 DWORD cchMaxRawHeaders = 1024;
5606 LPWSTR lpszRawHeaders = NULL;
5607 LPWSTR temp;
5608 DWORD cchRawHeaders = 0;
5609 BOOL codeHundred = FALSE;
5611 TRACE("-->\n");
5613 if(!request->netconn)
5614 goto lend;
5616 do {
5617 static const WCHAR szHundred[] = {'1','0','0',0};
5619 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5621 buflen = MAX_REPLY_LEN;
5622 if (!read_line(request, bufferA, &buflen))
5623 goto lend;
5625 /* clear old response headers (eg. from a redirect response) */
5626 if (clear) {
5627 HTTP_clear_response_headers( request );
5628 clear = FALSE;
5631 rc += buflen;
5632 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5633 /* check is this a status code line? */
5634 if (!strncmpW(buffer, g_szHttp1_0, 4))
5636 /* split the version from the status code */
5637 status_code = strchrW( buffer, ' ' );
5638 if( !status_code )
5639 goto lend;
5640 *status_code++=0;
5642 /* split the status code from the status text */
5643 status_text = strchrW( status_code, ' ' );
5644 if( !status_text )
5645 goto lend;
5646 *status_text++=0;
5648 TRACE("version [%s] status code [%s] status text [%s]\n",
5649 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5651 codeHundred = (!strcmpW(status_code, szHundred));
5653 else if (!codeHundred)
5655 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5657 heap_free(request->version);
5658 heap_free(request->statusText);
5660 request->version = heap_strdupW(g_szHttp1_0);
5661 request->statusText = heap_strdupW(szOK);
5663 heap_free(request->rawHeaders);
5664 request->rawHeaders = heap_strdupW(szDefaultHeader);
5666 bSuccess = TRUE;
5667 goto lend;
5669 } while (codeHundred);
5671 /* Add status code */
5672 HTTP_ProcessHeader(request, szStatus, status_code,
5673 HTTP_ADDHDR_FLAG_REPLACE);
5675 heap_free(request->version);
5676 heap_free(request->statusText);
5678 request->version = heap_strdupW(buffer);
5679 request->statusText = heap_strdupW(status_text);
5681 /* Restore the spaces */
5682 *(status_code-1) = ' ';
5683 *(status_text-1) = ' ';
5685 /* regenerate raw headers */
5686 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5687 if (!lpszRawHeaders) goto lend;
5689 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5690 cchMaxRawHeaders *= 2;
5691 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5692 if (temp == NULL) goto lend;
5693 lpszRawHeaders = temp;
5694 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5695 cchRawHeaders += (buflen-1);
5696 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5697 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5698 lpszRawHeaders[cchRawHeaders] = '\0';
5700 /* Parse each response line */
5703 buflen = MAX_REPLY_LEN;
5704 if (read_line(request, bufferA, &buflen))
5706 LPWSTR * pFieldAndValue;
5708 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5710 if (!bufferA[0]) break;
5711 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5713 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5714 if (pFieldAndValue)
5716 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5717 cchMaxRawHeaders *= 2;
5718 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5719 if (temp == NULL) goto lend;
5720 lpszRawHeaders = temp;
5721 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5722 cchRawHeaders += (buflen-1);
5723 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5724 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5725 lpszRawHeaders[cchRawHeaders] = '\0';
5727 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5728 HTTP_ADDREQ_FLAG_ADD );
5730 HTTP_FreeTokens(pFieldAndValue);
5733 else
5735 cbreaks++;
5736 if (cbreaks >= 2)
5737 break;
5739 }while(1);
5741 /* make sure the response header is terminated with an empty line. Some apps really
5742 truly care about that empty line being there for some reason. Just add it to the
5743 header. */
5744 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5746 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5747 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5748 if (temp == NULL) goto lend;
5749 lpszRawHeaders = temp;
5752 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5754 heap_free(request->rawHeaders);
5755 request->rawHeaders = lpszRawHeaders;
5756 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5757 bSuccess = TRUE;
5759 lend:
5761 TRACE("<--\n");
5762 if (bSuccess)
5763 return rc;
5764 else
5766 heap_free(lpszRawHeaders);
5767 return 0;
5771 /***********************************************************************
5772 * HTTP_InterpretHttpHeader (internal)
5774 * Parse server response
5776 * RETURNS
5778 * Pointer to array of field, value, NULL on success.
5779 * NULL on error.
5781 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5783 LPWSTR * pTokenPair;
5784 LPWSTR pszColon;
5785 INT len;
5787 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5789 pszColon = strchrW(buffer, ':');
5790 /* must have two tokens */
5791 if (!pszColon)
5793 HTTP_FreeTokens(pTokenPair);
5794 if (buffer[0])
5795 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5796 return NULL;
5799 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5800 if (!pTokenPair[0])
5802 HTTP_FreeTokens(pTokenPair);
5803 return NULL;
5805 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5806 pTokenPair[0][pszColon - buffer] = '\0';
5808 /* skip colon */
5809 pszColon++;
5810 len = strlenW(pszColon);
5811 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5812 if (!pTokenPair[1])
5814 HTTP_FreeTokens(pTokenPair);
5815 return NULL;
5817 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5819 strip_spaces(pTokenPair[0]);
5820 strip_spaces(pTokenPair[1]);
5822 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5823 return pTokenPair;
5826 /***********************************************************************
5827 * HTTP_ProcessHeader (internal)
5829 * Stuff header into header tables according to <dwModifier>
5833 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5835 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5837 LPHTTPHEADERW lphttpHdr = NULL;
5838 INT index = -1;
5839 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5840 DWORD res = ERROR_HTTP_INVALID_HEADER;
5842 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5844 /* REPLACE wins out over ADD */
5845 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5846 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5848 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5849 index = -1;
5850 else
5851 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5853 if (index >= 0)
5855 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5856 return ERROR_HTTP_INVALID_HEADER;
5857 lphttpHdr = &request->custHeaders[index];
5859 else if (value)
5861 HTTPHEADERW hdr;
5863 hdr.lpszField = (LPWSTR)field;
5864 hdr.lpszValue = (LPWSTR)value;
5865 hdr.wFlags = hdr.wCount = 0;
5867 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5868 hdr.wFlags |= HDR_ISREQUEST;
5870 return HTTP_InsertCustomHeader(request, &hdr);
5872 /* no value to delete */
5873 else return ERROR_SUCCESS;
5875 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5876 lphttpHdr->wFlags |= HDR_ISREQUEST;
5877 else
5878 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5880 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5882 HTTP_DeleteCustomHeader( request, index );
5884 if (value)
5886 HTTPHEADERW hdr;
5888 hdr.lpszField = (LPWSTR)field;
5889 hdr.lpszValue = (LPWSTR)value;
5890 hdr.wFlags = hdr.wCount = 0;
5892 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5893 hdr.wFlags |= HDR_ISREQUEST;
5895 return HTTP_InsertCustomHeader(request, &hdr);
5898 return ERROR_SUCCESS;
5900 else if (dwModifier & COALESCEFLAGS)
5902 LPWSTR lpsztmp;
5903 WCHAR ch = 0;
5904 INT len = 0;
5905 INT origlen = strlenW(lphttpHdr->lpszValue);
5906 INT valuelen = strlenW(value);
5908 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5910 ch = ',';
5911 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5913 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5915 ch = ';';
5916 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5919 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5921 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5922 if (lpsztmp)
5924 lphttpHdr->lpszValue = lpsztmp;
5925 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5926 if (ch > 0)
5928 lphttpHdr->lpszValue[origlen] = ch;
5929 origlen++;
5930 lphttpHdr->lpszValue[origlen] = ' ';
5931 origlen++;
5934 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5935 lphttpHdr->lpszValue[len] = '\0';
5936 res = ERROR_SUCCESS;
5938 else
5940 WARN("heap_realloc (%d bytes) failed\n",len+1);
5941 res = ERROR_OUTOFMEMORY;
5944 TRACE("<-- %d\n", res);
5945 return res;
5948 /***********************************************************************
5949 * HTTP_GetCustomHeaderIndex (internal)
5951 * Return index of custom header from header array
5954 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5955 int requested_index, BOOL request_only)
5957 DWORD index;
5959 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5961 for (index = 0; index < request->nCustHeaders; index++)
5963 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5964 continue;
5966 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5967 continue;
5969 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5970 continue;
5972 if (requested_index == 0)
5973 break;
5974 requested_index --;
5977 if (index >= request->nCustHeaders)
5978 index = -1;
5980 TRACE("Return: %d\n", index);
5981 return index;
5985 /***********************************************************************
5986 * HTTP_InsertCustomHeader (internal)
5988 * Insert header into array
5991 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5993 INT count;
5994 LPHTTPHEADERW lph = NULL;
5996 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5997 count = request->nCustHeaders + 1;
5998 if (count > 1)
5999 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6000 else
6001 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6003 if (!lph)
6004 return ERROR_OUTOFMEMORY;
6006 request->custHeaders = lph;
6007 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6008 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6009 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6010 request->custHeaders[count-1].wCount= lpHdr->wCount;
6011 request->nCustHeaders++;
6013 return ERROR_SUCCESS;
6017 /***********************************************************************
6018 * HTTP_DeleteCustomHeader (internal)
6020 * Delete header from array
6021 * If this function is called, the indexs may change.
6023 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6025 if( request->nCustHeaders <= 0 )
6026 return FALSE;
6027 if( index >= request->nCustHeaders )
6028 return FALSE;
6029 request->nCustHeaders--;
6031 heap_free(request->custHeaders[index].lpszField);
6032 heap_free(request->custHeaders[index].lpszValue);
6034 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6035 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6036 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6038 return TRUE;
6042 /***********************************************************************
6043 * HTTP_VerifyValidHeader (internal)
6045 * Verify the given header is not invalid for the given http request
6048 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6050 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6051 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6052 return ERROR_HTTP_INVALID_HEADER;
6054 return ERROR_SUCCESS;
6057 /***********************************************************************
6058 * IsHostInProxyBypassList (@)
6060 * Undocumented
6063 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6065 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6066 return FALSE;
6069 /***********************************************************************
6070 * InternetShowSecurityInfoByURLA (@)
6072 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6074 FIXME("stub: %s %p\n", url, window);
6075 return FALSE;
6078 /***********************************************************************
6079 * InternetShowSecurityInfoByURLW (@)
6081 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6083 FIXME("stub: %s %p\n", debugstr_w(url), window);
6084 return FALSE;
6087 /***********************************************************************
6088 * ShowX509EncodedCertificate (@)
6090 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6092 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6093 cert, len);
6094 DWORD ret;
6096 if (certContext)
6098 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6100 memset(&view, 0, sizeof(view));
6101 view.hwndParent = parent;
6102 view.pCertContext = certContext;
6103 if (CryptUIDlgViewCertificateW(&view, NULL))
6104 ret = ERROR_SUCCESS;
6105 else
6106 ret = GetLastError();
6107 CertFreeCertificateContext(certContext);
6109 else
6110 ret = GetLastError();
6111 return ret;