msxml3: Don't return REFIID to avoid compiler warnings.
[wine/multimedia.git] / dlls / wininet / http.c
blob16f550c68c858950d98561178240013280815e5b
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);
1887 return;
1890 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1891 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1893 free_netconn(req->netconn);
1894 req->netconn = NULL;
1896 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1897 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1900 static void drain_content(http_request_t *req)
1902 BOOL try_reuse;
1904 if (!req->netconn) return;
1906 if (req->contentLength == -1)
1907 try_reuse = FALSE;
1908 else if(!strcmpW(req->verb, szHEAD))
1909 try_reuse = TRUE;
1910 else
1911 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1913 http_release_netconn(req, try_reuse);
1916 static BOOL HTTP_KeepAlive(http_request_t *request)
1918 WCHAR szVersion[10];
1919 WCHAR szConnectionResponse[20];
1920 DWORD dwBufferSize = sizeof(szVersion);
1921 BOOL keepalive = FALSE;
1923 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1924 * the connection is keep-alive by default */
1925 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1926 && !strcmpiW(szVersion, g_szHttp1_1))
1928 keepalive = TRUE;
1931 dwBufferSize = sizeof(szConnectionResponse);
1932 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1933 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1935 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1938 return keepalive;
1941 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1943 http_request_t *req = (http_request_t*)hdr;
1945 drain_content(req);
1948 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1950 http_request_t *req = (http_request_t*)hdr;
1952 switch(option) {
1953 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1955 http_session_t *session = req->session;
1956 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1958 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1960 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1961 return ERROR_INSUFFICIENT_BUFFER;
1962 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1963 /* FIXME: can't get a SOCKET from our connection since we don't use
1964 * winsock
1966 info->Socket = 0;
1967 /* FIXME: get source port from req->netConnection */
1968 info->SourcePort = 0;
1969 info->DestPort = session->hostPort;
1970 info->Flags = 0;
1971 if (HTTP_KeepAlive(req))
1972 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1973 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1974 info->Flags |= IDSI_FLAG_PROXY;
1975 if (req->netconn->useSSL)
1976 info->Flags |= IDSI_FLAG_SECURE;
1978 return ERROR_SUCCESS;
1981 case INTERNET_OPTION_SECURITY_FLAGS:
1983 DWORD flags;
1985 if (*size < sizeof(ULONG))
1986 return ERROR_INSUFFICIENT_BUFFER;
1988 *size = sizeof(DWORD);
1989 flags = 0;
1990 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1991 flags |= SECURITY_FLAG_SECURE;
1992 flags |= req->security_flags;
1993 if(req->netconn) {
1994 int bits = NETCON_GetCipherStrength(req->netconn);
1995 if (bits >= 128)
1996 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1997 else if (bits >= 56)
1998 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1999 else
2000 flags |= SECURITY_FLAG_STRENGTH_WEAK;
2002 *(DWORD *)buffer = flags;
2003 return ERROR_SUCCESS;
2006 case INTERNET_OPTION_HANDLE_TYPE:
2007 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2009 if (*size < sizeof(ULONG))
2010 return ERROR_INSUFFICIENT_BUFFER;
2012 *size = sizeof(DWORD);
2013 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2014 return ERROR_SUCCESS;
2016 case INTERNET_OPTION_URL: {
2017 WCHAR url[INTERNET_MAX_URL_LENGTH];
2018 HTTPHEADERW *host;
2019 DWORD len;
2020 WCHAR *pch;
2022 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2024 TRACE("INTERNET_OPTION_URL\n");
2026 host = HTTP_GetHeader(req, hostW);
2027 strcpyW(url, httpW);
2028 strcatW(url, host->lpszValue);
2029 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2030 *pch = 0;
2031 strcatW(url, req->path);
2033 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2035 if(unicode) {
2036 len = (strlenW(url)+1) * sizeof(WCHAR);
2037 if(*size < len)
2038 return ERROR_INSUFFICIENT_BUFFER;
2040 *size = len;
2041 strcpyW(buffer, url);
2042 return ERROR_SUCCESS;
2043 }else {
2044 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2045 if(len > *size)
2046 return ERROR_INSUFFICIENT_BUFFER;
2048 *size = len;
2049 return ERROR_SUCCESS;
2053 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2054 INTERNET_CACHE_ENTRY_INFOW *info;
2055 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2056 WCHAR url[INTERNET_MAX_URL_LENGTH];
2057 DWORD nbytes, error;
2058 BOOL ret;
2060 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2062 if (*size < sizeof(*ts))
2064 *size = sizeof(*ts);
2065 return ERROR_INSUFFICIENT_BUFFER;
2067 nbytes = 0;
2068 HTTP_GetRequestURL(req, url);
2069 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2070 error = GetLastError();
2071 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2073 if (!(info = heap_alloc(nbytes)))
2074 return ERROR_OUTOFMEMORY;
2076 GetUrlCacheEntryInfoW(url, info, &nbytes);
2078 ts->ftExpires = info->ExpireTime;
2079 ts->ftLastModified = info->LastModifiedTime;
2081 heap_free(info);
2082 *size = sizeof(*ts);
2083 return ERROR_SUCCESS;
2085 return error;
2088 case INTERNET_OPTION_DATAFILE_NAME: {
2089 DWORD req_size;
2091 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2093 if(!req->cacheFile) {
2094 *size = 0;
2095 return ERROR_INTERNET_ITEM_NOT_FOUND;
2098 if(unicode) {
2099 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2100 if(*size < req_size)
2101 return ERROR_INSUFFICIENT_BUFFER;
2103 *size = req_size;
2104 memcpy(buffer, req->cacheFile, *size);
2105 return ERROR_SUCCESS;
2106 }else {
2107 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2108 if (req_size > *size)
2109 return ERROR_INSUFFICIENT_BUFFER;
2111 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2112 -1, buffer, *size, NULL, NULL);
2113 return ERROR_SUCCESS;
2117 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2118 PCCERT_CONTEXT context;
2120 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2121 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2122 return ERROR_INSUFFICIENT_BUFFER;
2125 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2126 if(context) {
2127 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2128 DWORD len;
2130 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2131 info->ftExpiry = context->pCertInfo->NotAfter;
2132 info->ftStart = context->pCertInfo->NotBefore;
2133 len = CertNameToStrA(context->dwCertEncodingType,
2134 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2135 info->lpszSubjectInfo = LocalAlloc(0, len);
2136 if(info->lpszSubjectInfo)
2137 CertNameToStrA(context->dwCertEncodingType,
2138 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2139 info->lpszSubjectInfo, len);
2140 len = CertNameToStrA(context->dwCertEncodingType,
2141 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2142 info->lpszIssuerInfo = LocalAlloc(0, len);
2143 if(info->lpszIssuerInfo)
2144 CertNameToStrA(context->dwCertEncodingType,
2145 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2146 info->lpszIssuerInfo, len);
2147 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2148 CertFreeCertificateContext(context);
2149 return ERROR_SUCCESS;
2154 return INET_QueryOption(hdr, option, buffer, size, unicode);
2157 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2159 http_request_t *req = (http_request_t*)hdr;
2161 switch(option) {
2162 case INTERNET_OPTION_SECURITY_FLAGS:
2164 DWORD flags;
2166 if (!buffer || size != sizeof(DWORD))
2167 return ERROR_INVALID_PARAMETER;
2168 flags = *(DWORD *)buffer;
2169 TRACE("%08x\n", flags);
2170 req->security_flags = flags;
2171 if(req->netconn)
2172 req->netconn->security_flags = flags;
2173 return ERROR_SUCCESS;
2175 case INTERNET_OPTION_SEND_TIMEOUT:
2176 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2177 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2179 if (size != sizeof(DWORD))
2180 return ERROR_INVALID_PARAMETER;
2182 if(!req->netconn) {
2183 FIXME("unsupported without active connection\n");
2184 return ERROR_SUCCESS;
2187 return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
2188 *(DWORD*)buffer);
2190 case INTERNET_OPTION_USERNAME:
2191 heap_free(req->session->userName);
2192 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2193 return ERROR_SUCCESS;
2195 case INTERNET_OPTION_PASSWORD:
2196 heap_free(req->session->password);
2197 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2198 return ERROR_SUCCESS;
2199 case INTERNET_OPTION_HTTP_DECODING:
2200 if(size != sizeof(BOOL))
2201 return ERROR_INVALID_PARAMETER;
2202 req->decoding = *(BOOL*)buffer;
2203 return ERROR_SUCCESS;
2206 return ERROR_INTERNET_INVALID_OPTION;
2209 /* read some more data into the read buffer (the read section must be held) */
2210 static DWORD read_more_data( http_request_t *req, int maxlen )
2212 DWORD res;
2213 int len;
2215 if (req->read_pos)
2217 /* move existing data to the start of the buffer */
2218 if(req->read_size)
2219 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2220 req->read_pos = 0;
2223 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2225 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2226 maxlen - req->read_size, 0, &len );
2227 if(res == ERROR_SUCCESS)
2228 req->read_size += len;
2230 return res;
2233 /* remove some amount of data from the read buffer (the read section must be held) */
2234 static void remove_data( http_request_t *req, int count )
2236 if (!(req->read_size -= count)) req->read_pos = 0;
2237 else req->read_pos += count;
2240 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2242 int count, bytes_read, pos = 0;
2243 DWORD res;
2245 EnterCriticalSection( &req->read_section );
2246 for (;;)
2248 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2250 if (eol)
2252 count = eol - (req->read_buf + req->read_pos);
2253 bytes_read = count + 1;
2255 else count = bytes_read = req->read_size;
2257 count = min( count, *len - pos );
2258 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2259 pos += count;
2260 remove_data( req, bytes_read );
2261 if (eol) break;
2263 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2265 *len = 0;
2266 TRACE( "returning empty string %u\n", res);
2267 LeaveCriticalSection( &req->read_section );
2268 INTERNET_SetLastError(res);
2269 return FALSE;
2272 LeaveCriticalSection( &req->read_section );
2274 if (pos < *len)
2276 if (pos && buffer[pos - 1] == '\r') pos--;
2277 *len = pos + 1;
2279 buffer[*len - 1] = 0;
2280 TRACE( "returning %s\n", debugstr_a(buffer));
2281 return TRUE;
2284 /* check if we have reached the end of the data to read (the read section must be held) */
2285 static BOOL end_of_read_data( http_request_t *req )
2287 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2290 /* fetch some more data into the read buffer (the read section must be held) */
2291 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2293 DWORD res, read=0;
2295 if(req->read_size == sizeof(req->read_buf))
2296 return ERROR_SUCCESS;
2298 if(req->read_pos) {
2299 if(req->read_size)
2300 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2301 req->read_pos = 0;
2304 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2305 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2306 req->read_size += read;
2308 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2309 if(read_bytes)
2310 *read_bytes = read;
2311 return res;
2314 /* return the size of data available to be read immediately (the read section must be held) */
2315 static DWORD get_avail_data( http_request_t *req )
2317 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2320 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2322 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2323 DWORD avail = 0;
2325 if(req->netconn)
2326 NETCON_query_data_available(req->netconn, &avail);
2327 return netconn_stream->content_length == ~0u
2328 ? avail
2329 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2332 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2334 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2335 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2338 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2339 DWORD *read, read_mode_t read_mode)
2341 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2342 int len = 0;
2344 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2346 if(read_mode == READMODE_NOBLOCK)
2347 size = min(size, netconn_get_avail_data(stream, req));
2349 if(size && req->netconn) {
2350 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2351 len = 0;
2352 if(!len)
2353 netconn_stream->content_length = netconn_stream->content_read;
2356 netconn_stream->content_read += *read = len;
2357 TRACE("read %u bytes\n", len);
2358 return ERROR_SUCCESS;
2361 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2363 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2364 BYTE buf[1024];
2365 DWORD avail;
2366 int len;
2368 if(netconn_end_of_data(stream, req))
2369 return TRUE;
2371 do {
2372 avail = netconn_get_avail_data(stream, req);
2373 if(!avail)
2374 return FALSE;
2376 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2377 return FALSE;
2379 netconn_stream->content_read += len;
2380 }while(netconn_stream->content_read < netconn_stream->content_length);
2382 return TRUE;
2385 static void netconn_destroy(data_stream_t *stream)
2389 static const data_stream_vtbl_t netconn_stream_vtbl = {
2390 netconn_get_avail_data,
2391 netconn_end_of_data,
2392 netconn_read,
2393 netconn_drain_content,
2394 netconn_destroy
2397 /* read some more data into the read buffer (the read section must be held) */
2398 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2400 DWORD res;
2401 int len;
2403 if (stream->buf_pos)
2405 /* move existing data to the start of the buffer */
2406 if(stream->buf_size)
2407 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2408 stream->buf_pos = 0;
2411 if (maxlen == -1) maxlen = sizeof(stream->buf);
2413 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2414 maxlen - stream->buf_size, 0, &len );
2415 if(res == ERROR_SUCCESS)
2416 stream->buf_size += len;
2418 return res;
2421 /* remove some amount of data from the read buffer (the read section must be held) */
2422 static void remove_chunked_data(chunked_stream_t *stream, int count)
2424 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2425 else stream->buf_pos += count;
2428 /* discard data contents until we reach end of line (the read section must be held) */
2429 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2431 DWORD res;
2435 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2436 if (eol)
2438 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2439 break;
2441 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2442 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2443 } while (stream->buf_size);
2444 return ERROR_SUCCESS;
2447 /* read the size of the next chunk (the read section must be held) */
2448 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2450 /* TODOO */
2451 DWORD chunk_size = 0, res;
2453 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2454 return res;
2456 for (;;)
2458 while (stream->buf_size)
2460 char ch = stream->buf[stream->buf_pos];
2461 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2462 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2463 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2464 else if (ch == ';' || ch == '\r' || ch == '\n')
2466 TRACE( "reading %u byte chunk\n", chunk_size );
2467 stream->chunk_size = chunk_size;
2468 req->contentLength += chunk_size;
2469 return discard_chunked_eol(stream, req);
2471 remove_chunked_data(stream, 1);
2473 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2474 if (!stream->buf_size)
2476 stream->chunk_size = 0;
2477 return ERROR_SUCCESS;
2482 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2484 /* Allow reading only from read buffer */
2485 return 0;
2488 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2490 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2491 return !chunked_stream->chunk_size;
2494 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2495 DWORD *read, read_mode_t read_mode)
2497 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2498 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2500 if(chunked_stream->chunk_size == ~0u) {
2501 res = start_next_chunk(chunked_stream, req);
2502 if(res != ERROR_SUCCESS)
2503 return res;
2506 while(size && chunked_stream->chunk_size) {
2507 if(chunked_stream->buf_size) {
2508 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2510 /* this could block */
2511 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2512 break;
2514 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2515 remove_chunked_data(chunked_stream, read_bytes);
2516 }else {
2517 read_bytes = min(size, chunked_stream->chunk_size);
2519 if(read_mode == READMODE_NOBLOCK) {
2520 DWORD avail;
2522 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2523 break;
2524 if(read_bytes > avail)
2525 read_bytes = avail;
2527 /* this could block */
2528 if(read_bytes == chunked_stream->chunk_size)
2529 break;
2532 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2533 if(res != ERROR_SUCCESS)
2534 break;
2537 chunked_stream->chunk_size -= read_bytes;
2538 size -= read_bytes;
2539 ret_read += read_bytes;
2540 if(!chunked_stream->chunk_size) {
2541 assert(read_mode != READMODE_NOBLOCK);
2542 res = start_next_chunk(chunked_stream, req);
2543 if(res != ERROR_SUCCESS)
2544 break;
2547 if(read_mode == READMODE_ASYNC)
2548 read_mode = READMODE_NOBLOCK;
2551 TRACE("read %u bytes\n", ret_read);
2552 *read = ret_read;
2553 return res;
2556 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2558 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2560 /* FIXME: we can do better */
2561 return !chunked_stream->chunk_size;
2564 static void chunked_destroy(data_stream_t *stream)
2566 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2567 heap_free(chunked_stream);
2570 static const data_stream_vtbl_t chunked_stream_vtbl = {
2571 chunked_get_avail_data,
2572 chunked_end_of_data,
2573 chunked_read,
2574 chunked_drain_content,
2575 chunked_destroy
2578 /* set the request content length based on the headers */
2579 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2581 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2582 WCHAR encoding[20];
2583 DWORD size;
2585 if(status_code == HTTP_STATUS_NO_CONTENT) {
2586 request->contentLength = request->netconn_stream.content_length = 0;
2587 return ERROR_SUCCESS;
2590 size = sizeof(request->contentLength);
2591 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2592 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2593 request->contentLength = ~0u;
2594 request->netconn_stream.content_length = request->contentLength;
2595 request->netconn_stream.content_read = request->read_size;
2597 size = sizeof(encoding);
2598 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2599 !strcmpiW(encoding, szChunked))
2601 chunked_stream_t *chunked_stream;
2603 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2604 if(!chunked_stream)
2605 return ERROR_OUTOFMEMORY;
2607 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2608 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2609 chunked_stream->chunk_size = ~0u;
2611 if(request->read_size) {
2612 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2613 chunked_stream->buf_size = request->read_size;
2614 request->read_size = request->read_pos = 0;
2617 request->data_stream = &chunked_stream->data_stream;
2618 request->contentLength = ~0u;
2619 request->read_chunked = TRUE;
2622 if(request->decoding) {
2623 int encoding_idx;
2625 static const WCHAR gzipW[] = {'g','z','i','p',0};
2627 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2628 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2629 return init_gzip_stream(request);
2632 return ERROR_SUCCESS;
2635 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2637 INTERNET_ASYNC_RESULT iar;
2639 iar.dwResult = result;
2640 iar.dwError = error;
2642 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2643 sizeof(INTERNET_ASYNC_RESULT));
2646 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2648 DWORD res, read = 0, avail = 0;
2649 read_mode_t mode;
2651 TRACE("%p\n", req);
2653 EnterCriticalSection( &req->read_section );
2655 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2656 res = refill_read_buffer(req, mode, &read);
2657 if(res == ERROR_SUCCESS && !first_notif)
2658 avail = get_avail_data(req);
2660 LeaveCriticalSection( &req->read_section );
2662 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2663 WARN("res %u read %u, closing connection\n", res, read);
2664 http_release_netconn(req, FALSE);
2667 if(res == ERROR_SUCCESS)
2668 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2669 else
2670 send_request_complete(req, 0, res);
2673 /* read data from the http connection (the read section must be held) */
2674 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2676 DWORD current_read = 0, ret_read = 0;
2677 read_mode_t read_mode;
2678 DWORD res = ERROR_SUCCESS;
2680 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2682 EnterCriticalSection( &req->read_section );
2684 if(req->read_size) {
2685 ret_read = min(size, req->read_size);
2686 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2687 req->read_size -= ret_read;
2688 req->read_pos += ret_read;
2689 if(read_mode == READMODE_ASYNC)
2690 read_mode = READMODE_NOBLOCK;
2693 if(ret_read < size) {
2694 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2695 ret_read += current_read;
2698 LeaveCriticalSection( &req->read_section );
2700 *read = ret_read;
2701 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2703 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2704 BOOL res;
2705 DWORD written;
2707 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2708 if(!res)
2709 WARN("WriteFile failed: %u\n", GetLastError());
2712 if(size && !ret_read)
2713 http_release_netconn(req, res == ERROR_SUCCESS);
2715 return res;
2719 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2721 http_request_t *req = (http_request_t*)hdr;
2722 DWORD res;
2724 EnterCriticalSection( &req->read_section );
2725 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2726 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2728 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2729 if(res == ERROR_SUCCESS)
2730 res = hdr->dwError;
2731 LeaveCriticalSection( &req->read_section );
2733 return res;
2736 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2738 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2739 http_request_t *req = (http_request_t*)workRequest->hdr;
2740 DWORD res;
2742 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2744 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2745 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2747 send_request_complete(req, res == ERROR_SUCCESS, res);
2750 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2751 DWORD flags, DWORD_PTR context)
2753 http_request_t *req = (http_request_t*)hdr;
2754 DWORD res, size, read, error = ERROR_SUCCESS;
2756 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2757 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2759 if (buffers->dwStructSize != sizeof(*buffers))
2760 return ERROR_INVALID_PARAMETER;
2762 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2764 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2766 WORKREQUEST workRequest;
2768 if (TryEnterCriticalSection( &req->read_section ))
2770 if (get_avail_data(req))
2772 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2773 &buffers->dwBufferLength, FALSE);
2774 size = buffers->dwBufferLength;
2775 LeaveCriticalSection( &req->read_section );
2776 goto done;
2778 LeaveCriticalSection( &req->read_section );
2781 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2782 workRequest.hdr = WININET_AddRef(&req->hdr);
2783 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2785 INTERNET_AsyncCall(&workRequest);
2787 return ERROR_IO_PENDING;
2790 read = 0;
2791 size = buffers->dwBufferLength;
2793 EnterCriticalSection( &req->read_section );
2794 if(hdr->dwError == ERROR_SUCCESS)
2795 hdr->dwError = INTERNET_HANDLE_IN_USE;
2796 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2797 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2799 while(1) {
2800 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2801 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2802 if(res != ERROR_SUCCESS)
2803 break;
2805 read += buffers->dwBufferLength;
2806 if(read == size || end_of_read_data(req))
2807 break;
2809 LeaveCriticalSection( &req->read_section );
2811 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2812 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2813 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2814 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2816 EnterCriticalSection( &req->read_section );
2819 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2820 hdr->dwError = ERROR_SUCCESS;
2821 else
2822 error = hdr->dwError;
2824 LeaveCriticalSection( &req->read_section );
2825 size = buffers->dwBufferLength;
2826 buffers->dwBufferLength = read;
2828 done:
2829 if (res == ERROR_SUCCESS) {
2830 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2831 &size, sizeof(size));
2834 return res==ERROR_SUCCESS ? error : res;
2837 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2839 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2840 http_request_t *req = (http_request_t*)workRequest->hdr;
2841 DWORD res;
2843 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2845 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2846 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2848 send_request_complete(req, res == ERROR_SUCCESS, res);
2851 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2852 DWORD flags, DWORD_PTR context)
2855 http_request_t *req = (http_request_t*)hdr;
2856 DWORD res, size, read, error = ERROR_SUCCESS;
2858 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2859 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2861 if (buffers->dwStructSize != sizeof(*buffers))
2862 return ERROR_INVALID_PARAMETER;
2864 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2866 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2868 WORKREQUEST workRequest;
2870 if (TryEnterCriticalSection( &req->read_section ))
2872 if (get_avail_data(req))
2874 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2875 &buffers->dwBufferLength, FALSE);
2876 size = buffers->dwBufferLength;
2877 LeaveCriticalSection( &req->read_section );
2878 goto done;
2880 LeaveCriticalSection( &req->read_section );
2883 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2884 workRequest.hdr = WININET_AddRef(&req->hdr);
2885 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2887 INTERNET_AsyncCall(&workRequest);
2889 return ERROR_IO_PENDING;
2892 read = 0;
2893 size = buffers->dwBufferLength;
2895 EnterCriticalSection( &req->read_section );
2896 if(hdr->dwError == ERROR_SUCCESS)
2897 hdr->dwError = INTERNET_HANDLE_IN_USE;
2898 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2899 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2901 while(1) {
2902 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2903 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2904 if(res != ERROR_SUCCESS)
2905 break;
2907 read += buffers->dwBufferLength;
2908 if(read == size || end_of_read_data(req))
2909 break;
2911 LeaveCriticalSection( &req->read_section );
2913 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2914 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2915 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2916 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2918 EnterCriticalSection( &req->read_section );
2921 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2922 hdr->dwError = ERROR_SUCCESS;
2923 else
2924 error = hdr->dwError;
2926 LeaveCriticalSection( &req->read_section );
2927 size = buffers->dwBufferLength;
2928 buffers->dwBufferLength = read;
2930 done:
2931 if (res == ERROR_SUCCESS) {
2932 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2933 &size, sizeof(size));
2936 return res==ERROR_SUCCESS ? error : res;
2939 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2941 DWORD res;
2942 http_request_t *request = (http_request_t*)hdr;
2944 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2946 *written = 0;
2947 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2948 if (res == ERROR_SUCCESS)
2949 request->bytesWritten += *written;
2951 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2952 return res;
2955 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2957 http_request_t *req = (http_request_t*)workRequest->hdr;
2959 HTTP_ReceiveRequestData(req, FALSE);
2962 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2964 http_request_t *req = (http_request_t*)hdr;
2966 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2968 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2970 WORKREQUEST workRequest;
2972 /* never wait, if we can't enter the section we queue an async request right away */
2973 if (TryEnterCriticalSection( &req->read_section ))
2975 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2976 if ((*available = get_avail_data( req ))) goto done;
2977 if (end_of_read_data( req )) goto done;
2978 LeaveCriticalSection( &req->read_section );
2981 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2982 workRequest.hdr = WININET_AddRef( &req->hdr );
2984 INTERNET_AsyncCall(&workRequest);
2986 return ERROR_IO_PENDING;
2989 EnterCriticalSection( &req->read_section );
2991 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2993 refill_read_buffer( req, READMODE_ASYNC, NULL );
2994 *available = get_avail_data( req );
2997 done:
2998 LeaveCriticalSection( &req->read_section );
3000 TRACE( "returning %u\n", *available );
3001 return ERROR_SUCCESS;
3004 static const object_vtbl_t HTTPREQVtbl = {
3005 HTTPREQ_Destroy,
3006 HTTPREQ_CloseConnection,
3007 HTTPREQ_QueryOption,
3008 HTTPREQ_SetOption,
3009 HTTPREQ_ReadFile,
3010 HTTPREQ_ReadFileExA,
3011 HTTPREQ_ReadFileExW,
3012 HTTPREQ_WriteFile,
3013 HTTPREQ_QueryDataAvailable,
3014 NULL
3017 /***********************************************************************
3018 * HTTP_HttpOpenRequestW (internal)
3020 * Open a HTTP request handle
3022 * RETURNS
3023 * HINTERNET a HTTP request handle on success
3024 * NULL on failure
3027 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3028 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3029 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3030 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3032 appinfo_t *hIC = session->appInfo;
3033 http_request_t *request;
3034 DWORD len, res = ERROR_SUCCESS;
3036 TRACE("-->\n");
3038 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3039 if(!request)
3040 return ERROR_OUTOFMEMORY;
3042 request->hdr.htype = WH_HHTTPREQ;
3043 request->hdr.dwFlags = dwFlags;
3044 request->hdr.dwContext = dwContext;
3045 request->contentLength = ~0u;
3047 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3048 request->data_stream = &request->netconn_stream.data_stream;
3050 InitializeCriticalSection( &request->read_section );
3051 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3053 WININET_AddRef( &session->hdr );
3054 request->session = session;
3055 list_add_head( &session->hdr.children, &request->hdr.entry );
3057 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3058 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3059 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3060 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3062 if (lpszObjectName && *lpszObjectName) {
3063 HRESULT rc;
3065 len = 0;
3066 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3067 if (rc != E_POINTER)
3068 len = strlenW(lpszObjectName)+1;
3069 request->path = heap_alloc(len*sizeof(WCHAR));
3070 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3071 URL_ESCAPE_SPACES_ONLY);
3072 if (rc != S_OK)
3074 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3075 strcpyW(request->path,lpszObjectName);
3077 }else {
3078 static const WCHAR slashW[] = {'/',0};
3080 request->path = heap_strdupW(slashW);
3083 if (lpszReferrer && *lpszReferrer)
3084 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3086 if (lpszAcceptTypes)
3088 int i;
3089 for (i = 0; lpszAcceptTypes[i]; i++)
3091 if (!*lpszAcceptTypes[i]) continue;
3092 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3093 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3094 HTTP_ADDHDR_FLAG_REQ |
3095 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3099 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3100 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3102 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3103 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3104 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3106 WCHAR *host_name;
3108 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3110 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3111 if (!host_name) {
3112 res = ERROR_OUTOFMEMORY;
3113 goto lend;
3116 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3117 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3118 heap_free(host_name);
3120 else
3121 HTTP_ProcessHeader(request, hostW, session->hostName,
3122 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3124 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3125 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3126 INTERNET_DEFAULT_HTTPS_PORT :
3127 INTERNET_DEFAULT_HTTP_PORT);
3129 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3130 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3131 INTERNET_DEFAULT_HTTPS_PORT :
3132 INTERNET_DEFAULT_HTTP_PORT);
3134 if (hIC->proxy && hIC->proxy[0])
3135 HTTP_DealWithProxy( hIC, session, request );
3137 INTERNET_SendCallback(&session->hdr, dwContext,
3138 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3139 sizeof(HINTERNET));
3141 lend:
3142 TRACE("<-- %u (%p)\n", res, request);
3144 if(res != ERROR_SUCCESS) {
3145 WININET_Release( &request->hdr );
3146 *ret = NULL;
3147 return res;
3150 *ret = request->hdr.hInternet;
3151 return ERROR_SUCCESS;
3154 /***********************************************************************
3155 * HttpOpenRequestW (WININET.@)
3157 * Open a HTTP request handle
3159 * RETURNS
3160 * HINTERNET a HTTP request handle on success
3161 * NULL on failure
3164 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3165 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3166 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3167 DWORD dwFlags, DWORD_PTR dwContext)
3169 http_session_t *session;
3170 HINTERNET handle = NULL;
3171 DWORD res;
3173 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3174 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3175 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3176 dwFlags, dwContext);
3177 if(lpszAcceptTypes!=NULL)
3179 int i;
3180 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3181 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3184 session = (http_session_t*) get_handle_object( hHttpSession );
3185 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3187 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3188 goto lend;
3192 * My tests seem to show that the windows version does not
3193 * become asynchronous until after this point. And anyhow
3194 * if this call was asynchronous then how would you get the
3195 * necessary HINTERNET pointer returned by this function.
3198 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3199 lpszVersion, lpszReferrer, lpszAcceptTypes,
3200 dwFlags, dwContext, &handle);
3201 lend:
3202 if( session )
3203 WININET_Release( &session->hdr );
3204 TRACE("returning %p\n", handle);
3205 if(res != ERROR_SUCCESS)
3206 SetLastError(res);
3207 return handle;
3210 static const LPCWSTR header_lookup[] = {
3211 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3212 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3213 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3214 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3215 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3216 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3217 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3218 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3219 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3220 szDate, /* HTTP_QUERY_DATE = 9 */
3221 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3222 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3223 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3224 szURI, /* HTTP_QUERY_URI = 13 */
3225 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3226 NULL, /* HTTP_QUERY_COST = 15 */
3227 NULL, /* HTTP_QUERY_LINK = 16 */
3228 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3229 NULL, /* HTTP_QUERY_VERSION = 18 */
3230 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3231 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3232 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3233 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3234 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3235 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3236 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3237 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3238 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3239 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3240 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3241 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3242 NULL, /* HTTP_QUERY_FROM = 31 */
3243 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3244 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3245 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3246 szReferer, /* HTTP_QUERY_REFERER = 35 */
3247 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3248 szServer, /* HTTP_QUERY_SERVER = 37 */
3249 NULL, /* HTTP_TITLE = 38 */
3250 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3251 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3252 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3253 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3254 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3255 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3256 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3257 NULL, /* HTTP_QUERY_REFRESH = 46 */
3258 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3259 szAge, /* HTTP_QUERY_AGE = 48 */
3260 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3261 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3262 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3263 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3264 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3265 szETag, /* HTTP_QUERY_ETAG = 54 */
3266 hostW, /* HTTP_QUERY_HOST = 55 */
3267 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3268 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3269 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3270 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3271 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3272 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3273 szRange, /* HTTP_QUERY_RANGE = 62 */
3274 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3275 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3276 szVary, /* HTTP_QUERY_VARY = 65 */
3277 szVia, /* HTTP_QUERY_VIA = 66 */
3278 szWarning, /* HTTP_QUERY_WARNING = 67 */
3279 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3280 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3281 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3284 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3286 /***********************************************************************
3287 * HTTP_HttpQueryInfoW (internal)
3289 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3290 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3292 LPHTTPHEADERW lphttpHdr = NULL;
3293 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3294 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3295 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3296 INT index = -1;
3298 /* Find requested header structure */
3299 switch (level)
3301 case HTTP_QUERY_CUSTOM:
3302 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3303 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3304 break;
3305 case HTTP_QUERY_RAW_HEADERS_CRLF:
3307 LPWSTR headers;
3308 DWORD len = 0;
3309 DWORD res = ERROR_INVALID_PARAMETER;
3311 if (request_only)
3312 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3313 else
3314 headers = request->rawHeaders;
3316 if (headers)
3317 len = strlenW(headers) * sizeof(WCHAR);
3319 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3321 len += sizeof(WCHAR);
3322 res = ERROR_INSUFFICIENT_BUFFER;
3324 else if (lpBuffer)
3326 if (headers)
3327 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3328 else
3330 len = strlenW(szCrLf) * sizeof(WCHAR);
3331 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3333 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3334 res = ERROR_SUCCESS;
3336 *lpdwBufferLength = len;
3338 if (request_only) heap_free(headers);
3339 return res;
3341 case HTTP_QUERY_RAW_HEADERS:
3343 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3344 DWORD i, size = 0;
3345 LPWSTR pszString = lpBuffer;
3347 for (i = 0; ppszRawHeaderLines[i]; i++)
3348 size += strlenW(ppszRawHeaderLines[i]) + 1;
3350 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3352 HTTP_FreeTokens(ppszRawHeaderLines);
3353 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3354 return ERROR_INSUFFICIENT_BUFFER;
3356 if (pszString)
3358 for (i = 0; ppszRawHeaderLines[i]; i++)
3360 DWORD len = strlenW(ppszRawHeaderLines[i]);
3361 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3362 pszString += len+1;
3364 *pszString = '\0';
3365 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3367 *lpdwBufferLength = size * sizeof(WCHAR);
3368 HTTP_FreeTokens(ppszRawHeaderLines);
3370 return ERROR_SUCCESS;
3372 case HTTP_QUERY_STATUS_TEXT:
3373 if (request->statusText)
3375 DWORD len = strlenW(request->statusText);
3376 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3378 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3379 return ERROR_INSUFFICIENT_BUFFER;
3381 if (lpBuffer)
3383 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3384 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3386 *lpdwBufferLength = len * sizeof(WCHAR);
3387 return ERROR_SUCCESS;
3389 break;
3390 case HTTP_QUERY_VERSION:
3391 if (request->version)
3393 DWORD len = strlenW(request->version);
3394 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3396 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3397 return ERROR_INSUFFICIENT_BUFFER;
3399 if (lpBuffer)
3401 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3402 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3404 *lpdwBufferLength = len * sizeof(WCHAR);
3405 return ERROR_SUCCESS;
3407 break;
3408 case HTTP_QUERY_CONTENT_ENCODING:
3409 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3410 requested_index,request_only);
3411 break;
3412 default:
3413 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3415 if (level < LAST_TABLE_HEADER && header_lookup[level])
3416 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3417 requested_index,request_only);
3420 if (index >= 0)
3421 lphttpHdr = &request->custHeaders[index];
3423 /* Ensure header satisfies requested attributes */
3424 if (!lphttpHdr ||
3425 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3426 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3428 return ERROR_HTTP_HEADER_NOT_FOUND;
3431 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3433 /* coalesce value to requested type */
3434 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3436 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3437 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3439 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3441 time_t tmpTime;
3442 struct tm tmpTM;
3443 SYSTEMTIME *STHook;
3445 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3447 tmpTM = *gmtime(&tmpTime);
3448 STHook = (SYSTEMTIME *)lpBuffer;
3449 STHook->wDay = tmpTM.tm_mday;
3450 STHook->wHour = tmpTM.tm_hour;
3451 STHook->wMilliseconds = 0;
3452 STHook->wMinute = tmpTM.tm_min;
3453 STHook->wDayOfWeek = tmpTM.tm_wday;
3454 STHook->wMonth = tmpTM.tm_mon + 1;
3455 STHook->wSecond = tmpTM.tm_sec;
3456 STHook->wYear = tmpTM.tm_year;
3458 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3459 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3460 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3462 else if (lphttpHdr->lpszValue)
3464 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3466 if (len > *lpdwBufferLength)
3468 *lpdwBufferLength = len;
3469 return ERROR_INSUFFICIENT_BUFFER;
3471 if (lpBuffer)
3473 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3474 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3476 *lpdwBufferLength = len - sizeof(WCHAR);
3478 return ERROR_SUCCESS;
3481 /***********************************************************************
3482 * HttpQueryInfoW (WININET.@)
3484 * Queries for information about an HTTP request
3486 * RETURNS
3487 * TRUE on success
3488 * FALSE on failure
3491 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3492 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3494 http_request_t *request;
3495 DWORD res;
3497 if (TRACE_ON(wininet)) {
3498 #define FE(x) { x, #x }
3499 static const wininet_flag_info query_flags[] = {
3500 FE(HTTP_QUERY_MIME_VERSION),
3501 FE(HTTP_QUERY_CONTENT_TYPE),
3502 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3503 FE(HTTP_QUERY_CONTENT_ID),
3504 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3505 FE(HTTP_QUERY_CONTENT_LENGTH),
3506 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3507 FE(HTTP_QUERY_ALLOW),
3508 FE(HTTP_QUERY_PUBLIC),
3509 FE(HTTP_QUERY_DATE),
3510 FE(HTTP_QUERY_EXPIRES),
3511 FE(HTTP_QUERY_LAST_MODIFIED),
3512 FE(HTTP_QUERY_MESSAGE_ID),
3513 FE(HTTP_QUERY_URI),
3514 FE(HTTP_QUERY_DERIVED_FROM),
3515 FE(HTTP_QUERY_COST),
3516 FE(HTTP_QUERY_LINK),
3517 FE(HTTP_QUERY_PRAGMA),
3518 FE(HTTP_QUERY_VERSION),
3519 FE(HTTP_QUERY_STATUS_CODE),
3520 FE(HTTP_QUERY_STATUS_TEXT),
3521 FE(HTTP_QUERY_RAW_HEADERS),
3522 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3523 FE(HTTP_QUERY_CONNECTION),
3524 FE(HTTP_QUERY_ACCEPT),
3525 FE(HTTP_QUERY_ACCEPT_CHARSET),
3526 FE(HTTP_QUERY_ACCEPT_ENCODING),
3527 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3528 FE(HTTP_QUERY_AUTHORIZATION),
3529 FE(HTTP_QUERY_CONTENT_ENCODING),
3530 FE(HTTP_QUERY_FORWARDED),
3531 FE(HTTP_QUERY_FROM),
3532 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3533 FE(HTTP_QUERY_LOCATION),
3534 FE(HTTP_QUERY_ORIG_URI),
3535 FE(HTTP_QUERY_REFERER),
3536 FE(HTTP_QUERY_RETRY_AFTER),
3537 FE(HTTP_QUERY_SERVER),
3538 FE(HTTP_QUERY_TITLE),
3539 FE(HTTP_QUERY_USER_AGENT),
3540 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3541 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3542 FE(HTTP_QUERY_ACCEPT_RANGES),
3543 FE(HTTP_QUERY_SET_COOKIE),
3544 FE(HTTP_QUERY_COOKIE),
3545 FE(HTTP_QUERY_REQUEST_METHOD),
3546 FE(HTTP_QUERY_REFRESH),
3547 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3548 FE(HTTP_QUERY_AGE),
3549 FE(HTTP_QUERY_CACHE_CONTROL),
3550 FE(HTTP_QUERY_CONTENT_BASE),
3551 FE(HTTP_QUERY_CONTENT_LOCATION),
3552 FE(HTTP_QUERY_CONTENT_MD5),
3553 FE(HTTP_QUERY_CONTENT_RANGE),
3554 FE(HTTP_QUERY_ETAG),
3555 FE(HTTP_QUERY_HOST),
3556 FE(HTTP_QUERY_IF_MATCH),
3557 FE(HTTP_QUERY_IF_NONE_MATCH),
3558 FE(HTTP_QUERY_IF_RANGE),
3559 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3560 FE(HTTP_QUERY_MAX_FORWARDS),
3561 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3562 FE(HTTP_QUERY_RANGE),
3563 FE(HTTP_QUERY_TRANSFER_ENCODING),
3564 FE(HTTP_QUERY_UPGRADE),
3565 FE(HTTP_QUERY_VARY),
3566 FE(HTTP_QUERY_VIA),
3567 FE(HTTP_QUERY_WARNING),
3568 FE(HTTP_QUERY_CUSTOM)
3570 static const wininet_flag_info modifier_flags[] = {
3571 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3572 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3573 FE(HTTP_QUERY_FLAG_NUMBER),
3574 FE(HTTP_QUERY_FLAG_COALESCE)
3576 #undef FE
3577 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3578 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3579 DWORD i;
3581 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3582 TRACE(" Attribute:");
3583 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3584 if (query_flags[i].val == info) {
3585 TRACE(" %s", query_flags[i].name);
3586 break;
3589 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3590 TRACE(" Unknown (%08x)", info);
3593 TRACE(" Modifier:");
3594 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3595 if (modifier_flags[i].val & info_mod) {
3596 TRACE(" %s", modifier_flags[i].name);
3597 info_mod &= ~ modifier_flags[i].val;
3601 if (info_mod) {
3602 TRACE(" Unknown (%08x)", info_mod);
3604 TRACE("\n");
3607 request = (http_request_t*) get_handle_object( hHttpRequest );
3608 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3610 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3611 goto lend;
3614 if (lpBuffer == NULL)
3615 *lpdwBufferLength = 0;
3616 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3617 lpBuffer, lpdwBufferLength, lpdwIndex);
3619 lend:
3620 if( request )
3621 WININET_Release( &request->hdr );
3623 TRACE("%u <--\n", res);
3624 if(res != ERROR_SUCCESS)
3625 SetLastError(res);
3626 return res == ERROR_SUCCESS;
3629 /***********************************************************************
3630 * HttpQueryInfoA (WININET.@)
3632 * Queries for information about an HTTP request
3634 * RETURNS
3635 * TRUE on success
3636 * FALSE on failure
3639 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3640 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3642 BOOL result;
3643 DWORD len;
3644 WCHAR* bufferW;
3646 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3647 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3649 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3650 lpdwBufferLength, lpdwIndex );
3653 if (lpBuffer)
3655 DWORD alloclen;
3656 len = (*lpdwBufferLength)*sizeof(WCHAR);
3657 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3659 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3660 if (alloclen < len)
3661 alloclen = len;
3663 else
3664 alloclen = len;
3665 bufferW = heap_alloc(alloclen);
3666 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3667 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3668 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3669 } else
3671 bufferW = NULL;
3672 len = 0;
3675 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3676 &len, lpdwIndex );
3677 if( result )
3679 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3680 lpBuffer, *lpdwBufferLength, NULL, NULL );
3681 *lpdwBufferLength = len - 1;
3683 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3685 else
3686 /* since the strings being returned from HttpQueryInfoW should be
3687 * only ASCII characters, it is reasonable to assume that all of
3688 * the Unicode characters can be reduced to a single byte */
3689 *lpdwBufferLength = len / sizeof(WCHAR);
3691 heap_free( bufferW );
3692 return result;
3695 /***********************************************************************
3696 * HTTP_GetRedirectURL (internal)
3698 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3700 static WCHAR szHttp[] = {'h','t','t','p',0};
3701 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3702 http_session_t *session = request->session;
3703 URL_COMPONENTSW urlComponents;
3704 DWORD url_length = 0;
3705 LPWSTR orig_url;
3706 LPWSTR combined_url;
3708 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3709 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3710 urlComponents.dwSchemeLength = 0;
3711 urlComponents.lpszHostName = session->hostName;
3712 urlComponents.dwHostNameLength = 0;
3713 urlComponents.nPort = session->hostPort;
3714 urlComponents.lpszUserName = session->userName;
3715 urlComponents.dwUserNameLength = 0;
3716 urlComponents.lpszPassword = NULL;
3717 urlComponents.dwPasswordLength = 0;
3718 urlComponents.lpszUrlPath = request->path;
3719 urlComponents.dwUrlPathLength = 0;
3720 urlComponents.lpszExtraInfo = NULL;
3721 urlComponents.dwExtraInfoLength = 0;
3723 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3724 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3725 return NULL;
3727 orig_url = heap_alloc(url_length);
3729 /* convert from bytes to characters */
3730 url_length = url_length / sizeof(WCHAR) - 1;
3731 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3733 heap_free(orig_url);
3734 return NULL;
3737 url_length = 0;
3738 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3739 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3741 heap_free(orig_url);
3742 return NULL;
3744 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3746 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3748 heap_free(orig_url);
3749 heap_free(combined_url);
3750 return NULL;
3752 heap_free(orig_url);
3753 return combined_url;
3757 /***********************************************************************
3758 * HTTP_HandleRedirect (internal)
3760 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3762 http_session_t *session = request->session;
3763 appinfo_t *hIC = session->appInfo;
3764 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3765 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3766 int index;
3768 if(lpszUrl[0]=='/')
3770 /* if it's an absolute path, keep the same session info */
3771 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3773 else
3775 URL_COMPONENTSW urlComponents;
3776 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3777 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3778 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3779 static WCHAR szHttp[] = {'h','t','t','p',0};
3780 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3782 userName[0] = 0;
3783 hostName[0] = 0;
3784 protocol[0] = 0;
3786 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3787 urlComponents.lpszScheme = protocol;
3788 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3789 urlComponents.lpszHostName = hostName;
3790 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3791 urlComponents.lpszUserName = userName;
3792 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3793 urlComponents.lpszPassword = NULL;
3794 urlComponents.dwPasswordLength = 0;
3795 urlComponents.lpszUrlPath = path;
3796 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3797 urlComponents.lpszExtraInfo = NULL;
3798 urlComponents.dwExtraInfoLength = 0;
3799 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3800 return INTERNET_GetLastError();
3802 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3803 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3805 TRACE("redirect from secure page to non-secure page\n");
3806 /* FIXME: warn about from secure redirect to non-secure page */
3807 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3809 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3810 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3812 TRACE("redirect from non-secure page to secure page\n");
3813 /* FIXME: notify about redirect to secure page */
3814 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3817 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3819 if (lstrlenW(protocol)>4) /*https*/
3820 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3821 else /*http*/
3822 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3825 #if 0
3827 * This upsets redirects to binary files on sourceforge.net
3828 * and gives an html page instead of the target file
3829 * Examination of the HTTP request sent by native wininet.dll
3830 * reveals that it doesn't send a referrer in that case.
3831 * Maybe there's a flag that enables this, or maybe a referrer
3832 * shouldn't be added in case of a redirect.
3835 /* consider the current host as the referrer */
3836 if (session->lpszServerName && *session->lpszServerName)
3837 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3838 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3839 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3840 #endif
3842 heap_free(session->hostName);
3843 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3844 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3846 int len;
3847 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3848 len = lstrlenW(hostName);
3849 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3850 session->hostName = heap_alloc(len*sizeof(WCHAR));
3851 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3853 else
3854 session->hostName = heap_strdupW(hostName);
3856 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3858 heap_free(session->userName);
3859 session->userName = NULL;
3860 if (userName[0])
3861 session->userName = heap_strdupW(userName);
3863 reset_data_stream(request);
3865 if(!using_proxy) {
3866 if(strcmpiW(session->serverName, hostName)) {
3867 heap_free(session->serverName);
3868 session->serverName = heap_strdupW(hostName);
3870 session->serverPort = urlComponents.nPort;
3873 heap_free(request->path);
3874 request->path=NULL;
3875 if (*path)
3877 DWORD needed = 0;
3878 HRESULT rc;
3880 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3881 if (rc != E_POINTER)
3882 needed = strlenW(path)+1;
3883 request->path = heap_alloc(needed*sizeof(WCHAR));
3884 rc = UrlEscapeW(path, request->path, &needed,
3885 URL_ESCAPE_SPACES_ONLY);
3886 if (rc != S_OK)
3888 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3889 strcpyW(request->path,path);
3893 /* Remove custom content-type/length headers on redirects. */
3894 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3895 if (0 <= index)
3896 HTTP_DeleteCustomHeader(request, index);
3897 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3898 if (0 <= index)
3899 HTTP_DeleteCustomHeader(request, index);
3901 return ERROR_SUCCESS;
3904 /***********************************************************************
3905 * HTTP_build_req (internal)
3907 * concatenate all the strings in the request together
3909 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3911 LPCWSTR *t;
3912 LPWSTR str;
3914 for( t = list; *t ; t++ )
3915 len += strlenW( *t );
3916 len++;
3918 str = heap_alloc(len*sizeof(WCHAR));
3919 *str = 0;
3921 for( t = list; *t ; t++ )
3922 strcatW( str, *t );
3924 return str;
3927 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3929 LPWSTR lpszPath;
3930 LPWSTR requestString;
3931 INT len;
3932 INT cnt;
3933 INT responseLen;
3934 char *ascii_req;
3935 DWORD res;
3936 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3937 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3938 http_session_t *session = request->session;
3940 TRACE("\n");
3942 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3943 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3944 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3945 heap_free( lpszPath );
3947 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3948 NULL, 0, NULL, NULL );
3949 len--; /* the nul terminator isn't needed */
3950 ascii_req = heap_alloc(len);
3951 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3952 heap_free( requestString );
3954 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3956 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3957 heap_free( ascii_req );
3958 if (res != ERROR_SUCCESS)
3959 return res;
3961 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3962 if (!responseLen)
3963 return ERROR_HTTP_INVALID_HEADER;
3965 return ERROR_SUCCESS;
3968 static void HTTP_InsertCookies(http_request_t *request)
3970 DWORD cookie_size, size, cnt = 0;
3971 HTTPHEADERW *host;
3972 WCHAR *cookies;
3974 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3976 host = HTTP_GetHeader(request, hostW);
3977 if(!host)
3978 return;
3980 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3981 return;
3983 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
3984 if(!(cookies = heap_alloc(size)))
3985 return;
3987 cnt += sprintfW(cookies, cookieW);
3988 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
3989 strcatW(cookies, szCrLf);
3991 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
3993 heap_free(cookies);
3996 static WORD HTTP_ParseWkday(LPCWSTR day)
3998 static const WCHAR days[7][4] = {{ 's','u','n',0 },
3999 { 'm','o','n',0 },
4000 { 't','u','e',0 },
4001 { 'w','e','d',0 },
4002 { 't','h','u',0 },
4003 { 'f','r','i',0 },
4004 { 's','a','t',0 }};
4005 int i;
4006 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4007 if (!strcmpiW(day, days[i]))
4008 return i;
4010 /* Invalid */
4011 return 7;
4014 static WORD HTTP_ParseMonth(LPCWSTR month)
4016 static const WCHAR jan[] = { 'j','a','n',0 };
4017 static const WCHAR feb[] = { 'f','e','b',0 };
4018 static const WCHAR mar[] = { 'm','a','r',0 };
4019 static const WCHAR apr[] = { 'a','p','r',0 };
4020 static const WCHAR may[] = { 'm','a','y',0 };
4021 static const WCHAR jun[] = { 'j','u','n',0 };
4022 static const WCHAR jul[] = { 'j','u','l',0 };
4023 static const WCHAR aug[] = { 'a','u','g',0 };
4024 static const WCHAR sep[] = { 's','e','p',0 };
4025 static const WCHAR oct[] = { 'o','c','t',0 };
4026 static const WCHAR nov[] = { 'n','o','v',0 };
4027 static const WCHAR dec[] = { 'd','e','c',0 };
4029 if (!strcmpiW(month, jan)) return 1;
4030 if (!strcmpiW(month, feb)) return 2;
4031 if (!strcmpiW(month, mar)) return 3;
4032 if (!strcmpiW(month, apr)) return 4;
4033 if (!strcmpiW(month, may)) return 5;
4034 if (!strcmpiW(month, jun)) return 6;
4035 if (!strcmpiW(month, jul)) return 7;
4036 if (!strcmpiW(month, aug)) return 8;
4037 if (!strcmpiW(month, sep)) return 9;
4038 if (!strcmpiW(month, oct)) return 10;
4039 if (!strcmpiW(month, nov)) return 11;
4040 if (!strcmpiW(month, dec)) return 12;
4041 /* Invalid */
4042 return 0;
4045 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4046 * optionally preceded by whitespace.
4047 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4048 * st, and sets *str to the first character after the time format.
4050 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4052 LPCWSTR ptr = *str;
4053 WCHAR *nextPtr;
4054 unsigned long num;
4056 while (isspaceW(*ptr))
4057 ptr++;
4059 num = strtoulW(ptr, &nextPtr, 10);
4060 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4062 ERR("unexpected time format %s\n", debugstr_w(ptr));
4063 return FALSE;
4065 if (num > 23)
4067 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4068 return FALSE;
4070 ptr = nextPtr + 1;
4071 st->wHour = (WORD)num;
4072 num = strtoulW(ptr, &nextPtr, 10);
4073 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4075 ERR("unexpected time format %s\n", debugstr_w(ptr));
4076 return FALSE;
4078 if (num > 59)
4080 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4081 return FALSE;
4083 ptr = nextPtr + 1;
4084 st->wMinute = (WORD)num;
4085 num = strtoulW(ptr, &nextPtr, 10);
4086 if (!nextPtr || nextPtr <= ptr)
4088 ERR("unexpected time format %s\n", debugstr_w(ptr));
4089 return FALSE;
4091 if (num > 59)
4093 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4094 return FALSE;
4096 ptr = nextPtr + 1;
4097 *str = ptr;
4098 st->wSecond = (WORD)num;
4099 return TRUE;
4102 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4104 static const WCHAR gmt[]= { 'G','M','T',0 };
4105 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4106 LPCWSTR ptr;
4107 SYSTEMTIME st = { 0 };
4108 unsigned long num;
4110 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4111 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4112 *dayPtr = *ptr;
4113 *dayPtr = 0;
4114 st.wDayOfWeek = HTTP_ParseWkday(day);
4115 if (st.wDayOfWeek >= 7)
4117 ERR("unexpected weekday %s\n", debugstr_w(day));
4118 return FALSE;
4121 while (isspaceW(*ptr))
4122 ptr++;
4124 for (monthPtr = month; !isspace(*ptr) &&
4125 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4126 monthPtr++, ptr++)
4127 *monthPtr = *ptr;
4128 *monthPtr = 0;
4129 st.wMonth = HTTP_ParseMonth(month);
4130 if (!st.wMonth || st.wMonth > 12)
4132 ERR("unexpected month %s\n", debugstr_w(month));
4133 return FALSE;
4136 while (isspaceW(*ptr))
4137 ptr++;
4139 num = strtoulW(ptr, &nextPtr, 10);
4140 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4142 ERR("unexpected day %s\n", debugstr_w(ptr));
4143 return FALSE;
4145 ptr = nextPtr;
4146 st.wDay = (WORD)num;
4148 while (isspaceW(*ptr))
4149 ptr++;
4151 if (!HTTP_ParseTime(&st, &ptr))
4152 return FALSE;
4154 while (isspaceW(*ptr))
4155 ptr++;
4157 num = strtoulW(ptr, &nextPtr, 10);
4158 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4160 ERR("unexpected year %s\n", debugstr_w(ptr));
4161 return FALSE;
4163 ptr = nextPtr;
4164 st.wYear = (WORD)num;
4166 while (isspaceW(*ptr))
4167 ptr++;
4169 /* asctime() doesn't report a timezone, but some web servers do, so accept
4170 * with or without GMT.
4172 if (*ptr && strcmpW(ptr, gmt))
4174 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4175 return FALSE;
4177 return SystemTimeToFileTime(&st, ft);
4180 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4182 static const WCHAR gmt[]= { 'G','M','T',0 };
4183 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4184 LPCWSTR ptr;
4185 unsigned long num;
4186 SYSTEMTIME st = { 0 };
4188 ptr = strchrW(value, ',');
4189 if (!ptr)
4190 return FALSE;
4191 if (ptr - value != 3)
4193 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4194 return FALSE;
4196 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4197 day[3] = 0;
4198 st.wDayOfWeek = HTTP_ParseWkday(day);
4199 if (st.wDayOfWeek > 6)
4201 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4202 return FALSE;
4204 ptr++;
4206 while (isspaceW(*ptr))
4207 ptr++;
4209 num = strtoulW(ptr, &nextPtr, 10);
4210 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4212 WARN("unexpected day %s\n", debugstr_w(value));
4213 return FALSE;
4215 ptr = nextPtr;
4216 st.wDay = (WORD)num;
4218 while (isspaceW(*ptr))
4219 ptr++;
4221 for (monthPtr = month; !isspace(*ptr) &&
4222 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4223 monthPtr++, ptr++)
4224 *monthPtr = *ptr;
4225 *monthPtr = 0;
4226 st.wMonth = HTTP_ParseMonth(month);
4227 if (!st.wMonth || st.wMonth > 12)
4229 WARN("unexpected month %s\n", debugstr_w(month));
4230 return FALSE;
4233 while (isspaceW(*ptr))
4234 ptr++;
4236 num = strtoulW(ptr, &nextPtr, 10);
4237 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4239 ERR("unexpected year %s\n", debugstr_w(value));
4240 return FALSE;
4242 ptr = nextPtr;
4243 st.wYear = (WORD)num;
4245 if (!HTTP_ParseTime(&st, &ptr))
4246 return FALSE;
4248 while (isspaceW(*ptr))
4249 ptr++;
4251 if (strcmpW(ptr, gmt))
4253 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4254 return FALSE;
4256 return SystemTimeToFileTime(&st, ft);
4259 static WORD HTTP_ParseWeekday(LPCWSTR day)
4261 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4262 { 'm','o','n','d','a','y',0 },
4263 { 't','u','e','s','d','a','y',0 },
4264 { 'w','e','d','n','e','s','d','a','y',0 },
4265 { 't','h','u','r','s','d','a','y',0 },
4266 { 'f','r','i','d','a','y',0 },
4267 { 's','a','t','u','r','d','a','y',0 }};
4268 int i;
4269 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4270 if (!strcmpiW(day, days[i]))
4271 return i;
4273 /* Invalid */
4274 return 7;
4277 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4279 static const WCHAR gmt[]= { 'G','M','T',0 };
4280 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4281 LPCWSTR ptr;
4282 unsigned long num;
4283 SYSTEMTIME st = { 0 };
4285 ptr = strchrW(value, ',');
4286 if (!ptr)
4287 return FALSE;
4288 if (ptr - value == 3)
4290 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4291 day[3] = 0;
4292 st.wDayOfWeek = HTTP_ParseWkday(day);
4293 if (st.wDayOfWeek > 6)
4295 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4296 return FALSE;
4299 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4301 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4302 day[ptr - value + 1] = 0;
4303 st.wDayOfWeek = HTTP_ParseWeekday(day);
4304 if (st.wDayOfWeek > 6)
4306 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4307 return FALSE;
4310 else
4312 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4313 return FALSE;
4315 ptr++;
4317 while (isspaceW(*ptr))
4318 ptr++;
4320 num = strtoulW(ptr, &nextPtr, 10);
4321 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4323 ERR("unexpected day %s\n", debugstr_w(value));
4324 return FALSE;
4326 ptr = nextPtr;
4327 st.wDay = (WORD)num;
4329 if (*ptr != '-')
4331 ERR("unexpected month format %s\n", debugstr_w(ptr));
4332 return FALSE;
4334 ptr++;
4336 for (monthPtr = month; *ptr != '-' &&
4337 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4338 monthPtr++, ptr++)
4339 *monthPtr = *ptr;
4340 *monthPtr = 0;
4341 st.wMonth = HTTP_ParseMonth(month);
4342 if (!st.wMonth || st.wMonth > 12)
4344 ERR("unexpected month %s\n", debugstr_w(month));
4345 return FALSE;
4348 if (*ptr != '-')
4350 ERR("unexpected year format %s\n", debugstr_w(ptr));
4351 return FALSE;
4353 ptr++;
4355 num = strtoulW(ptr, &nextPtr, 10);
4356 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4358 ERR("unexpected year %s\n", debugstr_w(value));
4359 return FALSE;
4361 ptr = nextPtr;
4362 st.wYear = (WORD)num;
4364 if (!HTTP_ParseTime(&st, &ptr))
4365 return FALSE;
4367 while (isspaceW(*ptr))
4368 ptr++;
4370 if (strcmpW(ptr, gmt))
4372 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4373 return FALSE;
4375 return SystemTimeToFileTime(&st, ft);
4378 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4380 static const WCHAR zero[] = { '0',0 };
4381 BOOL ret;
4383 if (!strcmpW(value, zero))
4385 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4386 ret = TRUE;
4388 else if (strchrW(value, ','))
4390 ret = HTTP_ParseRfc1123Date(value, ft);
4391 if (!ret)
4393 ret = HTTP_ParseRfc850Date(value, ft);
4394 if (!ret)
4395 ERR("unexpected date format %s\n", debugstr_w(value));
4398 else
4400 ret = HTTP_ParseDateAsAsctime(value, ft);
4401 if (!ret)
4402 ERR("unexpected date format %s\n", debugstr_w(value));
4404 return ret;
4407 static void HTTP_ProcessExpires(http_request_t *request)
4409 BOOL expirationFound = FALSE;
4410 int headerIndex;
4412 /* Look for a Cache-Control header with a max-age directive, as it takes
4413 * precedence over the Expires header.
4415 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4416 if (headerIndex != -1)
4418 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4419 LPWSTR ptr;
4421 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4423 LPWSTR comma = strchrW(ptr, ','), end, equal;
4425 if (comma)
4426 end = comma;
4427 else
4428 end = ptr + strlenW(ptr);
4429 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4431 if (*equal == '=')
4433 static const WCHAR max_age[] = {
4434 'm','a','x','-','a','g','e',0 };
4436 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4438 LPWSTR nextPtr;
4439 unsigned long age;
4441 age = strtoulW(equal + 1, &nextPtr, 10);
4442 if (nextPtr > equal + 1)
4444 LARGE_INTEGER ft;
4446 NtQuerySystemTime( &ft );
4447 /* Age is in seconds, FILETIME resolution is in
4448 * 100 nanosecond intervals.
4450 ft.QuadPart += age * (ULONGLONG)1000000;
4451 request->expires.dwLowDateTime = ft.u.LowPart;
4452 request->expires.dwHighDateTime = ft.u.HighPart;
4453 expirationFound = TRUE;
4457 if (comma)
4459 ptr = comma + 1;
4460 while (isspaceW(*ptr))
4461 ptr++;
4463 else
4464 ptr = NULL;
4467 if (!expirationFound)
4469 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4470 if (headerIndex != -1)
4472 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4473 FILETIME ft;
4475 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4477 expirationFound = TRUE;
4478 request->expires = ft;
4482 if (!expirationFound)
4484 LARGE_INTEGER t;
4486 /* With no known age, default to 10 minutes until expiration. */
4487 NtQuerySystemTime( &t );
4488 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4489 request->expires.dwLowDateTime = t.u.LowPart;
4490 request->expires.dwHighDateTime = t.u.HighPart;
4494 static void HTTP_ProcessLastModified(http_request_t *request)
4496 int headerIndex;
4498 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4499 if (headerIndex != -1)
4501 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4502 FILETIME ft;
4504 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4505 request->last_modified = ft;
4509 static void http_process_keep_alive(http_request_t *req)
4511 int index;
4513 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4514 if(index != -1)
4515 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4516 else
4517 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4520 static void HTTP_CacheRequest(http_request_t *request)
4522 WCHAR url[INTERNET_MAX_URL_LENGTH];
4523 WCHAR cacheFileName[MAX_PATH+1];
4524 BOOL b;
4526 b = HTTP_GetRequestURL(request, url);
4527 if(!b) {
4528 WARN("Could not get URL\n");
4529 return;
4532 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4533 if(b) {
4534 heap_free(request->cacheFile);
4535 CloseHandle(request->hCacheFile);
4537 request->cacheFile = heap_strdupW(cacheFileName);
4538 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4539 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4540 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4541 WARN("Could not create file: %u\n", GetLastError());
4542 request->hCacheFile = NULL;
4544 }else {
4545 WARN("Could not create cache entry: %08x\n", GetLastError());
4549 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4551 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4552 http_session_t *session = request->session;
4553 netconn_t *netconn = NULL;
4554 server_t *server;
4555 DWORD res;
4557 assert(!request->netconn);
4558 reset_data_stream(request);
4560 server = get_server(session->serverName, session->serverPort);
4561 if(!server)
4562 return ERROR_OUTOFMEMORY;
4564 res = HTTP_ResolveName(request, server);
4565 if(res != ERROR_SUCCESS) {
4566 server_release(server);
4567 return res;
4570 EnterCriticalSection(&connection_pool_cs);
4572 while(!list_empty(&server->conn_pool)) {
4573 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4574 list_remove(&netconn->pool_entry);
4576 if(NETCON_is_alive(netconn))
4577 break;
4579 TRACE("connection %p closed during idle\n", netconn);
4580 free_netconn(netconn);
4581 netconn = NULL;
4584 LeaveCriticalSection(&connection_pool_cs);
4586 if(netconn) {
4587 TRACE("<-- reusing %p netconn\n", netconn);
4588 request->netconn = netconn;
4589 *reusing = TRUE;
4590 return ERROR_SUCCESS;
4593 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4594 INTERNET_STATUS_CONNECTING_TO_SERVER,
4595 server->addr_str,
4596 strlen(server->addr_str)+1);
4598 res = create_netconn(is_https, server, request->security_flags, &netconn);
4599 server_release(server);
4600 if(res != ERROR_SUCCESS) {
4601 ERR("create_netconn failed: %u\n", res);
4602 return res;
4605 request->netconn = netconn;
4607 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4608 INTERNET_STATUS_CONNECTED_TO_SERVER,
4609 server->addr_str, strlen(server->addr_str)+1);
4611 if(is_https) {
4612 /* Note: we differ from Microsoft's WinINet here. they seem to have
4613 * a bug that causes no status callbacks to be sent when starting
4614 * a tunnel to a proxy server using the CONNECT verb. i believe our
4615 * behaviour to be more correct and to not cause any incompatibilities
4616 * because using a secure connection through a proxy server is a rare
4617 * case that would be hard for anyone to depend on */
4618 if(session->appInfo->proxy)
4619 res = HTTP_SecureProxyConnect(request);
4620 if(res == ERROR_SUCCESS)
4621 res = NETCON_secure_connect(request->netconn, session->hostName);
4622 if(res != ERROR_SUCCESS)
4624 WARN("Couldn't connect securely to host\n");
4626 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4627 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4628 || res == ERROR_INTERNET_INVALID_CA
4629 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4630 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4631 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4632 || res == ERROR_INTERNET_SEC_INVALID_CERT
4633 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4634 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4638 if(res != ERROR_SUCCESS) {
4639 http_release_netconn(request, FALSE);
4640 return res;
4643 *reusing = FALSE;
4644 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4645 return ERROR_SUCCESS;
4648 /***********************************************************************
4649 * HTTP_HttpSendRequestW (internal)
4651 * Sends the specified request to the HTTP server
4653 * RETURNS
4654 * ERROR_SUCCESS on success
4655 * win32 error code on failure
4658 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4659 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4660 DWORD dwContentLength, BOOL bEndRequest)
4662 INT cnt;
4663 BOOL redirected = FALSE;
4664 LPWSTR requestString = NULL;
4665 INT responseLen;
4666 BOOL loop_next;
4667 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4668 static const WCHAR szContentLength[] =
4669 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4670 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4671 DWORD res;
4673 TRACE("--> %p\n", request);
4675 assert(request->hdr.htype == WH_HHTTPREQ);
4677 /* if the verb is NULL default to GET */
4678 if (!request->verb)
4679 request->verb = heap_strdupW(szGET);
4681 if (dwContentLength || strcmpW(request->verb, szGET))
4683 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4684 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4685 request->bytesToWrite = dwContentLength;
4687 if (request->session->appInfo->agent)
4689 WCHAR *agent_header;
4690 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4691 int len;
4693 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4694 agent_header = heap_alloc(len * sizeof(WCHAR));
4695 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4697 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4698 heap_free(agent_header);
4700 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4702 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4703 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4705 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4707 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4708 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4709 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4714 DWORD len;
4715 BOOL reusing_connection;
4716 char *ascii_req;
4718 loop_next = FALSE;
4720 /* like native, just in case the caller forgot to call InternetReadFile
4721 * for all the data */
4722 drain_content(request);
4723 if(redirected) {
4724 request->contentLength = ~0u;
4725 request->bytesToWrite = 0;
4728 if (TRACE_ON(wininet))
4730 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4731 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4734 HTTP_FixURL(request);
4735 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4737 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4739 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4740 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4742 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4743 HTTP_InsertCookies(request);
4745 /* add the headers the caller supplied */
4746 if( lpszHeaders && dwHeaderLength )
4748 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4749 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4752 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4754 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4755 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4756 heap_free(url);
4758 else
4759 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4762 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4764 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4765 break;
4767 /* send the request as ASCII, tack on the optional data */
4768 if (!lpOptional || redirected)
4769 dwOptionalLength = 0;
4770 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4771 NULL, 0, NULL, NULL );
4772 ascii_req = heap_alloc(len + dwOptionalLength);
4773 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4774 ascii_req, len, NULL, NULL );
4775 if( lpOptional )
4776 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4777 len = (len + dwOptionalLength - 1);
4778 ascii_req[len] = 0;
4779 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4781 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4782 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4784 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4785 heap_free( ascii_req );
4786 if(res != ERROR_SUCCESS) {
4787 TRACE("send failed: %u\n", res);
4788 if(!reusing_connection)
4789 break;
4790 http_release_netconn(request, FALSE);
4791 loop_next = TRUE;
4792 continue;
4795 request->bytesWritten = dwOptionalLength;
4797 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4798 INTERNET_STATUS_REQUEST_SENT,
4799 &len, sizeof(DWORD));
4801 if (bEndRequest)
4803 DWORD dwBufferSize;
4804 DWORD dwStatusCode;
4806 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4807 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4809 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4810 /* FIXME: We should know that connection is closed before sending
4811 * headers. Otherwise wrong callbacks are executed */
4812 if(!responseLen && reusing_connection) {
4813 TRACE("Connection closed by server, reconnecting\n");
4814 http_release_netconn(request, FALSE);
4815 loop_next = TRUE;
4816 continue;
4819 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4820 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4821 sizeof(DWORD));
4823 http_process_keep_alive(request);
4824 HTTP_ProcessCookies(request);
4825 HTTP_ProcessExpires(request);
4826 HTTP_ProcessLastModified(request);
4828 dwBufferSize = sizeof(dwStatusCode);
4829 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4830 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4831 dwStatusCode = 0;
4833 res = set_content_length(request, dwStatusCode);
4834 if(res != ERROR_SUCCESS)
4835 goto lend;
4836 if(!request->contentLength)
4837 http_release_netconn(request, TRUE);
4839 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4841 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4842 dwBufferSize=sizeof(szNewLocation);
4843 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4844 dwStatusCode == HTTP_STATUS_MOVED ||
4845 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4846 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4847 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4849 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4850 dwStatusCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4852 heap_free(request->verb);
4853 request->verb = heap_strdupW(szGET);
4855 drain_content(request);
4856 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4858 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4859 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4860 res = HTTP_HandleRedirect(request, new_url);
4861 if (res == ERROR_SUCCESS)
4863 heap_free(requestString);
4864 loop_next = TRUE;
4866 heap_free( new_url );
4868 redirected = TRUE;
4871 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4873 WCHAR szAuthValue[2048];
4874 dwBufferSize=2048;
4875 if (dwStatusCode == HTTP_STATUS_DENIED)
4877 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4878 DWORD dwIndex = 0;
4879 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4881 if (HTTP_DoAuthorization(request, szAuthValue,
4882 &request->authInfo,
4883 request->session->userName,
4884 request->session->password,
4885 Host->lpszValue))
4887 heap_free(requestString);
4888 loop_next = TRUE;
4889 break;
4893 if(!loop_next) {
4894 TRACE("Cleaning wrong authorization data\n");
4895 destroy_authinfo(request->authInfo);
4896 request->authInfo = NULL;
4899 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4901 DWORD dwIndex = 0;
4902 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4904 if (HTTP_DoAuthorization(request, szAuthValue,
4905 &request->proxyAuthInfo,
4906 request->session->appInfo->proxyUsername,
4907 request->session->appInfo->proxyPassword,
4908 NULL))
4910 loop_next = TRUE;
4911 break;
4915 if(!loop_next) {
4916 TRACE("Cleaning wrong proxy authorization data\n");
4917 destroy_authinfo(request->proxyAuthInfo);
4918 request->proxyAuthInfo = NULL;
4923 else
4924 res = ERROR_SUCCESS;
4926 while (loop_next);
4928 if(res == ERROR_SUCCESS)
4929 HTTP_CacheRequest(request);
4931 lend:
4932 heap_free(requestString);
4934 /* TODO: send notification for P3P header */
4936 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4938 if (res == ERROR_SUCCESS) {
4939 if(request->contentLength && request->bytesWritten == request->bytesToWrite)
4940 HTTP_ReceiveRequestData(request, TRUE);
4941 else
4942 send_request_complete(request,
4943 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4944 }else {
4945 send_request_complete(request, 0, res);
4949 TRACE("<--\n");
4950 return res;
4953 /***********************************************************************
4955 * Helper functions for the HttpSendRequest(Ex) functions
4958 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4960 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4961 http_request_t *request = (http_request_t*) workRequest->hdr;
4963 TRACE("%p\n", request);
4965 HTTP_HttpSendRequestW(request, req->lpszHeader,
4966 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4967 req->dwContentLength, req->bEndRequest);
4969 heap_free(req->lpszHeader);
4973 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4975 INT responseLen;
4976 DWORD dwCode, dwCodeLength;
4977 DWORD dwBufferSize;
4978 DWORD res = ERROR_SUCCESS;
4980 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4981 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4983 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4984 if (!responseLen)
4985 res = ERROR_HTTP_HEADER_NOT_FOUND;
4987 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4988 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4990 /* process cookies here. Is this right? */
4991 http_process_keep_alive(request);
4992 HTTP_ProcessCookies(request);
4993 HTTP_ProcessExpires(request);
4994 HTTP_ProcessLastModified(request);
4996 dwCodeLength = sizeof(dwCode);
4997 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4998 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
4999 dwCode = 0;
5001 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
5002 if(!request->contentLength)
5003 http_release_netconn(request, TRUE);
5006 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5008 if (dwCode == HTTP_STATUS_REDIRECT ||
5009 dwCode == HTTP_STATUS_MOVED ||
5010 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
5011 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
5013 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5014 dwBufferSize=sizeof(szNewLocation);
5015 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
5017 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5018 dwCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
5020 heap_free(request->verb);
5021 request->verb = heap_strdupW(szGET);
5023 drain_content(request);
5024 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5026 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5027 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5028 res = HTTP_HandleRedirect(request, new_url);
5029 if (res == ERROR_SUCCESS)
5030 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5031 heap_free( new_url );
5037 if (res == ERROR_SUCCESS && request->contentLength)
5038 HTTP_ReceiveRequestData(request, TRUE);
5039 else
5040 send_request_complete(request, res == ERROR_SUCCESS, res);
5042 return res;
5045 /***********************************************************************
5046 * HttpEndRequestA (WININET.@)
5048 * Ends an HTTP request that was started by HttpSendRequestEx
5050 * RETURNS
5051 * TRUE if successful
5052 * FALSE on failure
5055 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5056 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5058 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5060 if (lpBuffersOut)
5062 SetLastError(ERROR_INVALID_PARAMETER);
5063 return FALSE;
5066 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5069 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5071 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5072 http_request_t *request = (http_request_t*)work->hdr;
5074 TRACE("%p\n", request);
5076 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5079 /***********************************************************************
5080 * HttpEndRequestW (WININET.@)
5082 * Ends an HTTP request that was started by HttpSendRequestEx
5084 * RETURNS
5085 * TRUE if successful
5086 * FALSE on failure
5089 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5090 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5092 http_request_t *request;
5093 DWORD res;
5095 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5097 if (lpBuffersOut)
5099 SetLastError(ERROR_INVALID_PARAMETER);
5100 return FALSE;
5103 request = (http_request_t*) get_handle_object( hRequest );
5105 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5107 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5108 if (request)
5109 WININET_Release( &request->hdr );
5110 return FALSE;
5112 request->hdr.dwFlags |= dwFlags;
5114 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5116 WORKREQUEST work;
5117 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5119 work.asyncproc = AsyncHttpEndRequestProc;
5120 work.hdr = WININET_AddRef( &request->hdr );
5122 work_endrequest = &work.u.HttpEndRequestW;
5123 work_endrequest->dwFlags = dwFlags;
5124 work_endrequest->dwContext = dwContext;
5126 INTERNET_AsyncCall(&work);
5127 res = ERROR_IO_PENDING;
5129 else
5130 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5132 WININET_Release( &request->hdr );
5133 TRACE("%u <--\n", res);
5134 if(res != ERROR_SUCCESS)
5135 SetLastError(res);
5136 return res == ERROR_SUCCESS;
5139 /***********************************************************************
5140 * HttpSendRequestExA (WININET.@)
5142 * Sends the specified request to the HTTP server and allows chunked
5143 * transfers.
5145 * RETURNS
5146 * Success: TRUE
5147 * Failure: FALSE, call GetLastError() for more information.
5149 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5150 LPINTERNET_BUFFERSA lpBuffersIn,
5151 LPINTERNET_BUFFERSA lpBuffersOut,
5152 DWORD dwFlags, DWORD_PTR dwContext)
5154 INTERNET_BUFFERSW BuffersInW;
5155 BOOL rc = FALSE;
5156 DWORD headerlen;
5157 LPWSTR header = NULL;
5159 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5160 lpBuffersOut, dwFlags, dwContext);
5162 if (lpBuffersIn)
5164 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5165 if (lpBuffersIn->lpcszHeader)
5167 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5168 lpBuffersIn->dwHeadersLength,0,0);
5169 header = heap_alloc(headerlen*sizeof(WCHAR));
5170 if (!(BuffersInW.lpcszHeader = header))
5172 SetLastError(ERROR_OUTOFMEMORY);
5173 return FALSE;
5175 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5176 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5177 header, headerlen);
5179 else
5180 BuffersInW.lpcszHeader = NULL;
5181 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5182 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5183 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5184 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5185 BuffersInW.Next = NULL;
5188 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5190 heap_free(header);
5191 return rc;
5194 /***********************************************************************
5195 * HttpSendRequestExW (WININET.@)
5197 * Sends the specified request to the HTTP server and allows chunked
5198 * transfers
5200 * RETURNS
5201 * Success: TRUE
5202 * Failure: FALSE, call GetLastError() for more information.
5204 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5205 LPINTERNET_BUFFERSW lpBuffersIn,
5206 LPINTERNET_BUFFERSW lpBuffersOut,
5207 DWORD dwFlags, DWORD_PTR dwContext)
5209 http_request_t *request;
5210 http_session_t *session;
5211 appinfo_t *hIC;
5212 DWORD res;
5214 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5215 lpBuffersOut, dwFlags, dwContext);
5217 request = (http_request_t*) get_handle_object( hRequest );
5219 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5221 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5222 goto lend;
5225 session = request->session;
5226 assert(session->hdr.htype == WH_HHTTPSESSION);
5227 hIC = session->appInfo;
5228 assert(hIC->hdr.htype == WH_HINIT);
5230 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5232 WORKREQUEST workRequest;
5233 struct WORKREQ_HTTPSENDREQUESTW *req;
5235 workRequest.asyncproc = AsyncHttpSendRequestProc;
5236 workRequest.hdr = WININET_AddRef( &request->hdr );
5237 req = &workRequest.u.HttpSendRequestW;
5238 if (lpBuffersIn)
5240 DWORD size = 0;
5242 if (lpBuffersIn->lpcszHeader)
5244 if (lpBuffersIn->dwHeadersLength == ~0u)
5245 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5246 else
5247 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5249 req->lpszHeader = heap_alloc(size);
5250 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5252 else req->lpszHeader = NULL;
5254 req->dwHeaderLength = size / sizeof(WCHAR);
5255 req->lpOptional = lpBuffersIn->lpvBuffer;
5256 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5257 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5259 else
5261 req->lpszHeader = NULL;
5262 req->dwHeaderLength = 0;
5263 req->lpOptional = NULL;
5264 req->dwOptionalLength = 0;
5265 req->dwContentLength = 0;
5268 req->bEndRequest = FALSE;
5270 INTERNET_AsyncCall(&workRequest);
5272 * This is from windows.
5274 res = ERROR_IO_PENDING;
5276 else
5278 if (lpBuffersIn)
5279 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5280 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5281 lpBuffersIn->dwBufferTotal, FALSE);
5282 else
5283 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5286 lend:
5287 if ( request )
5288 WININET_Release( &request->hdr );
5290 TRACE("<---\n");
5291 SetLastError(res);
5292 return res == ERROR_SUCCESS;
5295 /***********************************************************************
5296 * HttpSendRequestW (WININET.@)
5298 * Sends the specified request to the HTTP server
5300 * RETURNS
5301 * TRUE on success
5302 * FALSE on failure
5305 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5306 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5308 http_request_t *request;
5309 http_session_t *session = NULL;
5310 appinfo_t *hIC = NULL;
5311 DWORD res = ERROR_SUCCESS;
5313 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5314 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5316 request = (http_request_t*) get_handle_object( hHttpRequest );
5317 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5319 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5320 goto lend;
5323 session = request->session;
5324 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5326 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5327 goto lend;
5330 hIC = session->appInfo;
5331 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5333 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5334 goto lend;
5337 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5339 WORKREQUEST workRequest;
5340 struct WORKREQ_HTTPSENDREQUESTW *req;
5342 workRequest.asyncproc = AsyncHttpSendRequestProc;
5343 workRequest.hdr = WININET_AddRef( &request->hdr );
5344 req = &workRequest.u.HttpSendRequestW;
5345 if (lpszHeaders)
5347 DWORD size;
5349 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5350 else size = dwHeaderLength * sizeof(WCHAR);
5352 req->lpszHeader = heap_alloc(size);
5353 memcpy(req->lpszHeader, lpszHeaders, size);
5355 else
5356 req->lpszHeader = 0;
5357 req->dwHeaderLength = dwHeaderLength;
5358 req->lpOptional = lpOptional;
5359 req->dwOptionalLength = dwOptionalLength;
5360 req->dwContentLength = dwOptionalLength;
5361 req->bEndRequest = TRUE;
5363 INTERNET_AsyncCall(&workRequest);
5365 * This is from windows.
5367 res = ERROR_IO_PENDING;
5369 else
5371 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5372 dwHeaderLength, lpOptional, dwOptionalLength,
5373 dwOptionalLength, TRUE);
5375 lend:
5376 if( request )
5377 WININET_Release( &request->hdr );
5379 SetLastError(res);
5380 return res == ERROR_SUCCESS;
5383 /***********************************************************************
5384 * HttpSendRequestA (WININET.@)
5386 * Sends the specified request to the HTTP server
5388 * RETURNS
5389 * TRUE on success
5390 * FALSE on failure
5393 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5394 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5396 BOOL result;
5397 LPWSTR szHeaders=NULL;
5398 DWORD nLen=dwHeaderLength;
5399 if(lpszHeaders!=NULL)
5401 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5402 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5403 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5405 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5406 heap_free(szHeaders);
5407 return result;
5410 /***********************************************************************
5411 * HTTPSESSION_Destroy (internal)
5413 * Deallocate session handle
5416 static void HTTPSESSION_Destroy(object_header_t *hdr)
5418 http_session_t *session = (http_session_t*) hdr;
5420 TRACE("%p\n", session);
5422 WININET_Release(&session->appInfo->hdr);
5424 heap_free(session->hostName);
5425 heap_free(session->serverName);
5426 heap_free(session->password);
5427 heap_free(session->userName);
5430 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5432 switch(option) {
5433 case INTERNET_OPTION_HANDLE_TYPE:
5434 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5436 if (*size < sizeof(ULONG))
5437 return ERROR_INSUFFICIENT_BUFFER;
5439 *size = sizeof(DWORD);
5440 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5441 return ERROR_SUCCESS;
5444 return INET_QueryOption(hdr, option, buffer, size, unicode);
5447 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5449 http_session_t *ses = (http_session_t*)hdr;
5451 switch(option) {
5452 case INTERNET_OPTION_USERNAME:
5454 heap_free(ses->userName);
5455 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5456 return ERROR_SUCCESS;
5458 case INTERNET_OPTION_PASSWORD:
5460 heap_free(ses->password);
5461 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5462 return ERROR_SUCCESS;
5464 default: break;
5467 return ERROR_INTERNET_INVALID_OPTION;
5470 static const object_vtbl_t HTTPSESSIONVtbl = {
5471 HTTPSESSION_Destroy,
5472 NULL,
5473 HTTPSESSION_QueryOption,
5474 HTTPSESSION_SetOption,
5475 NULL,
5476 NULL,
5477 NULL,
5478 NULL,
5479 NULL
5483 /***********************************************************************
5484 * HTTP_Connect (internal)
5486 * Create http session handle
5488 * RETURNS
5489 * HINTERNET a session handle on success
5490 * NULL on failure
5493 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5494 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5495 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5496 DWORD dwInternalFlags, HINTERNET *ret)
5498 http_session_t *session = NULL;
5500 TRACE("-->\n");
5502 if (!lpszServerName || !lpszServerName[0])
5503 return ERROR_INVALID_PARAMETER;
5505 assert( hIC->hdr.htype == WH_HINIT );
5507 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5508 if (!session)
5509 return ERROR_OUTOFMEMORY;
5512 * According to my tests. The name is not resolved until a request is sent
5515 session->hdr.htype = WH_HHTTPSESSION;
5516 session->hdr.dwFlags = dwFlags;
5517 session->hdr.dwContext = dwContext;
5518 session->hdr.dwInternalFlags |= dwInternalFlags;
5520 WININET_AddRef( &hIC->hdr );
5521 session->appInfo = hIC;
5522 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5524 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5525 if(hIC->proxyBypass)
5526 FIXME("Proxy bypass is ignored.\n");
5528 session->serverName = heap_strdupW(lpszServerName);
5529 session->hostName = heap_strdupW(lpszServerName);
5530 if (lpszUserName && lpszUserName[0])
5531 session->userName = heap_strdupW(lpszUserName);
5532 if (lpszPassword && lpszPassword[0])
5533 session->password = heap_strdupW(lpszPassword);
5534 session->serverPort = serverPort;
5535 session->hostPort = serverPort;
5537 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5538 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5540 INTERNET_SendCallback(&hIC->hdr, dwContext,
5541 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5542 sizeof(HINTERNET));
5546 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5547 * windows
5550 TRACE("%p --> %p\n", hIC, session);
5552 *ret = session->hdr.hInternet;
5553 return ERROR_SUCCESS;
5556 /***********************************************************************
5557 * HTTP_clear_response_headers (internal)
5559 * clear out any old response headers
5561 static void HTTP_clear_response_headers( http_request_t *request )
5563 DWORD i;
5565 for( i=0; i<request->nCustHeaders; i++)
5567 if( !request->custHeaders[i].lpszField )
5568 continue;
5569 if( !request->custHeaders[i].lpszValue )
5570 continue;
5571 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5572 continue;
5573 HTTP_DeleteCustomHeader( request, i );
5574 i--;
5578 /***********************************************************************
5579 * HTTP_GetResponseHeaders (internal)
5581 * Read server response
5583 * RETURNS
5585 * TRUE on success
5586 * FALSE on error
5588 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5590 INT cbreaks = 0;
5591 WCHAR buffer[MAX_REPLY_LEN];
5592 DWORD buflen = MAX_REPLY_LEN;
5593 BOOL bSuccess = FALSE;
5594 INT rc = 0;
5595 char bufferA[MAX_REPLY_LEN];
5596 LPWSTR status_code = NULL, status_text = NULL;
5597 DWORD cchMaxRawHeaders = 1024;
5598 LPWSTR lpszRawHeaders = NULL;
5599 LPWSTR temp;
5600 DWORD cchRawHeaders = 0;
5601 BOOL codeHundred = FALSE;
5603 TRACE("-->\n");
5605 if(!request->netconn)
5606 goto lend;
5608 do {
5609 static const WCHAR szHundred[] = {'1','0','0',0};
5611 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5613 buflen = MAX_REPLY_LEN;
5614 if (!read_line(request, bufferA, &buflen))
5615 goto lend;
5617 /* clear old response headers (eg. from a redirect response) */
5618 if (clear) {
5619 HTTP_clear_response_headers( request );
5620 clear = FALSE;
5623 rc += buflen;
5624 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5625 /* check is this a status code line? */
5626 if (!strncmpW(buffer, g_szHttp1_0, 4))
5628 /* split the version from the status code */
5629 status_code = strchrW( buffer, ' ' );
5630 if( !status_code )
5631 goto lend;
5632 *status_code++=0;
5634 /* split the status code from the status text */
5635 status_text = strchrW( status_code, ' ' );
5636 if( !status_text )
5637 goto lend;
5638 *status_text++=0;
5640 TRACE("version [%s] status code [%s] status text [%s]\n",
5641 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5643 codeHundred = (!strcmpW(status_code, szHundred));
5645 else if (!codeHundred)
5647 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5649 heap_free(request->version);
5650 heap_free(request->statusText);
5652 request->version = heap_strdupW(g_szHttp1_0);
5653 request->statusText = heap_strdupW(szOK);
5655 heap_free(request->rawHeaders);
5656 request->rawHeaders = heap_strdupW(szDefaultHeader);
5658 bSuccess = TRUE;
5659 goto lend;
5661 } while (codeHundred);
5663 /* Add status code */
5664 HTTP_ProcessHeader(request, szStatus, status_code,
5665 HTTP_ADDHDR_FLAG_REPLACE);
5667 heap_free(request->version);
5668 heap_free(request->statusText);
5670 request->version = heap_strdupW(buffer);
5671 request->statusText = heap_strdupW(status_text);
5673 /* Restore the spaces */
5674 *(status_code-1) = ' ';
5675 *(status_text-1) = ' ';
5677 /* regenerate raw headers */
5678 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5679 if (!lpszRawHeaders) goto lend;
5681 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5682 cchMaxRawHeaders *= 2;
5683 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5684 if (temp == NULL) goto lend;
5685 lpszRawHeaders = temp;
5686 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5687 cchRawHeaders += (buflen-1);
5688 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5689 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5690 lpszRawHeaders[cchRawHeaders] = '\0';
5692 /* Parse each response line */
5695 buflen = MAX_REPLY_LEN;
5696 if (read_line(request, bufferA, &buflen))
5698 LPWSTR * pFieldAndValue;
5700 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5702 if (!bufferA[0]) break;
5703 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5705 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5706 if (pFieldAndValue)
5708 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5709 cchMaxRawHeaders *= 2;
5710 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5711 if (temp == NULL) goto lend;
5712 lpszRawHeaders = temp;
5713 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5714 cchRawHeaders += (buflen-1);
5715 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5716 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5717 lpszRawHeaders[cchRawHeaders] = '\0';
5719 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5720 HTTP_ADDREQ_FLAG_ADD );
5722 HTTP_FreeTokens(pFieldAndValue);
5725 else
5727 cbreaks++;
5728 if (cbreaks >= 2)
5729 break;
5731 }while(1);
5733 /* make sure the response header is terminated with an empty line. Some apps really
5734 truly care about that empty line being there for some reason. Just add it to the
5735 header. */
5736 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5738 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5739 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5740 if (temp == NULL) goto lend;
5741 lpszRawHeaders = temp;
5744 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5746 heap_free(request->rawHeaders);
5747 request->rawHeaders = lpszRawHeaders;
5748 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5749 bSuccess = TRUE;
5751 lend:
5753 TRACE("<--\n");
5754 if (bSuccess)
5755 return rc;
5756 else
5758 heap_free(lpszRawHeaders);
5759 return 0;
5763 /***********************************************************************
5764 * HTTP_InterpretHttpHeader (internal)
5766 * Parse server response
5768 * RETURNS
5770 * Pointer to array of field, value, NULL on success.
5771 * NULL on error.
5773 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5775 LPWSTR * pTokenPair;
5776 LPWSTR pszColon;
5777 INT len;
5779 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5781 pszColon = strchrW(buffer, ':');
5782 /* must have two tokens */
5783 if (!pszColon)
5785 HTTP_FreeTokens(pTokenPair);
5786 if (buffer[0])
5787 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5788 return NULL;
5791 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5792 if (!pTokenPair[0])
5794 HTTP_FreeTokens(pTokenPair);
5795 return NULL;
5797 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5798 pTokenPair[0][pszColon - buffer] = '\0';
5800 /* skip colon */
5801 pszColon++;
5802 len = strlenW(pszColon);
5803 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5804 if (!pTokenPair[1])
5806 HTTP_FreeTokens(pTokenPair);
5807 return NULL;
5809 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5811 strip_spaces(pTokenPair[0]);
5812 strip_spaces(pTokenPair[1]);
5814 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5815 return pTokenPair;
5818 /***********************************************************************
5819 * HTTP_ProcessHeader (internal)
5821 * Stuff header into header tables according to <dwModifier>
5825 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5827 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5829 LPHTTPHEADERW lphttpHdr = NULL;
5830 INT index = -1;
5831 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5832 DWORD res = ERROR_HTTP_INVALID_HEADER;
5834 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5836 /* REPLACE wins out over ADD */
5837 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5838 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5840 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5841 index = -1;
5842 else
5843 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5845 if (index >= 0)
5847 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5848 return ERROR_HTTP_INVALID_HEADER;
5849 lphttpHdr = &request->custHeaders[index];
5851 else if (value)
5853 HTTPHEADERW hdr;
5855 hdr.lpszField = (LPWSTR)field;
5856 hdr.lpszValue = (LPWSTR)value;
5857 hdr.wFlags = hdr.wCount = 0;
5859 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5860 hdr.wFlags |= HDR_ISREQUEST;
5862 return HTTP_InsertCustomHeader(request, &hdr);
5864 /* no value to delete */
5865 else return ERROR_SUCCESS;
5867 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5868 lphttpHdr->wFlags |= HDR_ISREQUEST;
5869 else
5870 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5872 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5874 HTTP_DeleteCustomHeader( request, index );
5876 if (value)
5878 HTTPHEADERW hdr;
5880 hdr.lpszField = (LPWSTR)field;
5881 hdr.lpszValue = (LPWSTR)value;
5882 hdr.wFlags = hdr.wCount = 0;
5884 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5885 hdr.wFlags |= HDR_ISREQUEST;
5887 return HTTP_InsertCustomHeader(request, &hdr);
5890 return ERROR_SUCCESS;
5892 else if (dwModifier & COALESCEFLAGS)
5894 LPWSTR lpsztmp;
5895 WCHAR ch = 0;
5896 INT len = 0;
5897 INT origlen = strlenW(lphttpHdr->lpszValue);
5898 INT valuelen = strlenW(value);
5900 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5902 ch = ',';
5903 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5905 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5907 ch = ';';
5908 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5911 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5913 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5914 if (lpsztmp)
5916 lphttpHdr->lpszValue = lpsztmp;
5917 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5918 if (ch > 0)
5920 lphttpHdr->lpszValue[origlen] = ch;
5921 origlen++;
5922 lphttpHdr->lpszValue[origlen] = ' ';
5923 origlen++;
5926 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5927 lphttpHdr->lpszValue[len] = '\0';
5928 res = ERROR_SUCCESS;
5930 else
5932 WARN("heap_realloc (%d bytes) failed\n",len+1);
5933 res = ERROR_OUTOFMEMORY;
5936 TRACE("<-- %d\n", res);
5937 return res;
5940 /***********************************************************************
5941 * HTTP_GetCustomHeaderIndex (internal)
5943 * Return index of custom header from header array
5946 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5947 int requested_index, BOOL request_only)
5949 DWORD index;
5951 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5953 for (index = 0; index < request->nCustHeaders; index++)
5955 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5956 continue;
5958 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5959 continue;
5961 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5962 continue;
5964 if (requested_index == 0)
5965 break;
5966 requested_index --;
5969 if (index >= request->nCustHeaders)
5970 index = -1;
5972 TRACE("Return: %d\n", index);
5973 return index;
5977 /***********************************************************************
5978 * HTTP_InsertCustomHeader (internal)
5980 * Insert header into array
5983 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5985 INT count;
5986 LPHTTPHEADERW lph = NULL;
5988 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5989 count = request->nCustHeaders + 1;
5990 if (count > 1)
5991 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5992 else
5993 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5995 if (!lph)
5996 return ERROR_OUTOFMEMORY;
5998 request->custHeaders = lph;
5999 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6000 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6001 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6002 request->custHeaders[count-1].wCount= lpHdr->wCount;
6003 request->nCustHeaders++;
6005 return ERROR_SUCCESS;
6009 /***********************************************************************
6010 * HTTP_DeleteCustomHeader (internal)
6012 * Delete header from array
6013 * If this function is called, the indexs may change.
6015 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6017 if( request->nCustHeaders <= 0 )
6018 return FALSE;
6019 if( index >= request->nCustHeaders )
6020 return FALSE;
6021 request->nCustHeaders--;
6023 heap_free(request->custHeaders[index].lpszField);
6024 heap_free(request->custHeaders[index].lpszValue);
6026 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6027 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6028 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6030 return TRUE;
6034 /***********************************************************************
6035 * HTTP_VerifyValidHeader (internal)
6037 * Verify the given header is not invalid for the given http request
6040 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6042 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6043 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6044 return ERROR_HTTP_INVALID_HEADER;
6046 return ERROR_SUCCESS;
6049 /***********************************************************************
6050 * IsHostInProxyBypassList (@)
6052 * Undocumented
6055 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6057 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6058 return FALSE;
6061 /***********************************************************************
6062 * InternetShowSecurityInfoByURLA (@)
6064 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6066 FIXME("stub: %s %p\n", url, window);
6067 return FALSE;
6070 /***********************************************************************
6071 * InternetShowSecurityInfoByURLW (@)
6073 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6075 FIXME("stub: %s %p\n", debugstr_w(url), window);
6076 return FALSE;
6079 /***********************************************************************
6080 * ShowX509EncodedCertificate (@)
6082 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6084 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6085 cert, len);
6086 DWORD ret;
6088 if (certContext)
6090 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6092 memset(&view, 0, sizeof(view));
6093 view.hwndParent = parent;
6094 view.pCertContext = certContext;
6095 if (CryptUIDlgViewCertificateW(&view, NULL))
6096 ret = ERROR_SUCCESS;
6097 else
6098 ret = GetLastError();
6099 CertFreeCertificateContext(certContext);
6101 else
6102 ret = GetLastError();
6103 return ret;